Reading Time: 4 minutes
Introduction
Microsoft mitigated many traditional kernel information leaks starting with Windows 11/Windows Server 2022 24H2, including calls such as NtQuerySystemInformation() (when used with the SystemModuleInformation class), by suppressing kernel base addresses unless the caller had the SeDebugPrivilege , typically reserved for administrative processes. That change effectively neutered one of the most accessible KASLR bypass techniques, and, without knowledge of the kernel’s base addresses, exploitation became harder.
While doing patch analysis for CVE-2024-43511, I realised that Microsoft made a mistake leading to a kernel address leak vulnerability. This new bug requires winning a race condition to read out the address; however, it’s pretty easy to achieve. It provides a powerful kernel address leak for any token handle, which can be easily chained with other vulnerabilities to obtain a complete exploit on the latest version of the system.
Vulnerability
Quick review on the patch for CVE-2024-43511
In October 2024, Microsoft released a patch for a Time-of-check Time-of-use (TOCTOU) Race Condition vulnerability in the Windows kernel, namely CVE-2024-43511.
To fix the issue, when passing parameters to the RtlSidHashInitialize() function, it reads data from a kernel pointer (which is a member of the TOKEN structure), instead of the value set in a user-controlled buffer.
Spotting the bug
With the new update, the RtlSidHashInitialize() function, which performs hash initialisation, now takes as its first parameter a pointer from the TOKEN structure and as its third parameter a user-controlled buffer. Then, RtlSidHashInitialize() stores the first parameter (which is a pointer to the UserAndGroups field of the TOKEN structure) into the third parameter (user-supplied pointer), and starts doing hash initialisation later on:
Although the caller function will replace the stored pointer in the user-buffer pointer after that, it still leaves a small time window for us to win a race condition and read out the leaked kernel address. To trigger the vulnerable function, we only need to invoke the NtQuerySystemInformation() API with the SystemTokenInformation class.
Effects of the bug
This leak primitive is particularly useful for Windows versions 24H2 or later, as the well-known technique for leaking kernel addresses using NtQuerySystemInformation() or other alternative methods has been patched. As the vulnerability is located within an NT syscall, the bug can be exploited from either Low IL or AppContainer. If chained with a write-what-where bug to overwrite the Privileges field of the TOKEN object, it will result in a complete LPE.
Exploitation
Setup
To exploit this bug, I need to create two threads to run concurrently:
One thread to read at the specific offset, which will be used to store the kernel address in the user buffer.
One thread performs the syscall. It is required to run the syscall several times before archiving the kernel leak.
Reliability
Although this is a race condition bug, the time window is wide enough to read the kernel address from the user-space buffer. To increase the success rate, we repeatedly call NtQuerySystemInformation() while keeping reading until we get the leak. The read becomes very reliable, and we can obtain the leaked TOKEN almost every time we run the exploit.
Proof-of-concepts
The results below show the exploit on a Windows Insider Preview in April 2025 (latest version at the time of writing), running the exploit from the Low IL and App Container contexts:
Conclusion
Patch analysis is one of the fastest ways to improve our skills and sharpen our mindset in bug finding. Additionally, it also helps us improve our secure coding skills. Sometimes, bug fixes in a function can introduce new bugs in other parts of the code. When conducting vulnerability research, it’s recommended to take a deep look to understand how the bug was patched and whether the patch completely resolves the issue or leaves other gaps open. From a developer’s point of view, every change made to a function can affect others as well, so take extra care when making any changes to the codebase. It is essential to thoroughly understand how a function works before modifying it, as this helps avoid mistakes or misuse of the function.
Disclosure Timeline