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
- Obtain kernel R/W primitive -- typically via I/O Ring buffer table corruption, pipe attribute read-back, or another method
- Locate current process
_EPROCESS-- useNtQuerySystemInformation(SystemHandleInformation)to scan for the current process handle and extract the_EPROCESSkernel address - Locate SYSTEM process
_EPROCESS-- traverse theActiveProcessLinksdoubly-linked list starting from the current process, searching for PID 4 (the SYSTEM process), or usePsInitialSystemProcessif its address is known - Read SYSTEM token -- read the
EX_FAST_REFvalue atSYSTEM_EPROCESS + Token_offset; mask off the low 4 bits to extract the raw token pointer - Write SYSTEM token to current process -- overwrite the
EX_FAST_REFvalue atCURRENT_EPROCESS + Token_offsetwith the SYSTEM token value, setting the low reference count bits to a valid non-zero value (e.g.,0x6or0x7) - Verify escalation -- call
OpenProcesson a SYSTEM-owned process such ascsrss.exeto 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:
- Find current process token address -- read
_EPROCESS.Token, mask off low bits, obtain the_TOKENkernel address - Overwrite
_SEP_TOKEN_PRIVILEGES.Presentwith0xFFFFFFFFFFFFFFFF-- all privilege bits marked as present - Overwrite
_SEP_TOKEN_PRIVILEGES.Enabledwith0xFFFFFFFFFFFFFFFF-- all privilege bits enabled - Use elevated privileges --
SeDebugPrivilegeallows opening any process,SeImpersonatePrivilegeallows impersonating SYSTEM tokens,SeLoadDriverPrivilegeallows 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
_EPROCESSbase address, then read theTokenfield at the known offset. - ActiveProcessLinks traversal: Starting from any known
_EPROCESSaddress, traverse theActiveProcessLinksdoubly-linked list to enumerate all processes. The SYSTEM process (PID 4) is typically the first entry sincePsInitialSystemProcesspoints to it. - KPCR/KPRCB thread traversal: On some Windows versions, the
KPCRatgs:[0]contains a pointer to the currentKTHREAD, which links back to_EPROCESS. This provides a starting point for process list traversal without needing an info leak. - Hardcoded offsets: The
Tokenfield offset within_EPROCESSvaries by build number. Common values include0x4B8(Windows 10 21H2, Windows 11 22H2) and0x4B8(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:
_EPROCESSlayout changes with every major Windows build. The exploit must detect the target OS version (viaNtQuerySystemInformationorPEB.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
NtSetInformationThreadto 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
PsSetCreateProcessNotifyRoutinecallbacks 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.Tokenis 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
OpenProcesson a SYSTEM-owned process (e.g.,csrss.exe) -- success indicates SYSTEM privileges - Spawning
cmd.exeand runningwhoamito confirm the new security context - Reading back the
_EPROCESS.Tokenfield to confirm the write landed correctly
Related CVEs
| 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
- Token Manipulation -- broader token manipulation primitives
- I/O Ring -- commonly used to obtain the R/W primitive needed for token swap
- Pipe Attributes -- alternative R/W primitive for token modification
- Pool Spray / Heap Feng Shui -- pool grooming step that precedes token swap in many exploit chains