Skip to content

Token Swapping

Overwriting the _TOKEN pointer or token privilege fields in kernel memory to escalate a process to SYSTEM-level privileges.

Description

Every Windows process has a Token field in its _EPROCESS structure that points to a _TOKEN object containing the process's full security context -- privileges, security identifiers (SIDs), integrity level, and session information. Token swapping exploits use an arbitrary read/write primitive to either replace the Token pointer in _EPROCESS with one copied from the SYSTEM process (PID 4), or directly modify the privilege bitmasks within the existing token to grant all privileges. This is the most common final step in Windows kernel privilege escalation chains.

The technique is straightforward once an arbitrary kernel read/write primitive is available: locate the current process's _EPROCESS structure, read the SYSTEM process's token pointer, and overwrite the current process's token field. After the swap, the attacker's process inherits the full security context of SYSTEM, including SeDebugPrivilege, SeTcbPrivilege, and membership in all administrative groups. The simplicity and reliability of this approach has made it the dominant privilege escalation technique in modern Windows kernel exploits.

Token swapping requires build-specific _EPROCESS offsets because the Token field moves between Windows versions. Exploits typically include a lookup table mapping build numbers to the correct offset. The technique also requires care with the EX_FAST_REF encoding used for the token pointer and with reference counting to avoid crashes during process teardown.

Mechanism

Step-by-Step Token Pointer Swap

  1. Obtain kernel R/W primitive -- typically via I/O Ring buffer table corruption, pipe attribute read-back, or another method
  2. Locate current process _EPROCESS -- use NtQuerySystemInformation(SystemHandleInformation) to scan for the current process handle and extract the _EPROCESS kernel address
  3. Locate SYSTEM process _EPROCESS -- traverse the ActiveProcessLinks doubly-linked list starting from the current process, searching for PID 4 (the SYSTEM process), or use PsInitialSystemProcess if its address is known
  4. Read SYSTEM token -- read the EX_FAST_REF value at SYSTEM_EPROCESS + Token_offset; mask off the low 4 bits to extract the raw token pointer
  5. Write SYSTEM token to current process -- overwrite the EX_FAST_REF value at CURRENT_EPROCESS + Token_offset with the SYSTEM token value, setting the low reference count bits to a valid non-zero value (e.g., 0x6 or 0x7)
  6. Verify escalation -- call OpenProcess on a SYSTEM-owned process such as csrss.exe to confirm elevated privileges

_EPROCESS Token Field

_EPROCESS
  +0x4B8  Token              // EX_FAST_REF (Windows 10 21H2 / Windows 11 22H2)

The EX_FAST_REF encoding stores the actual pointer in the upper 60 bits (on x64) and a reference count in the lower 4 bits. When reading the token pointer, mask with & ~0xF. When writing, set the low bits to a small non-zero value to maintain a valid reference.

_TOKEN Structure (Key Fields)

_TOKEN (+0x000 to ~0x480)
  +0x040  TokenId                    // LUID
  +0x048  AuthenticationId           // LUID (SYSTEM = 0x3e7)
  +0x060  Privileges                 // _SEP_TOKEN_PRIVILEGES
    +0x000  Present                  // ULONG64 bitmask
    +0x008  Enabled                  // ULONG64 bitmask
    +0x010  EnabledByDefault         // ULONG64 bitmask
  +0x098  IntegrityLevelIndex
  +0x0D0  SessionId
  +0x0E8  UserAndGroupCount
  +0x4B8  UserAndGroups              // (varies by build)

Offsets vary per Windows build. Exploits must use correct offsets for the target version.

Alternative: Privilege Field Manipulation

Instead of swapping the entire token pointer, some exploits modify fields within the existing _TOKEN object:

  1. Find current process token address -- read _EPROCESS.Token, mask off low bits, obtain the _TOKEN kernel address
  2. Overwrite _SEP_TOKEN_PRIVILEGES.Present with 0xFFFFFFFFFFFFFFFF -- all privilege bits marked as present
  3. Overwrite _SEP_TOKEN_PRIVILEGES.Enabled with 0xFFFFFFFFFFFFFFFF -- all privilege bits enabled
  4. Use elevated privileges -- SeDebugPrivilege allows opening any process, SeImpersonatePrivilege allows impersonating SYSTEM tokens, SeLoadDriverPrivilege allows loading arbitrary drivers

