Lab 04 — Windows Token & Service Privilege-Escalation Analyzer
Intensity: advanced. Windows local privilege escalation (LPE) is where the abstract "token = SIDs + privileges + integrity" model becomes concrete and dangerous. This lab builds the analyzer that takes a process token and a service inventory and reports the LPE primitives present — the powerful privileges (SeImpersonate, SeDebug, SeBackup…), unquoted service paths, and weak service-binary ACLs — then composes them into
user → SYSTEMescalation paths. It is the defensive counterpart ofwhoami /priv+accesschk: it explains and prioritizes; it does not exploit.Prerequisites: Phase 05 WARMUP Chapter 5 (tokens/SIDs/ACLs/privileges) and Chapter 6 (integrity levels, why UAC is not a boundary). All inputs are synthetic.
Table of Contents
- 1. The Windows Token Model
- 2. Privilege-Based Escalation Primitives
- 3. Service-Config Escalation Primitives
- 4. Integrity Levels Gate the Service Path
- 5. Threat Model
- 6. Architecture of This Lab
- 7. Implementation Steps
- 8. Running and Expected Evidence
- 9. Remediation
- 10. Hardening / Stretch Tasks
- 11. Common Mistakes
- 12. Interview Questions
- 13. Resume Bullet
- 14. References
1. The Windows Token Model
When a user logs on, Windows builds an access token and attaches it to their processes (Phase 05 WARMUP, Chapter 5). The token carries three things this lab reasons about:
- SIDs — the user's Security Identifier plus the SIDs of every group they belong to. Object access (DACLs) is decided by comparing the token's SIDs against an object's ACEs.
- Privileges — token-wide powers that are separate from object ACLs. Some are benign
(
SeChangeNotifyPrivilege); a handful are near-equivalent to "owns the machine." - Integrity level — Low / Medium / High / System. Mandatory Integrity Control prevents a lower-integrity process from writing a higher-integrity object.
Crucially: UAC is not a security boundary (Chapter 6). When you reason about an attacker who already has code execution as a service account or a standard user, you must not count UAC as the thing stopping escalation — the real question is "what primitives does this token hand the attacker?"
2. Privilege-Based Escalation Primitives
Several privileges are, by themselves, a documented route to NT AUTHORITY\SYSTEM:
| Privilege | Primitive | Why it escalates |
|---|---|---|
SeImpersonatePrivilege | impersonate-to-system | Held by service accounts by default. The "potato" family of techniques coerces a SYSTEM service to authenticate and impersonates its token. The most common real-world service LPE. |
SeAssignPrimaryTokenPrivilege | assign-primary-token | Set a process's primary token — pair with impersonation to spawn as SYSTEM. |
SeDebugPrivilege | debug-any-process | Open any process (including SYSTEM ones) and inject/steal its token. |
SeBackupPrivilege | read-any-file | Bypass file DACLs for "backup" — read the SAM/SYSTEM registry hives and extract credentials. |
SeRestorePrivilege | write-any-file | Bypass DACLs for "restore" — overwrite a privileged binary or registry key. |
SeTakeOwnershipPrivilege | take-ownership | Take ownership of any securable object, then grant yourself full control. |
SeLoadDriverPrivilege | load-kernel-driver | Load a (signed) driver — kernel-level code execution. |
SeTcbPrivilege | act-as-part-of-the-os | Effectively is SYSTEM (critical). |
analyze_token flags each held dangerous privilege. The point for a defender: a service
account holding SeImpersonatePrivilege (which is normal) means a web-app RCE in that
service is a straight line to SYSTEM — so that service must be hardened and monitored
accordingly.
3. Service-Config Escalation Primitives
Windows services run as SYSTEM and start automatically — so any way a low-privileged user can control what code a SYSTEM service runs is an escalation. Two classic misconfigurations:
Unquoted service path. A service binary path with spaces that is not quoted, e.g.
C:\Program Files\My App\svc.exe. Windows, when launching an unquoted path, tries each
space-delimited prefix as an executable:
1) C:\Program.exe
2) C:\Program Files\My.exe
3) C:\Program Files\My App\svc.exe (the intended one)
If a low-priv user can write to C:\ or C:\Program Files\, they plant Program.exe /
My.exe, and the SYSTEM service executes it. unquoted_candidate_dirs computes exactly those
candidate directories; analyze_service flags the service if any candidate is in the token's
writable_paths.
Weak service-binary ACL. If the service's binary itself (or its directory) is writable by the current user, they simply replace it and wait for the service to restart as SYSTEM.
4. Integrity Levels Gate the Service Path
A Low-integrity process — e.g. a sandboxed browser renderer or an AppContainer — cannot
write machine-wide service configuration, even if the file ACL would otherwise allow it,
because Mandatory Integrity Control blocks the write to the higher-integrity object. So the
analyzer suppresses service-config findings for low-integrity tokens (_can_modify_machine_config).
This nuance is exactly the kind of thing that separates a real finding from a false positive —
and a frequent interview discriminator.
5. Threat Model
Asset : NT AUTHORITY\SYSTEM (full control of the host)
Actor : an attacker with code execution as a service account or standard user (post-RCE)
Invariant : no low/medium-privilege principal can cause SYSTEM to run attacker code or
read/write privileged objects
Primitives : dangerous token privileges; unquoted service paths with a writable insertion dir;
writable service binaries on SYSTEM services
Demonstrated : analyze() emits the primitive; escalation_paths() shows 'user -> SYSTEM'
Out of scope : actually performing the escalation (this is a static analyzer)
6. Architecture of This Lab
Token(user, sids, privileges, integrity, writable_paths)
Service(name, binary_path, path_quoted, binary_writable_by, running_as)
│
├── analyze_token(token) → privilege primitives
├── analyze_service(service, token) → unquoted-path / writable-binary (integrity-gated)
▼
analyze(token, services) → [Finding(primitive, detail, severity, target)]
escalation_paths(...) → ["user --[primitive]--> SYSTEM", ...]
is_clean(...) → bool
7. Implementation Steps
Edit lab.py (the dangerous-privilege table and dataclasses are provided):
_can_modify_machine_config(token)—Falsefor low integrity, elseTrue.analyze_token(token)— flag each held privilege that's inDANGEROUS_PRIVILEGES.unquoted_candidate_dirs(path)— the directory parser (see §3 and the docstring).analyze_service(service, token)— only SYSTEM services, only when machine config is writable; emitunquoted-service-pathand/orwritable-service-binary.analyze,escalation_paths,is_clean— composition and reporting.
8. Running and Expected Evidence
LAB_MODULE=solution pytest -q # reference — all tests pass
pytest -q # your implementation
The suite proves: the unquoted-path parser returns the right candidate dirs; each dangerous privilege is flagged at the right severity while benign privileges are ignored; an unquoted path with a writable insertion dir is flagged but a quoted path (or no writable dir) is safe; a writable SYSTEM-service binary is flagged; a low-integrity token cannot reach service-config findings; a non-SYSTEM service is not a target; and a hardened host is reported clean with no escalation paths.
9. Remediation
- Quote every service path (
"C:\Program Files\My App\svc.exe") — eliminates unquoted-path hijacks. - Tighten service binary/directory ACLs so only administrators/SYSTEM can write.
- Remove unnecessary privileges from service accounts; if
SeImpersonatePrivilegeis required, isolate and monitor that service heavily (its compromise = SYSTEM). - Run risky code at Low integrity / in AppContainer so it can't touch machine config.
- Patch — many privilege primitives (potato variants) rely on coercion techniques addressed by updates and by disabling unneeded authentication relay surfaces.
10. Hardening / Stretch Tasks
- Add
SeImpersonate+ service-account correlation: rank a finding higher when the token is a service account, because the impersonation LPE is then directly reachable. - Model registry-based service config (writable
ImagePathorParameters) as another primitive. - Add DLL search-order hijacking: a writable directory on a SYSTEM service's DLL search path.
- Emit MITRE ATT&CK technique IDs per primitive (e.g. T1574.009 unquoted path) and a normalized finding the Phase 05 event-normalizer can consume.
- Compute a single highest-severity escalation path and an executive one-liner.
11. Common Mistakes
- Counting UAC as a boundary. It isn't (Phase 05 WARMUP, Chapter 6); reason about token primitives directly.
- Flagging
SeImpersonatePrivilegeas a misconfiguration. It's normal for service accounts — the finding is "this service is one RCE from SYSTEM," not "someone set this wrong." - Ignoring integrity level. A writable service config is not exploitable from a low-integrity sandbox; reporting it as critical is a false positive.
- Only checking the binary, not the path. Unquoted-path hijacks need no write to the binary at all — just to an insertion directory.
- Treating object ACLs and privileges as the same thing. Privileges are token-wide powers that bypass ACLs; both must be reviewed.
12. Interview Questions
- A service account has
SeImpersonatePrivilege— is that a vulnerability? It's normal, but it means an RCE in that service reaches SYSTEM via token impersonation (the potato family). The finding is to harden/monitor that service, not "fix the privilege." - What's an unquoted service path attack? A SYSTEM service with an unquoted spaced path lets
Windows try each prefix as an executable; a writable insertion directory (
C:\,C:\Program Files\) lets a low-priv user plant a binary the service runs as SYSTEM. Fix: quote the path. - Why isn't UAC a boundary here? Microsoft says so explicitly; it has by-design elevation paths. The real boundaries are the kernel, integrity levels, and the user/SYSTEM divide — so you model escalation from token primitives, not "getting past UAC."
- How does integrity level change the analysis? A low-integrity process can't write machine-wide config, so service-config LPEs aren't reachable from it; reporting them as exploitable would be a false positive.
- Difference between a privilege and an ACL grant? An ACL grant lets a SID access a specific
object; a privilege is a token-wide power, several of which (
SeDebug,SeBackup,SeImpersonate) bypass ACLs entirely. Both are part of the escalation surface.
13. Resume Bullet
Built a Windows privilege-escalation analyzer that maps token privileges (SeImpersonate, SeDebug, SeBackup, …), unquoted service paths, and weak service ACLs to
user → SYSTEMescalation paths, with integrity-level gating to suppress non-exploitable findings.
14. References
- Microsoft Docs: access tokens, privilege constants, Mandatory Integrity Control, "UAC is not a security boundary."
- Windows Internals (Russinovich, Solomon, Ionescu) — tokens, SIDs, privileges.
- Sysinternals
accesschk, Process Explorer;whoami /priv. - MITRE ATT&CK: T1134 (Access Token Manipulation), T1574.009 (Unquoted Path Interception), T1078 (Valid Accounts).
- Phase 05 WARMUP Chapters 5–6; Phase 05 HITCHHIKERS-GUIDE "Reading a Windows token".