Back to blog
Mar 20268 min read

The Token Game

The keys to the kingdom are kernel objects, not passwords

You dump credentials, you crack hashes, you spray passwords. But the thing that actually controls what you can do on a Windows system isn't a password. It's a kernel object called an access token. Every process and every thread has one. It dictates who you are, what groups you belong to, and what privileges you hold.

Most operators treat tokens as something that happens in the background. You run whoami and move on. But understanding tokens is the difference between getting stuck at "access denied" and owning the box.

Two Types of Tokens

Windows has two token types, and confusing them is where most people get tripped up.

PRIMARY TOKEN
Assigned to every process
Created at logon time
Inherited by child processes
Used for CreateProcessAsUser()
Think: "who started this process"
IMPERSONATION TOKEN
Assigned to a thread
Created during impersonation
Temporary, per-operation
Used for access checks
Think: "who is this thread pretending to be"

Here's the key insight: when Windows checks your access to a resource, it looks at the thread token first. If the thread has an impersonation token, that's what gets checked. If it doesn't, it falls back to the process token. This is why impersonation is so powerful. You don't need to change who you are. You just need to change who the current thread is pretending to be.

Why whoami Lies to You

whoami shows the process token. Always. If your thread is impersonating a domain admin but the process is running as a service account, whoami still shows the service account. You might have domain admin access and not know it.

token-context.txt
// What you see
C:\> whoami
nt authority\local service
// What's actually happening on your thread
Process token: LOCAL SERVICE
Thread token: CORP\domain_admin (impersonating)
// What Windows checks when you access \\DC01\C$
Uses thread token. Access granted.

Impersonation Levels

Not all impersonation tokens are equal. Windows defines four levels, and the level determines what you can actually do with the token.

LevelWhat You Can DoUseful?
AnonymousNothing. Can't identify the client.NO
IdentificationRead identity and privileges, but can't act as them.RECON ONLY
ImpersonationAct as the client on local resources.YES
DelegationAct as the client on local and remote resources.JACKPOT

Impersonation level lets you access local resources as that user. Delegation level lets you hop to other machines as that user. When you steal a token from a process, check the impersonation level. A delegation token from a domain admin is a lateral movement goldmine. An identification token is worthless for anything beyond reading their username.

Stealing Tokens

Every process on the box has a token. If you have the right privileges (SeDebugPrivilege, or SYSTEM), you can open any process, duplicate its token, and use it. This is what tools like steal_token and impersonate do under the hood.

token-theft.pseudo
// Step 1: Open the target process
hProc = OpenProcess(PROCESS_QUERY_INFORMATION, targetPID)
// Step 2: Get its token
OpenProcessToken(hProc, TOKEN_DUPLICATE, &hToken)
// Step 3: Duplicate it for our use
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, &hDup)
// Step 4: Become them
ImpersonateLoggedOnUser(hDup) // thread is now that user
// or
CreateProcessAsUser(hDup, "cmd.exe") // new process as that user

The Token Hunting Workflow

On any compromised box, the process list is a menu of available identities. List processes, check who owns each one, and decide whose token you want.

1
List processes with owners
ps in your C2, or tasklist /v natively. Look for processes running as domain users, service accounts, or privileged accounts.
2
Identify high-value tokens
Domain admins are obvious. But also look for service accounts with delegation rights, accounts in privileged AD groups, or accounts that have sessions on other machines you need to reach.
3
Steal and use
Duplicate the token and either impersonate on the current thread or spawn a new process. Check the impersonation level first. If it's only Identification, the token is useless for access.

Make Token: No Process Needed

If you have plaintext credentials but no process running as that user, you can forge a token from scratch using LogonUserW(). This creates a new logon session and gives you a primary token without needing an interactive logon, an RDP session, or a running process to steal from.

make-token.pseudo
// Create a token from credentials
LogonUserW("admin", "CORP", "password",
LOGON32_LOGON_NEW_CREDENTIALS, // type 9
LOGON32_PROVIDER_DEFAULT, &hToken)
// Now use it
ImpersonateLoggedOnUser(hToken) // network access as CORP\admin

LOGON32_LOGON_NEW_CREDENTIALS (type 9) is the magic flag. It creates a token that uses the supplied credentials for network access but keeps your current identity for local access. This is what every C2's make_token command does under the hood.

The Takeaway

Passwords get you in. Tokens let you move. Every lateral movement technique, every privilege escalation, every impersonation attack comes back to tokens. The operator who understands token types, impersonation levels, and when to steal vs when to forge will always move faster than the one who just runs hashdump and hopes for the best.

Windows InternalsPrivilege EscalationTradecraft

Token behaviour described here applies to Windows 10/11 and Server 2016+. API availability may vary on older platforms.