This approach requires two write operations (to Present and Enabled) but avoids changing the token pointer itself, which sidesteps reference counting issues and some detection mechanisms.

Token Pointer Swap vs In-Place Privilege Overwrite

Aspect Pointer Swap Privilege Overwrite
Write operations 1 (token pointer) 2 (Present + Enabled)
Inherited groups All SYSTEM groups Original groups only
Reference count risk Must manage EX_FAST_REF None
Detection surface Token pointer change visible Privilege fields change visible
Complexity Simpler single write Slightly more writes but safer cleanup

Finding the SYSTEM Token

Several techniques exist for locating the SYSTEM process token:

  • NtQuerySystemInformation (SystemHandleInformation): Returns kernel object addresses for all open handles. Scan for handles belonging to PID 4 to find the SYSTEM _EPROCESS base address, then read the Token field at the known offset.
  • ActiveProcessLinks traversal: Starting from any known _EPROCESS address, traverse the ActiveProcessLinks doubly-linked list to enumerate all processes. The SYSTEM process (PID 4) is typically the first entry since PsInitialSystemProcess points to it.
  • KPCR/KPRCB thread traversal: On some Windows versions, the KPCR at gs:[0] contains a pointer to the current KTHREAD, which links back to _EPROCESS. This provides a starting point for process list traversal without needing an info leak.
  • Hardcoded offsets: The Token field offset within _EPROCESS varies by build number. Common values include 0x4B8 (Windows 10 21H2, Windows 11 22H2) and 0x4B8 (Windows 11 23H2). Exploits include lookup tables mapping build numbers to offsets.

Practical Considerations

  • EX_FAST_REF encoding: The token pointer has low 4 bits used as a reference count. Failing to preserve or set these bits correctly causes immediate dereference failures and BSOD.
  • Build-specific offsets: _EPROCESS layout changes with every major Windows build. The exploit must detect the target OS version (via NtQuerySystemInformation or PEB.OSBuildNumber) and select the correct offset.
  • Token reference counting: Swapping the pointer without adjusting reference counts can cause a BSOD during process teardown. Robust exploits increment the reference count on the SYSTEM token and decrement the count on the original token, or they complete exploitation and spawn a child process before cleanup.
  • Thread impersonation tokens: After swapping the process token, clear any thread-level impersonation tokens with NtSetInformationThread to ensure the new security context applies to all operations.
  • PPL (Protected Process Light): Processes marked as PPL have additional token integrity checks. Token swapping alone may not bypass PPL restrictions.
  • Kernel callbacks: EDR products register PsSetCreateProcessNotifyRoutine callbacks that may detect unexpected token changes and flag or terminate the process.

Mitigations and Limitations

  • Kernel Data Protection (KDP): On Windows 11 with VBS enabled, KDP can protect critical kernel data structures in VBS-backed memory pages. If _EPROCESS.Token is protected, direct overwrite attempts will fault. This is an active area of mitigation development but not yet widely applied to tokens.
  • HVCI (Hypervisor-Enforced Code Integrity): Makes obtaining the initial arbitrary R/W primitive harder by preventing code execution from corrupted function pointers, but does not directly block data-only token manipulation.
  • Credential Guard: Isolates LSASS secrets in a secure VBS enclave but does not protect kernel token structures from direct memory writes.
  • No direct mitigation for data-only attacks: Once an attacker has kernel arbitrary R/W, there is no mainstream mitigation that specifically prevents token field modification. The defense relies on preventing the initial primitive.

Verification

After performing a token swap, the exploit can verify success by:

  • Calling OpenProcess on a SYSTEM-owned process (e.g., csrss.exe) -- success indicates SYSTEM privileges
  • Spawning cmd.exe and running whoami to confirm the new security context
  • Reading back the _EPROCESS.Token field to confirm the write landed correctly
CVE Driver Role
CVE-2024-30085 cldflt.sys Token swap after obtaining kernel R/W via pool overflow
CVE-2024-30088 ntoskrnl.exe Token swap after TOCTOU race
CVE-2024-38193 afd.sys Privilege escalation via token after UAF
CVE-2023-28252 clfs.sys Token swap in CLFS exploit chain
CVE-2023-29336 win32kfull.sys Token swap after UAF

See Also