Skip to content

CVE-2024-30088

NT Kernel — TOCTOU race in AuthzBasepCopyoutInternalSecurityAttributes

Exploited in the Wild

This vulnerability was exploited in the wild before or shortly after patching. It was also demonstrated at Pwn2Own Vancouver 2024, making it one of the highest-profile Windows kernel bugs of the year.

Summary

Field Value
Driver ntoskrnl.exe
Vulnerability Class Race Condition / TOCTOU
Vulnerable Build 10.0.22621.3672 (KB5037853)
Fixed Build 10.0.22621.3733 (KB5039212)
Exploited ITW Yes

The setup

This bug lives in ntoskrnl.exe itself, in a function most people have never heard of: AuthzBasepCopyoutInternalSecurityAttributes. The function's job is mundane. During access-check operations like NtAccessCheckByTypeAndAuditAlarm, the kernel needs to copy security attribute data from kernel space into a user-mode output buffer. The caller provides a buffer, the kernel validates it, and the kernel writes the results. Thousands of kernel functions follow this same pattern without incident.

What makes AuthzBasepCopyoutInternalSecurityAttributes special is a gap between two operations that should have been atomic but were not.

Affected Functions

  • AuthzBasepCopyoutInternalSecurityAttributes
  • NtAccessCheckByTypeAndAuditAlarm

Root cause: the race that should not exist

Here is what happens, step by step, when a process calls NtAccessCheckByTypeAndAuditAlarm with a user-mode output buffer:

  1. The kernel receives the syscall and reaches AuthzBasepCopyoutInternalSecurityAttributes.
  2. The function validates the user-mode buffer address and size. It confirms the pointer targets user-mode memory and that the buffer is large enough for the output. This is the time-of-check.
  3. The function then copies security attribute data into the buffer. This is the time-of-use.
sequenceDiagram
    participant T1 as Thread 1 (Syscall)
    participant K as Kernel
    participant T2 as Thread 2 (Racer)

    T1->>K: NtAccessCheckByTypeAndAuditAlarm(buf=0x1000)
    K->>K: Validate buf → user-mode ✓, size ✓
    Note over K: ← TOCTOU window opens →
    T2->>T2: NtUnmapViewOfSection(0x1000)
    T2->>T2: NtMapViewOfSection(0x1000 → kernel addr)
    Note over K: ← TOCTOU window closes →
    K->>K: RtlCopyMemory(0x1000, attrs, size)
    Note over K: Writes to kernel address!

Between steps 2 and 3, the buffer's virtual address mapping can change. There is no lock, no pinning of the pages, no capture of the physical address. The function validated a virtual address, and another thread can alter what that virtual address points to before the copy occurs.

The attack exploits this gap with a second thread that races to remap the buffer's virtual address range. The racing thread calls NtUnmapViewOfSection to destroy the original mapping, then immediately calls NtMapViewOfSection to map the same virtual address range to a section backed by a kernel-mode target (or, more precisely, arranges for the kernel's copy to land at a kernel address through page table manipulation). If the remap completes between the validation and the copy, the kernel writes security attribute data to an attacker-chosen kernel address instead of the original user-mode buffer.

No synchronization primitive prevented this. The virtual address was validated, the virtual address was used, and between those two operations the address could mean something entirely different. This is the textbook definition of a TOCTOU vulnerability, but applied to virtual memory mappings rather than the file system paths where TOCTOU is traditionally discussed.

Exploitation: winning the race

The TOCTOU race gives a controlled kernel write primitive. The target address is controlled via the buffer remap, and the written data is partially controlled through the security attribute content passed to the access-check call. This is a write-what-where condition with some constraints on the payload bytes, but enough flexibility for practical use.

The exploitation flow works like this. The attacker allocates a user-mode buffer at a known virtual address and issues a security attribute query syscall to establish the output buffer. A second thread runs in a tight loop, racing to remap the buffer's address range to a kernel-mode target. The most common target is a field in the calling process's EPROCESS token structure, because overwriting the right token field converts a standard user process into SYSTEM.

The race window is surprisingly wide. The kernel's validation-to-copy path involves enough instructions (security attribute marshaling, size calculations, structure traversal) that a dedicated racing thread on another CPU core wins reliably within seconds. The attacker does not need exotic timing control; running both threads in tight loops with appropriate CPU affinity (SetThreadAffinityMask to pin each thread to a different core) produces consistent results.

The full chain is: win the race to redirect a kernel write into the process token, overwrite the token's privilege or SID fields to grant SYSTEM-level access, then spawn a new process that inherits the elevated token. The original process appears to be a normal user-mode application until the moment the token swap succeeds.

