Level 2 · Chapter 5

Privileges (Se*)

The capabilities in a token that bypass or augment ordinary object access — and why holding one isn't the same as using it.

Security descriptors & ACLs covered access to specific objects. Privileges are a different, complementary axis: capabilities that govern sensitive, often system-wide operations that don't map cleanly onto "access to one object" at all — attaching a debugger to an arbitrary process, changing the system clock, shutting the machine down remotely, bypassing normal file-access checks for backup software.

Two different gates, living in the same token

An access token carries both group SIDs (checked against a DACL) and privileges (checked against a specific, named requirement inside a sensitive API) — Access tokens covers where these live structurally. The distinction that matters practically:

  • DACLs gate access to a specific object. "Can this token read this particular file."
  • Privileges gate an operation, often independent of any single object's ACL. SeDebugPrivilege, for instance, lets a process open a handle to essentially any other process on the system with debug access — something no reasonable per-process DACL could grant safely, which is exactly why it's modeled as a privilege rather than as an ACE.

Some well-known examples: SeBackupPrivilege and SeRestorePrivilege let backup software read and write files regardless of their DACL, precisely because a general-purpose backup tool can't practically be granted individual ACEs on every file on the system. SeShutdownPrivilege gates the ability to shut down or restart the machine. SeDebugPrivilege — probably the most operationally significant one for both administrators and attackers — gates the ability to open arbitrary processes for debugging, which in practice means reading their memory.

Held is not the same as enabled

A subtlety that surprises people who assume "administrator" means "every privilege active all the time": a privilege being present in a token doesn't mean it's currently enabled. Most privileges must be explicitly enabled on a per-thread basis (AdjustTokenPrivileges) immediately before use, and disabled again afterward — well-written privileged code follows a principle of least privilege in time, holding a powerful privilege active for the shortest possible window rather than for the process's entire lifetime.

This is also a meaningful security control in its own right: a process that never enables SeDebugPrivilege in the first place, even if its token technically carries it, doesn't benefit from it — malicious code trying to abuse a privilege still has to successfully call the enablement API first, which is itself a detectable, loggable action.

A worked example: why "run as administrator" isn't automatically full-strength

An elevated process (the full, unfiltered token from UAC, integrity levels, and the secure desktop) has more privileges available than a standard-user token — but many of them still sit disabled by default until the code explicitly enables them. A debugger attaching to another user's process has to both be running with a token that holds SeDebugPrivilege and explicitly enable it before the attach will succeed; skipping either step produces an access-denied result that has nothing to do with the target process's own DACL.

A common mistake

Assuming administrators automatically have every privilege active at all times conflates "granted" with "enabled," and it's one of the more common wrong mental models people bring into debugging a permission failure. The correct question, when a privileged operation unexpectedly fails, usually isn't "does this account have rights" — it's "does this specific token hold this specific privilege, and was it actually enabled on this thread before the call."

Where this connects