Tokens, Privileges & Access Control
The previous chapter covered authentication: proving to Windows who you are, through NTLM or Kerberos, before you get anywhere near a resource. That question is now settled. This chapter covers a separate and frequently confused question: once Windows knows who you are, what does it actually let you do? The answer lives in a small set of interlocking mechanisms, the access token, the security identifier, the access control list, and the privilege, and analysts who blur these together tend to misread both normal admin behavior and real escalation attempts. Understanding how they fit together is what makes a process listing, an event log entry, or a "why did this fail" ticket make sense.
Access Tokens
Every process and thread running under a user's context carries an access token, and that token is the actual object Windows consults when it decides whether an action is allowed. A token is not a password or a credential you can inspect and reuse directly. It is an in-memory kernel object built at logon that summarizes everything the security subsystem needs to know about a security context in one place.
When a process tries to open a file, create a registry key, or call a privileged API, the kernel does not go check a password again. It reads the token.
What a Token Carries
- User SID and group SIDs. The account's own SID plus the SID of every group it belongs to, including nested and well-known groups picked up during logon.
- Privileges. A list of system-wide capabilities the account has been granted, such as the right to debug other processes or change the system clock.
- Integrity level. Caps what the token can touch regardless of group membership.
- Default DACL. Gets applied automatically to new objects the process creates.
- Logon session identifier. Ties the token back to the session that produced it.
All of this gets evaluated together whenever an access check happens, which is why two accounts in the same group can still behave differently if their privilege sets or integrity levels diverge.
Primary vs Impersonation Tokens
Tokens come in two flavors that matter a great deal for how attacks and legitimate delegation both work. A primary token is attached directly to a process at creation time. An impersonation token is assigned to a single thread instead, letting that thread temporarily act using another user's security context for one specific operation.
| Token Type | Attached To | Lifetime |
|---|---|---|
| Primary token | Whole process, at creation | The full lifetime of the process |
| Impersonation token | A single thread | Duration of one operation, then reverts |
A file server thread handling a client request is a canonical example: it impersonates the connecting user just long enough to perform the file access on their behalf, then reverts.
- Anonymous: grants essentially nothing.
- Identification: lets a thread see who it's impersonating without acting as them.
- Impersonation: lets the thread act as the user, but only on the local system.
- Delegation: extends that ability across the network to other machines.
Delegation is powerful, and is exactly why unconstrained delegation is treated as a high-risk configuration in Active Directory environments. It lets a compromised service reuse a user's identity well beyond the single request that generated it.
Why This Distinction Matters
The practical reason to care about this distinction shows up constantly in incident work. A process token tells you the security context an entire process is running under, visible in tools like Process Explorer or via a wmic/PowerShell query.
An impersonation token, by contrast, is thread-scoped and short-lived. That's exactly what makes privilege escalation techniques built around stealing or duplicating impersonation tokens both effective and hard to catch in a simple process listing.
Security Identifiers
A Security Identifier, or SID, is the value Windows actually checks when it decides who you are for access-control purposes. Usernames are a convenience layer for humans; SIDs are the durable identity underneath. Every account, every group, and every security principal, including built-in ones like Everyone or Administrators, has a unique SID.
It is that SID, not the display name, that gets written into every ACL, every token, and every audit log entry.
- SID: a structured string that uniquely identifies a security principal.
- Authority: who issued the SID, such as the local machine or a domain.
- RID (Relative Identifier): the final value in a SID, unique within its issuing authority.
- Well-known SID: a SID whose value is fixed and identical across machines or domains, such as Everyone.
How a SID Is Structured
A SID encodes an authority plus a series of sub-authority values that narrow it down to a specific principal, ending in a RID that is unique within that authority. A domain user SID and a local user SID look similar in shape but are anchored to different issuing authorities.
That's exactly why the same username on two different machines, or a local account versus a domain account with the same name, are not the same security principal at all, even though a human reading a report might assume they are.
Well-Known SIDs to Memorize
Everyone (S-1-1-0) represents essentially all users, including anonymous ones in older configurations. Authenticated Users (S-1-5-11) is narrower and generally safer to grant access to, since it excludes anonymous and guest access.
The local Administrators group carries a well-known RID (544) appended to the machine or domain authority. That's how tooling and threat actors alike can recognize "this is the local admin group" across completely different machines without knowing the hostname in advance.
| Well-Known SID | Represents | Practical Note |
|---|---|---|
| S-1-1-0 | Everyone | Broadest possible grant; a red flag on sensitive object ACLs |
| S-1-5-11 | Authenticated Users | Excludes anonymous and guest sessions, generally safer than Everyone |
| S-1-5-32-544 | Local Administrators | Same RID (544) on every machine, only the prefix authority changes |
| S-1-5-18 | Local System | The highest-privileged local account, used to run most core services |
Why Names Aren't the Real Identity
Rename a user account, and its SID does not change. Every ACL entry, group membership, and permission that referenced the old name still applies correctly, because the underlying SID is untouched.
Delete an account and create a new one with the identical username, and the opposite happens: it gets a brand-new SID, and none of the old permissions carry over, even though the name in the UI looks unchanged. Analysts who reason about "the user" purely by name will misread both scenarios; SID history and resolution are what actually determine access.
ACLs and DACLs
An Access Control List, or ACL, is attached to a securable object, a file, a registry key, a service, an Active Directory object, and it is the list Windows consults to decide who can do what to that object.
DACL vs SACL
Most objects carry two kinds of ACL. The Discretionary Access Control List (DACL) actually grants or denies access. The System Access Control List (SACL) controls auditing, meaning what gets logged when the object is touched, rather than whether the touch is permitted.
When people say "ACL" in the context of permissions, they almost always mean the DACL.
| List | Controls | Answers |
|---|---|---|
| DACL | Access, allow or deny | "Can this happen?" |
| SACL | Auditing | "Should this be logged?" |
Anatomy of an ACE
A DACL is not one monolithic rule, it is an ordered collection of Access Control Entries, or ACEs. Each ACE carries three pieces of information:
- Trustee. The SID the entry applies to.
- Access mask. Which rights are being granted or denied, such as read, write, or delete.
- Type. Whether it's an allow entry or a deny entry.
When a process tries to access an object, the kernel walks the DACL's ACEs in order and evaluates them against the requesting token's SIDs until it reaches a decision. Explicit deny entries are evaluated ahead of allow entries by convention in the standard ACL-ordering rules, which is why a single explicit deny can override an otherwise-generous allow grant elsewhere in the same list.
Inheritance and Explicit Overrides
Inheritance is what lets permissions scale across an object hierarchy instead of requiring every file to be configured individually. When you set permissions on a folder and mark them to apply to child objects, every file and subfolder created underneath inherits those ACEs automatically, shown in tools like icacls or the Windows Security tab as "inherited" rather than "explicit."
This is enormously convenient for administration. It is also exactly why a single overly permissive change high in a folder tree can silently propagate write access to hundreds of files several levels down without anyone touching those files directly.
Explicit ACEs set directly on an object always take precedence over inherited ones, which gives administrators an escape hatch to lock down a specific file or subfolder even when the parent folder is more permissive. This is also a common source of access-control drift in real environments: someone adds an explicit ACE to fix an immediate problem, forgets to document why, and years later nobody can explain why one file in an otherwise-consistent folder tree has different permissions than everything around it.
Why This Matters for Defense
ACL misconfiguration is one of the most common and least glamorous privilege escalation vectors in Windows environments, precisely because it doesn't require exploiting a vulnerability. It just requires finding an object where the DACL grants more than it should have.
Tools like accesschk and PowerShell's Get-Acl exist specifically to hunt for these gaps. Reading a DACL correctly, trustee, access mask, allow or deny, inherited or explicit, is the core skill that makes those tools useful rather than noisy.
Windows Privileges
Privileges are a distinct concept from the object permissions covered in the previous section, and conflating the two is a common source of confusion. An object permission, granted through an ACE, controls access to one specific securable object, a file, a key, a service.
A privilege, by contrast, is a system-wide capability granted to an account or group through the Local Security Authority, and it applies across the entire machine regardless of any individual object's ACL. Holding a privilege can let you bypass the normal object-permission check entirely for the operations that privilege covers.
How Privileges Are Assigned
Privileges are assigned through Local Security Policy or Group Policy, visible under User Rights Assignment, and they show up in a token's privilege list at logon. Most start disabled and get enabled programmatically only when a process actually needs them.
Two Privileges Worth Knowing First
- SeDebugPrivilege. Lets a process open a handle to essentially any other process on the machine, including ones owned by other users or running as SYSTEM, and read or write its memory. Legitimate debuggers and some monitoring tools need it; so does any technique that wants to dump LSASS memory to harvest credentials.
- SeImpersonatePrivilege. Allows a thread that has already authenticated a client, through a named pipe, RPC call, or COM activation, to impersonate that client's security context for the duration of the operation, as described in the tokens section above. Service accounts commonly hold this privilege because it's exactly what's needed to serve requests on behalf of connecting users. The problem is that many of those service accounts run with far more authority than the requests they're meant to serve.
The Potato Techniques
SeImpersonatePrivilege is the conceptual root of the "Potato" family of token-impersonation escalation techniques. In broad strokes, an attacker who already has code execution as a low-privileged service account that holds SeImpersonatePrivilege can coerce a SYSTEM-level component into authenticating to a listener they control.
From there, the attacker captures the resulting SYSTEM impersonation token and uses the privilege they already legitimately held to act as SYSTEM for further actions. The privilege itself was never misconfigured, it does exactly what it's designed to do; the escalation comes from combining it with a way to trick a higher-privileged process into authenticating unexpectedly.
How UAC Actually Works
User Account Control is frequently misunderstood as a security boundary that stops malware, when its actual design goal is narrower: it exists to keep processes running with standard-user rights by default even when the logged-on account is a member of the local Administrators group. That way everyday actions can't silently make system-wide changes without a deliberate, visible step.
Microsoft has been explicit that UAC elevation is a convenience feature, not a security boundary, which matters when you're deciding how much weight to put on a UAC prompt appearing, or not appearing, during an investigation.
The Split-Token Model
The mechanism behind UAC is the split-token model. When a member of the local Administrators group logs on, Windows actually creates two tokens for that session:
- A filtered, standard-user token is attached to the initial desktop session and to every process launched from it by default, with the Administrators group SID marked deny-only and most administrative privileges stripped or disabled. Ordinary applications, Explorer, a browser, a text editor, all run under this token and simply cannot perform admin-level actions no matter what the user clicks inside them.
- A full administrator token is also created and held in reserve, carrying the complete group membership and privilege set the account is actually entitled to. It only gets attached to a process after an explicit elevation.
The Elevation Prompt
An elevation prompt is what happens when a process requests to run with the full administrator token instead. The consent.exe process renders that prompt on the secure desktop, a separate desktop session that ordinary applications cannot draw into or send synthetic input to, specifically to prevent malware from auto-clicking through the dialog or spoofing its appearance.
Approving the prompt causes a new process to be created using the full, unfiltered token rather than elevating the existing process in place. That's why an elevated window sometimes appears as a distinct new process rather than the original one simply gaining new powers.
Auto-Elevation and Bypass Techniques
Not every elevation requires a visible prompt. Windows maintains a set of binaries signed by Microsoft and manifested for auto-elevation that can request the full token silently, provided they were launched from a trusted location like System32 and meet signature requirements.
This convenience is also the doorway that UAC bypass techniques exploit as a category. Rather than attacking UAC's cryptography or process model directly, they look for ways to get attacker-controlled code to run inside, or in place of, one of those already-trusted, auto-elevating components, so the elevation happens without ever showing the user a prompt at all.
Common Privilege Escalation Patterns
The concepts covered in this chapter, tokens, SIDs, ACLs, and privileges, are not independent trivia. They are the load-bearing pieces behind nearly every local Windows privilege escalation technique you'll encounter, and seeing the common thread between them is more useful than memorizing any single technique in isolation.
Three Patterns Worth Knowing
- Misconfigured service permissions. Windows services run under a security context, frequently SYSTEM, and the service object itself has its own DACL controlling who can query, start, stop, and reconfigure it. If that DACL grants a low-privileged user or group the right to change the service's configuration, for instance the SERVICE_CHANGE_CONFIG right, that user can repoint the service's executable path to a binary of their choosing and then restart the service. Their chosen binary then runs with whatever privileged account the service was configured to use. The ACL gap is the vulnerability; the service's elevated run-as context is what makes that gap worth exploiting.
- Unquoted service path vulnerabilities. A narrower, purely mechanical version of the same idea. When a service's executable path contains spaces and is not wrapped in quotation marks, for example C:\Program Files\My App\service.exe, Windows will try each space-delimited segment in order as a potential executable before falling back to the full intended path, meaning it will attempt C:\Program.exe, then C:\Program Files\My.exe, and so on. If an attacker can write a file to one of those intermediate locations, typically because a writable folder sits earlier in the path than it should, that planted executable runs instead, again inheriting whatever privileged context the service was configured with. No ACL was even misconfigured on the service object itself; the weakness is in how an unquoted path string gets parsed.
- Token impersonation abuse. The Potato-style techniques introduced in the privileges section sit alongside these as a third distinct pattern rather than a variation on the same theme. Instead of exploiting a permissions gap or a path-parsing quirk, it exploits the combination of a legitimately held privilege, SeImpersonatePrivilege, with a way to coerce a higher-privileged process into authenticating to attacker-controlled code. The account never needed an ACL misconfiguration or a service to abuse; it already had exactly the capability it used, just not the intended trigger for using it.
The Common Thread
Windows enforces privilege boundaries through a specific set of checkpoints, object ACLs, service configuration rights, and held privileges. Each checkpoint only works correctly if it was configured with the actual threat model in mind.
A service DACL that's more permissive than intended, a path string that's missing quotes, or a privilege granted to an account without considering what it enables in combination with normal system behavior, are all failures of the same underlying kind: the gap between what an object or account is supposed to be able to do and what the OS will actually let it do once you look closely enough.
Key Takeaways
- An access token summarizes a security context: user SID, group SIDs, privileges, and integrity level. Primary tokens belong to a whole process; impersonation tokens are thread-scoped and temporary.
- SIDs, not usernames, are the persistent identity Windows checks. Renaming an account keeps its SID and its access; deleting and recreating an account with the same name does not.
- A DACL is an ordered list of ACEs, each naming a trustee, an access mask, and an allow or deny type. Inheritance propagates ACEs down an object hierarchy, and explicit ACEs override inherited ones.
- Privileges are system-wide capabilities distinct from object permissions. SeDebugPrivilege and SeImpersonatePrivilege are the two most relevant to escalation activity.
- UAC works through a split-token model: local admins get a filtered standard token by default and a full admin token only on elevation. It is a convenience feature, not a hard security boundary.
- Misconfigured service DACLs, unquoted service paths, and token impersonation abuse are three distinct escalation patterns that all exploit a gap between intended and actual OS-enforced access.
Knowledge Check
Click an answer to reveal the explanation.
An HR administrator's account is renamed from jsmith to jsmith2 after a legal name change. Their access to a shared folder, granted months earlier through an ACE on that folder, continues to work exactly as before with no changes made to the folder's permissions. Why?
A user is a member of the local Administrators group but, while working in a normal desktop session, tries to save a file directly into C:\Program Files and gets Access Denied. They are confused because they know they are an administrator. What's actually happening?
A penetration tester gains code execution as a low-privileged service account on a Windows server. Checking the account's token shows it holds SeImpersonatePrivilege but is not a local administrator and has no unusual object permissions elsewhere on the box. What does this combination most directly enable?