Windows InternalsAdvanced

Hunting Tokens: Impersonation Without a Shell

Stealing and impersonating primary tokens straight from process memory.

OpenProcessToken(h, &tok) DuplicateTokenEx(tok, ...) ImpersonateLoggedOnUser(dup) [+] running as NT AUTHORITY
// internals
Token theft
▸ On this page 7 sections

Background

Every process on Windows carries an access token describing its security context. If you can open a more privileged process and duplicate its token, you can impersonate that context - often without ever spawning a new shell, which is exactly what EDR is watching for.

This is a lab note on the mechanics. Code is illustrative pseudo-C.

Locating a useful token

Enumerate processes, filter for ones running as a target identity, and open their tokens with TOKEN_DUPLICATE access:

c · illustrative
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
HANDLE tok;
OpenProcessToken(h, TOKEN_DUPLICATE | TOKEN_QUERY, &tok);
✓ Tip

You only need SeDebugPrivilege for many cross-session opens - check what you already hold before reaching for anything louder.

Impersonating

Duplicate the token as an impersonation token, then apply it to the current thread:

c · illustrative
DuplicateTokenEx(tok, MAXIMUM_ALLOWED, NULL,
                 SecurityImpersonation, TokenImpersonation, &dup);
ImpersonateLoggedOnUser(dup);
⚠ OPSEC

ImpersonateLoggedOnUser applies the token to the calling thread, not the process. That matters: process-level token queries (e.g. EDR walking NtQueryInformationProcess) will still see the original identity. Only file / network / registry calls made from that thread run under the impersonated context.

At this point, file and network calls from the current thread run under the duplicated identity. Revert with RevertToSelf() when done.

Creating a process under the stolen token

If you need a full process context (not just thread impersonation), use CreateProcessWithTokenW:

c · illustrative
HANDLE primary;
DuplicateTokenEx(tok, MAXIMUM_ALLOWED, NULL,
                 SecurityImpersonation, TokenPrimary, &primary);

STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
CreateProcessWithTokenW(primary, LOGON_NETCREDENTIALS_ONLY,
                        L"cmd.exe", NULL, 0, NULL, NULL, &si, &pi);
! Note

LOGON_NETCREDENTIALS_ONLY keeps local execution under your own identity but uses the stolen token for any network authentication. Useful for lateral movement without touching disk on the source host.

Privilege escalation via token manipulation

Certain privileges - when held by your impersonation token - are directly exploitable. SeImpersonatePrivilege is the classic example: service accounts running under IIS, MSSQL, or DCOM hold it by default, and it lets you impersonate any token that authenticates to a COM server you control (the "potato" family of techniques).

powershell · check current privileges
whoami /priv
# Look for:
# SeImpersonatePrivilege    Enabled
# SeAssignPrimaryTokenPrivilege    Enabled
⚠ Warning

Enabling a privilege requires AdjustTokenPrivileges - but you can only enable privileges already present in the token. You can't grant yourself SeDebugPrivilege from nothing; you need a process that already holds it.

Detection surface

Token theft is deliberately low-noise, but not invisible:

  • OpenProcess with PROCESS_QUERY_INFORMATION on high-integrity processes is flagged by many EDRs when the caller is lower-integrity.
  • DuplicateHandle across process boundaries (if relayed through a broker) is visible in kernel callbacks.
  • CreateProcessWithTokenW emits a process-creation event with mismatched parent/token SIDs - straightforward to alert on.
  • Thread impersonation via ImpersonateLoggedOnUser leaves an ETW event under Microsoft-Windows-Security-Auditing if object access auditing is enabled.

The stealthiest path stays at thread impersonation, avoids cross-process handle duplication where possible, and cleans up the duplicated handle immediately after use.

Takeaway

Token theft is a first-class technique precisely because it blends into Windows' own impersonation model. The defensive answer isn't to disable impersonation - too much breaks - but to audit the chain: which service accounts hold SeImpersonatePrivilege, which processes run at high integrity with queryable handles, and whether your EDR correlates thread-level identity changes rather than just process-creation events.

Tested on
Windows 11 23H2 · Windows Server 2022 (lab)
Tools
token-tool (pseudo)
Status
by-design · documented

References