AutoPiff categorizes this as race_condition with detection rules:

  • added_lock_around_toctou
  • added_spinlock_guard
  • added_mutex_guard

Patch analysis

The patch in KB5039212 (build 10.0.22621.3733) addresses the root cause directly: it adds lock acquisition around the check-and-copy sequence in AuthzBasepCopyoutInternalSecurityAttributes. The buffer address validation and the subsequent memory copy now execute atomically with respect to address-space modifications. No thread can remap the buffer's virtual address range while the lock is held, closing the TOCTOU window entirely.

This is a clean fix. The kernel does not capture the buffer contents early, does not pin the pages, and does not switch to MDL-based access. It simply serializes the operation that should have been serialized from the beginning. AutoPiff detects this via the added_lock_around_toctou, added_spinlock_guard, and added_mutex_guard rules, which flag functions where synchronization primitives were introduced around previously unguarded check-then-use sequences.

Detection

YARA Rule

rule CVE_2024_30088_ntoskrnl {
    meta:
        description = "Detects vulnerable version of ntoskrnl.exe (pre-patch)"
        cve = "CVE-2024-30088"
        author = "KernelSight"
        severity = "high"
    strings:
        $mz = { 4D 5A }
        $driver_name = "ntoskrnl.exe" wide ascii nocase
        $version = "10.0.22621.3672" wide ascii
        $authz_func = "AuthzBasepCopyoutInternalSecurityAttributes" ascii
        $access_check = "NtAccessCheckByTypeAndAuditAlarm" ascii
    condition:
        $mz at 0 and $driver_name and $version and ($authz_func or $access_check)
}

ETW Indicators

Provider Event / Signal Relevance
Microsoft-Windows-Security-Auditing Event 4672 — Special privileges assigned Unexpected SeDebugPrivilege or SeTcbPrivilege acquisition after token swap
Microsoft-Windows-Kernel-Audit Authorization subsystem events Anomalous call patterns to AuthzBasepCopyoutInternalSecurityAttributes and related access-check routines
Microsoft-Windows-Kernel-Process Process token modification Runtime process token replacement indicating TOCTOU exploitation
Microsoft-Windows-Security-Auditing Event 4688 — Process creation SYSTEM-privilege processes correlated with the exploiting process lineage
Microsoft-Windows-Kernel-Memory Section object mapping events Rapid NtMapViewOfSection / NtUnmapViewOfSection cycling on the same virtual address range

Behavioral Indicators

The exploitation pattern produces several observable signals, any of which in combination should trigger investigation:

  • A multi-threaded process issuing rapid NtAccessCheckByTypeAndAuditAlarm calls from one thread while a second thread cycles NtUnmapViewOfSection / NtMapViewOfSection on the same virtual address range. This two-thread pattern is the signature of the race.
  • High-frequency virtual address remapping: thousands of map/unmap cycles per second targeting a single buffer region that was passed as the output buffer to the authorization syscall. Legitimate applications have no reason to remap memory at this rate.
  • Security attribute copyout writing kernel data to a kernel-mode page rather than the original user-mode buffer, indicating the race was won. This is detectable through kernel memory write auditing if enabled.
  • A process token overwritten mid-execution: a standard user token replaced with a SYSTEM token without a corresponding logon event, service call, or UAC prompt. This is the exploitation's end state and the clearest indicator of compromise.
  • Multiple threads with tight CPU affinity (SetThreadAffinityMask) to maximize the probability of winning the race window. While not malicious on its own, combined with the syscall pattern above, it strongly suggests exploitation.

Why this matters beyond one CVE

CVE-2024-30088 is instructive beyond its own impact because it represents a class of vulnerability that remains underexplored. TOCTOU bugs in virtual memory mappings are fundamentally different from the file-system TOCTOU bugs that security researchers traditionally study. The kernel validates a virtual address, but virtual addresses are not stable identifiers. Any code path where the kernel validates a user-mode pointer and then dereferences it in a separate step, without pinning the underlying pages or capturing the data in a single atomic operation, is potentially vulnerable to the same class of attack.

The fix pattern (adding a lock) is simple. The audit pattern (finding unprotected validate-then-use sequences on user-mode pointers) is systematic and automatable. The broader question is how many similar gaps exist in other ntoskrnl functions that copy data to user-mode buffers. AutoPiff's added_lock_around_toctou rule was specifically designed to catch this class of fix across the entire Windows kernel binary set.

References