In recent years, Microsoft has focused its efforts on mitigating bug classes and exploitation techniques. In latest Windows versions this includes another change that adds a significant challenge to attackers targeting the Windows kernel — restricting kernel address leaks to user mode. With almost any memory bugs, an attacker needs some kernel address leak to know which address will be read / written into / overflowed / corrupted. That address could be the address of ntoskrnl.exe or other kernel drivers, or the address of some object that the attacker targets. Until recently, getting those was very easy (for anyone running at medium integrity level or above). All you had to do was call one of several known windows APIs.
But starting Windows 11 / Windows Server 2022
24H2 edition, those APIs will no longer leak any kernel addresses, unless the requesting process has enabled
SeDebugPrivilege, a powerful privilege which is only available to admin processes and not enabled by default. This check is implemented with a new flag passed to
ExIsRestrictedCaller is called in various places in the kernel to check whether a process should receive access to a resource or be allowed to perform an operation. This is used to restrict processes running with an integrity level of
Untrusted from calling APIs that return kernel addresses, for example. Now, this API also checks if the process enables
SeDebugPrivilege and uses the result to set the
RestrictKernelAddressLeaks argument (name chosen by me, as the argument name is not public) and return it to the caller. This argument is then used by the caller to decide what kernel data can be returned to the user-mode caller.
For example, when
NtQuerySystemInformation (which calls the internal
ExpQuerySystemInformation) is called with the
SystemModuleInformation class, ExIsRestrictedCaller is called to determine what data the caller can receive. The output argument then gets passed into
RestrictKernelAddressLeaks argument is used to decide whether the function will populate the
DllBase field for every kernel module loaded in the system:
If the argument is set, which means the process does not enable
SeDebugPrivilege, the process will still be able to receive information about the loaded kernel modules. But that information will not include the base address of those modules – that field will be set to
This check is done in all other APIs known to leak kernel addresses to user-mode callers. In all cases, the query will succeed for callers running at Medium IL or above, but the fields that normally contain kernel addresses will be left empty. The full list of APIs which now restrict kernel address leaks are:
API Information Class Data Structure Returned by the API Restricted Field
NtQuerySystemInformation SystemModuleInformation (11) RTL_PROCESS_MODULE_INFORMATION RTL_PROCESS_MODULE_INFORMATION.DllBase
NtQuerySystemInformation SystemLocksInformation (12) RTL_PROCESS_LOCK_INFORMATION RTL_PROCESS_LOCK_INFORMATION.Address
NtQuerySystemInformation SystemHandleInformation (16) SYSTEM_HANDLE_INFORMATION SYSTEM_HANDLE_INFORMATION.Handles[N].Object
NtQuerySystemInformation SystemObjectInformation (17) SYSTEM_OBJECT_INFORMATION SYSTEM_OBJECT_INFORMATION.Object
NtQuerySystemInformation SystemExtendedHandleInformation (64) SYSTEM_HANDLE_INFORMATION_EX SYSTEM_HANDLE_INFORMATION_EX.Handles[N].Object
NtQuerySystemInformation SystemBigPoolInformation (66) SYSTEM_BIGPOOL_INFORMATION SYSTEM_BIGPOOL_INFORMATION.AllocationInfo[N].VirtualAddress
NtQuerySystemInformation SystemModuleInformationEx (77) RTL_PROCESS_MODULE_INFORMATION_EX RTL_PROCESS_MODULE_INFORMATION_EX.BaseInfo.ImageBase
NtQuerySystemInformation SystemFullProcessInformation (148) The undocumented SYSTEM_FULL_PROCESS_INFORMATION structure contains: SYSTEM_PROCESS_INFORMATION + array of SYSTEM_THREAD_EXTENDED_THREAD_INFORMATION + SYSTEM_PROCESS_INFORMATION_EXTENSION SYSTEM_EXTENDED_THREAD_INFORMATION.StackBase
SYSTEM_EXTENDED_THREAD_INFORMATION.Win32StartAddress if the thread’s Win32StartAddress is a kernel address.
NtQueryInformationProcess ProcessHandleTracing (32) PROCESS_HANDLE_TRACING_QUERY PROCESS_HANDLE_TRACING_QUERY.HandleTrace[N].Stacks
NtQueryInformationProcess ProcessWorkingSetWatchEx (42) PROCESS_WS_WATCH_INFORMATION_EX PROCESS_WS_WATCH_INFORMATION_EX.BasicInfo.FaultingPc – if FaultingPc is a kernel address.
PROCESS_WS_WATCH_INFORMATION_EX.BasicInfo.FaultingVa – if FaultingVa is a kernel address.
Are these APIs the only ways to leak kernel addresses? Are KASLR leaks finally dead? Of course not. But more on that in another blog post 🙂
- Troubleshooting a System Crash
- KASLR Leaks Restriction
- Investigating Filter Communication Ports
- An End to KASLR Bypasses?
- Understanding a New Mitigation: Module Tampering Protection
- One I/O Ring to Rule Them All: A Full Read/Write Exploit Primitive on Windows 11
- One Year to I/O Ring: What Changed?
- HyperGuard Part 3 – More SKPG Extents
- An Exercise in Dynamic Analysis
- HyperGuard – Secure Kernel Patch Guard: Part 2 – SKPG Extents