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
:
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 Low
or 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 ExpQueryModuleInformation
:
Inside ExpQueryModuleInformation
, the 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 0
.
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:
[table id=1 /]
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 🙂
- Secure Kernel Research with LiveCloudKd
- 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