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 → SYSTEM escalation paths. It is the defensive counterpart of whoami /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

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:

PrivilegePrimitiveWhy it escalates
SeImpersonatePrivilegeimpersonate-to-systemHeld 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.
SeAssignPrimaryTokenPrivilegeassign-primary-tokenSet a process's primary token — pair with impersonation to spawn as SYSTEM.
SeDebugPrivilegedebug-any-processOpen any process (including SYSTEM ones) and inject/steal its token.
SeBackupPrivilegeread-any-fileBypass file DACLs for "backup" — read the SAM/SYSTEM registry hives and extract credentials.
SeRestorePrivilegewrite-any-fileBypass DACLs for "restore" — overwrite a privileged binary or registry key.
SeTakeOwnershipPrivilegetake-ownershipTake ownership of any securable object, then grant yourself full control.
SeLoadDriverPrivilegeload-kernel-driverLoad a (signed) driver — kernel-level code execution.
SeTcbPrivilegeact-as-part-of-the-osEffectively 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):

  1. _can_modify_machine_config(token)False for low integrity, else True.
  2. analyze_token(token) — flag each held privilege that's in DANGEROUS_PRIVILEGES.
  3. unquoted_candidate_dirs(path) — the directory parser (see §3 and the docstring).
  4. analyze_service(service, token) — only SYSTEM services, only when machine config is writable; emit unquoted-service-path and/or writable-service-binary.
  5. 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 SeImpersonatePrivilege is 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 ImagePath or Parameters) 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 SeImpersonatePrivilege as 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

  1. 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."
  2. 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.
  3. 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."
  4. 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.
  5. 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 → SYSTEM escalation 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".