Windows Fundamentals · intermediate · ~11 min
- Explain how a Windows **access token** (SID + group SIDs + privileges) decides what a process may do - Identify the high-value privileges (`SeImpersonatePrivilege`, `SeDebugPrivilege`, `SeBackupPrivilege`) and *why* they matter - Describe **integrity levels** and **UAC** as defence-in-depth layers (and their limits) - Apply **least privilege** to service accounts and detect risky privilege assignments - Recognize the difference between a **primary** and an **impersonation** token
On Windows, what a process may do is decided by its access token, not merely by the username behind it. The token is the unit of authorization — understanding it explains both how privilege-escalation findings work and, more importantly, how to prevent and detect them.
This lesson builds on Users and groups: those accounts and groups become SIDs inside a token. It's an intermediate lesson because the payoff is defensive judgement — knowing why a low-privileged service account holding one extra privilege can be equivalent to full SYSTEM, and how to stop that.
Everything here is for hosts you own or are authorized to assess.
Most Windows privilege-escalation on real engagements isn't a memory-corruption exploit — it's abuse of an over-privileged token: a service account that shouldn't have SeImpersonatePrivilege, or a backup operator role handed out too widely. Defenders who understand the token model can remove those privileges before they're abused and can spot the event-log signs when someone tries.
It's also foundational for hardening: least privilege on Windows literally means shrinking tokens.
Definition. When you log on, Windows builds a token attached to every process you start. It carries:
SeDebugPrivilege).Windows checks the token, not the name, on every security decision.
A few privileges are effectively equivalent to full control, which is why they belong only on tightly-controlled accounts:
| Privilege | Why it's powerful | Who should rarely have it |
|---|---|---|
SeImpersonatePrivilege |
Can impersonate other tokens (basis of "potato" techniques) | General service accounts |
SeDebugPrivilege |
Open any process, including SYSTEM's | Non-admin users |
SeBackupPrivilege |
Read any file (e.g. the SAM/NTDS credential stores) | Broad user groups |
Dangerous assumption: "it's just a low-privileged service account, so it's safe." If it holds SeImpersonatePrivilege, it may be one step from SYSTEM.
Every process also has an integrity level: Low, Medium, High, System. A Medium-integrity process can't modify a High-integrity one — a containment layer.
UAC (User Account Control) is why an admin runs at Medium integrity by default and must elevate (consent) to get a High-integrity token. It's a valuable friction/defence layer, but not a hard security boundary — treat "admin = trusted" and design least privilege accordingly.
System (SYSTEM services) highest
High (elevated admin) <-- UAC elevation gets you here
Medium (normal user / un-elevated admin)
Low (sandboxed, e.g. browser tab) lowest
A primary token is what a process runs with. An impersonation token lets a thread temporarily act as another security context (used legitimately by services). Impersonation is the mechanism SeImpersonatePrivilege abuses — which is why granting it casually is dangerous.
Knowledge check:
SeBackupPrivilege. What sensitive files could it read, and why is that risky?Inspect (don't abuse) token privileges on a host you own:
whoami /priv # lists the current token's privileges + state (Enabled/Disabled)
whoami /groups # group SIDs in the token
whoami /all # SID, groups, and privileges together
In PowerShell, Get-LocalGroupMember and (for services) checking each service's logon account tell you which accounts carry which rights.
Windows decides what a process may do from its access token, not just from its username.
When you log on, Windows builds a token. The token carries:
SeDebugPrivilege, SeImpersonatePrivilege, and SeBackupPrivilege).Every process you start carries a copy of this token.
Some privileges are effectively keys to the SYSTEM account:
Processes also have an integrity level: Low, Medium, High, or System.
This is a defence-in-depth layer. A Medium-integrity process cannot modify a High-integrity one, even when both run as the same user. (Browsers use this idea to run sandboxed tabs at Low integrity.)
User Account Control (UAC) gives admins a standard (Medium) token by default. The elevated (High) token is only used after a consent prompt.
UAC is a convenience and a speed-bump, not a strong security boundary — Microsoft states this directly, and many bypasses exist.
So report a UAC bypass as escalation within admin, not as crossing a trust boundary.
Windows privilege escalation is often about token privileges. The classic line of reasoning is: "I'm a low-privilege service account, but I hold SeImpersonatePrivilege, so I can become SYSTEM."
Enumerating whoami /priv is a first move.
A defensive audit: on a host you own, list the privileges the current token holds and flag the dangerous ones, then show the hardening action (remove an unneeded privilege from an account via Group Policy / secpol.msc).
# audit-token-privileges.ps1 — run on a host you own.
# Show the current token's privileges and highlight high-risk ones.
$danger = 'SeImpersonatePrivilege','SeDebugPrivilege','SeBackupPrivilege',
'SeRestorePrivilege','SeTakeOwnershipPrivilege','SeTcbPrivilege'
$priv = whoami /priv | Select-String 'Se\w+Privilege' |
ForEach-Object { ($_ -split '\s+')[0] }
foreach ($p in $priv) {
$flag = if ($danger -contains $p) { 'HIGH-RISK' } else { 'ok' }
'{0,-28} {1}' -f $p, $flag
}
# Hardening: remove a privilege from an account via User Rights Assignment
# (secpol.msc -> Local Policies -> User Rights Assignment), granting it ONLY
# to accounts that truly need it (least privilege).
What it does. It reads the current token's privileges (whoami /priv), then labels any that appear in the high-risk list. The comment points to the fix: privileges are assigned through User Rights Assignment, so hardening means auditing who holds the dangerous ones and removing them from accounts that don't need them.
Expected output. A list of privilege names each tagged ok or HIGH-RISK — a quick, defensible inventory you can act on.
| Step | What happens | Why it matters |
|---|---|---|
$danger = … |
Defines the high-risk privilege set | The things to hunt for |
whoami /priv | Select-String … |
Extracts privilege names from the token | Reads the actual authorization state |
foreach … if ($danger -contains $p) |
Tags each as HIGH-RISK or ok | Turns raw data into a finding |
| comment: User Rights Assignment | Points to the remediation | Least privilege = remove unneeded rights |
The habit: inventory the token → flag the dangerous privileges → remove the ones that aren't justified.
Mistake 1 — "low-priv account = safe".
Wrong: ignoring a service account because it isn't an admin.
Why wrong: one privilege like SeImpersonatePrivilege can bridge to SYSTEM.
Fix: audit privileges, not just group membership; remove what isn't needed.
Mistake 2 — treating UAC as a wall. Wrong: assuming a Medium-integrity admin can't reach High. Why wrong: UAC is a consent/friction layer, not a hard boundary. Fix: apply least privilege and monitoring; don't rely on UAC alone.
Mistake 3 — broad backup/debug rights.
Wrong: granting SeBackupPrivilege/SeDebugPrivilege to wide groups for convenience.
Why wrong: those rights can read credential stores or any process memory.
Fix: grant them only to specific, controlled accounts; review regularly.
Recognize it: if you can't justify why an account holds a dangerous privilege, that's the finding.
whoami /priv shows a privilege as Disabled — it's still held; many privileges are enabled on demand, so "Disabled" is not "safe."Access is denied despite being admin — you're probably in a Medium-integrity (un-elevated) process; relaunch elevated.Get-CimInstance Win32_Service | Select Name,StartName); that account's token is what matters.Questions to ask: Which token is this action using? Which privileges does it hold (not just have enabled)? Is this account's privilege set justified by least privilege?
Security & safety — detection & hardening.
SeImpersonate/SeDebug/SeBackup off general service accounts; run services as low-privileged, purpose-built accounts (or virtual/gMSA accounts).Beginner habits: read whoami /all; never grant a privilege "just to make it work."
Advanced habits: enforce least privilege with tiered admin models and just-in-time elevation; monitor sensitive-privilege events centrally; prefer gMSA over static service accounts.
Beginner 1 — Read your token. Objective: on your own VM, run whoami /all and identify your SID, two group SIDs, and one privilege; say what that privilege allows. Concepts: tokens, SIDs, privileges.
Beginner 2 — Rank the danger. Objective: given SeImpersonatePrivilege, SeChangeNotifyPrivilege, and SeBackupPrivilege, rank them by risk and justify the top one in one sentence. Concepts: high-value privileges.
Intermediate 1 — Privilege audit script. Objective: adapt the audit script to flag high-risk privileges in the current token and print a count of how many are HIGH-RISK. Concepts: token inspection, defensive audit. Constraint: your own host.
Intermediate 2 — Detection mapping. Objective: for "a service account gains SYSTEM-equivalent privileges," name the Windows event id(s) a defender would watch and what a false positive might look like. Concepts: detection, event 4672/4673.
Challenge — Least-privilege service plan. Objective: for a fictional in-house service that only needs to read a folder and write a log, specify the account type and the exact privileges it should (and should not) hold, plus how you'd verify. Requirements: defensive; least privilege; no exploitation. Concepts: everything in this lesson.
SeImpersonatePrivilege, SeDebugPrivilege, SeBackupPrivilege are effectively keys to SYSTEM; keep them off general accounts.Next: continue into the privilege-escalation lessons, where these tokens are the thing attackers target and defenders lock down.