The Integrity-Level Stack: MIC, UIPI, and Twenty Years of UAC's Quiet Plumbing
What UAC actually is beneath the consent prompt: Mandatory Integrity Control, UIPI, the split-token model, and twenty years of bypass research as proof.
Permalink1. Two whoami Outputs, Sixty Seconds Apart
Open an unelevated PowerShell on a Windows 11 administrator account. Run whoami /groups /priv. Click "Yes" on the yellow prompt. Open an elevated PowerShell on the same account. Run the same command. The two outputs are different lists of SIDs. Sixty seconds have passed. The consent prompt did not move a single bit of OS state on its own. The operating system did, because of a stack of primitives that ship with every Windows install and that almost no Windows user has ever heard the names of. This article is a tour of that stack, and of what twenty years of bypass research has taught us about it.
Place the two outputs side by side. The user is the same. The session is the same. The clock has barely moved. Read them carefully.
PS C:\Users\admin> whoami /groups /priv | findstr /i "Mandatory Administrators SeDebug"
BUILTIN\Administrators Group used for deny only
Mandatory Label\Medium Mandatory Level Label
(SeDebugPrivilege not present)
PS C:\Users\admin> whoami /groups /priv | findstr /i "Mandatory Administrators SeDebug"
BUILTIN\Administrators Enabled by default, Enabled group, Group owner
Mandatory Label\High Mandatory Level Label
SeDebugPrivilege Disabled
Four facts fall out of those two outputs, and each one of them is a foothold for the rest of this article.
The first fact is that the administrator group SID is present in both tokens. It is not added by the elevation. In the filtered token it carries the flag SE_GROUP_USE_FOR_DENY_ONLY, which means the access-check algorithm consults it only when matching a deny ACE and otherwise pretends it is absent [1]. In the elevated token, the same SID is fully enabled. The dialog did not add a SID; it changed which token Windows uses.
The second fact is the integrity level. In the filtered token, the mandatory label reads Mandatory Label\Medium Mandatory Level. In the elevated token, the same label reads Mandatory Label\High Mandatory Level. That label corresponds to a well-known SID under the S-1-16-X family (S-1-16-8192 for Medium and S-1-16-12288 for High) [2]. The integrity level is not a regular group SID. It is a separate field on the token, and as we will see in §4, it drives a separate access-check evaluator that runs before the discretionary access check [3].
The third fact is the privilege set. The filtered token holds a small set of user-mode privileges (SeChangeNotifyPrivilege, SeShutdownPrivilege, a handful of others). The elevated token holds the full administrator privilege set, including the named ones the security press writes about: SeDebugPrivilege, SeTakeOwnershipPrivilege, SeLoadDriverPrivilege, SeBackupPrivilege, SeAssignPrimaryTokenPrivilege, and twenty or so others, depending on the Windows build [4].
The fourth fact is the most subtle, and the one this whole article exists to make rigorous. The yellow dialog did not create the elevated token. The OS created it at logon, almost half an hour before the prompt ever rendered, and held it dormant in the LSA. The prompt asked the user a single question: may I, the operating system, use the token I already have? It did not ask: may I, the operating system, mint a more privileged token now? That distinction is the difference between how every Windows user talks about UAC and how UAC actually works.
The four primitives we are about to tour are the substrate beneath everything in those two whoami outputs. Mandatory Integrity Control (MIC) is the access-check evaluator that decided your Medium-IL PowerShell could not write into %SystemRoot%\System32 before any DACL was consulted. User Interface Privilege Isolation (UIPI) is the windowing-layer analog that prevented your Medium-IL Edge tab from injecting WM_SETTEXT into the High-IL elevated PowerShell next to it. The split-token model is the LSA policy that decided your interactive shell should hold the Medium-IL token instead of the High-IL one. The Application Information service (Appinfo) is the SYSTEM-trusted broker that mediated the token swap when you clicked "Yes."
This article walks every one of those layers, then ends at the empirical proof: twenty years of "UAC bypasses," and Microsoft's own quiet acknowledgement, from week one, that the dialog was never the security boundary [5]. Why did Microsoft build this stack in the first place? What was wrong with how Windows XP did it?
2. The XP Problem and the Vista Bet
On the overwhelming majority of consumer Windows XP installs in 2003, every process the user launched ran as Administrator, because the first interactive account XP provisioned at setup was an Administrator and the typical user never created a separate Limited User account [6]. Every browser tab. Every embedded Word macro. Every drive-by download. The operational vulnerability surface was the entire OS, because authority on Windows is carried in the access token, and the access token of those XP-era user processes held the full administrator SID set.
Sysinternals co-founder Mark Russinovich, then a Microsoft engineer following the 2006 Winternals acquisition, framed the problem precisely in the June 2007 issue of TechNet Magazine: "Most users of Windows XP run with full administrative rights all the time, allowing all software they run, including malware, to have unrestricted access to the system" [4]. The sentence reads like a confession, and it was. The OS shipped with a sound access-control model and an operational policy that defeated it from the first reboot.
Two distinct threat models drove the architectural response Vista shipped four years later.
Threat model one: the runaway admin
The first threat model was the runaway admin. Default-admin consumer installs meant malware silently inherited admin authority because the user was the admin. A drive-by exploit in Internet Explorer ran as the user, the user was an admin, and the malware was an admin. There was no point in the OS where a least-privilege boundary could intervene, because the token never carried a least-privilege bound to begin with. The DACLs were correct; the policy that filled the tokens was the failure.
Threat model two: the shatter-attack class
The second threat model was the shatter-attack class. In August 2002, security researcher Chris Paget published a paper titled "Exploiting design flaws in the Win32 API for privilege escalation" on the Bugtraq mailing list, immediately mirrored on Help Net Security [7]. The paper coined the term shatter attack and demonstrated that on Windows NT, 2000, and XP, any process running on a user's interactive desktop could send a WM_TIMER message carrying a callback function pointer to any other process's message loop on the same desktop. The receiving process would invoke the callback in its own address space, at its own privilege level [8].
This was an architectural defect. The receiving process did not authenticate the message origin. It could not, because the Win32 messaging system was designed in the late 1980s under the assumption that all windows on a desktop belonged to one trust principal. By 2002, that assumption had been false for a decade: services ran on the user's interactive desktop with LocalSystem authority, and the user's browser could send them messages.
Microsoft's December 2002 patch (security bulletin MS02-071) fixed individual services that exposed the most exploitable callbacks. It did not fix the architectural class, because the class was a property of the Win32 messaging design, not of any one service [8].
The Vista bet, stated as four design decisions
Between 2005 and 2006, Microsoft made four decisions about how Vista would respond. The first was to split the administrator's authority by default: an admin user would not hold a single admin token at logon, but a filtered token plus a dormant linked one. The second was to mediate the recombination through an OS-controlled UI surface, so the user could see and consent to the moment authority crossed an integrity boundary. The third was to add a second access-check axis (integrity) that the DACL could not override. The fourth was to add a windowing-layer analog to close the cross-IL variant of the shatter-attack class.
All four shipped together. Vista RTM'd on November 8, 2006 to OEMs and businesses, and Microsoft launched it to consumers on January 30, 2007 [9]. The press release called it "the most significant product launch in Microsoft Corp.'s history."
The architectural canon was published five months later, in the June 2007 issue of TechNet Magazine under the title Security: Inside Windows Vista User Account Control [4]. The author was Russinovich, and the article became the single most-cited primary on UAC in the Windows-security literature. Five months earlier, however, in a TechNet Blogs post about PsExec, the same author had quietly written something the entire later debate would rest on, and almost no one read it for what it actually said [5]. We will return to that post in §7. First, the harder question: why couldn't NT's existing access-control model handle any of this on its own?
3. Why the DACL and the Privilege Were Not Enough
Windows NT had the access-control model from day one. It had Security Identifiers (SIDs), access tokens, discretionary access control lists (DACLs), privileges, and an access-check algorithm with a name (SeAccessCheck) that the kernel exposed and documented [10][11]. The model was correct in theory and broken in practice. To see why, watch what happens when an XP administrator opens a malicious Word document.
The user double-clicks the document. Word starts. Word loads the document's embedded macro. The macro calls URLDownloadToFile and writes evil.exe into %TEMP%. Then it calls CreateProcess on evil.exe. The new process inherits its parent's primary access token, which is the user's interactive token, which carries the administrator group SID, enabled, with the full administrator privilege set. The DACL on HKLM\SYSTEM\CurrentControlSet\Services grants Full Control to BUILTIN\Administrators. The malware writes a new service entry. The malware now persists across reboots, all without a single elevation prompt, because there was no elevation transition to prompt at. The user was already the administrator [4].
The first problem is in the D of DACL. Discretionary access control lists are discretionary by definition: the owning principal of an object decides who has access [12]. An attacker running as the user can rewrite any DACL the user owns. That is not a bug; it is the meaning of the word discretionary. Mandatory access-control models (Bell-LaPadula 1973 [13], Biba 1977 [14]) exist precisely because discretionary models cannot defend against principals running with the owner's authority.
The second problem is in the privilege model. A Windows access token carries a list of named privileges such as SeDebugPrivilege, SeTakeOwnershipPrivilege, SeLoadDriverPrivilege. Each privilege is a per-token authorisation to bypass some specific DACL check. An admin token holds them all. There is no way in the NT 4.0 / 2000 / XP design to say "this Word process holds the admin's identity but should not be trusted to use SeDebugPrivilege." Privileges are granted to tokens at logon, and the only way to remove them from a downstream process is to construct a restricted token explicitly, by hand, with CreateRestrictedToken [15].
Generation 1: the seven-year failure to make least-privilege voluntary
Between 1999 and 2006, Microsoft and the Windows security community tried five different ways to make least privilege voluntary. None of them worked at consumer scale.
CreateRestrictedToken is a Win32 API, documented since Windows XP and Server 2003, that produces a copy of an existing access token with selected SIDs marked deny-only, selected privileges removed, and an optional list of restricting SIDs added [15]. It is the kernel primitive every later sandbox (Chromium's renderer sandbox, AppContainer, Office Protected View) is built on. It was a primitive, not a policy. A consumer install with default-admin logons could not use it without an opt-in from every application vendor.
runas.exe, shipped in Windows 2000, let a user explicitly launch a process under a different identity. The user was supposed to log in as a standard user and runas an administrator account when needed. In practice, the user logged in as the administrator and forgot the standard account existed.
Software Restriction Policies (SRP), shipped with Windows XP, let a domain admin define hash, path, certificate, or zone rules that the OS enforced at process creation [16]. SRP was a policy mechanism on top of the SAFER substrate [17]. It worked when configured. On consumer Windows it was off by default; on enterprise Windows it was configured by the few who knew it existed.
Aaron Margosis, then a Microsoft consultant, ran a years-long blog campaign called "Non-Admin" arguing that ordinary users should log in as standard users and only elevate when necessary. His tooling included LUA Buglight (which diagnosed which OS calls a misbehaving application made that required admin privilege), MakeMeAdmin (a runas shim), and PrivBar (a status-bar widget that displayed the IL of the current process) [6]. The blog became required reading inside Microsoft and the Windows-admin community.
The lesson Microsoft took from the 1999-2006 experience was that voluntary least privilege does not scale. You cannot solve the runaway-admin problem with policy and exhortation. You need an architectural primitive that runs by default, bounds authority by integrity rather than by identity, and absorbs the legacy of applications written for unrestricted admin without breaking them. All four primitives of the Vista bet shipped together in November 2006 [9].
What does an integrity primitive look like, and how is it different from "another ACE"?
4. The Twin Primitives: MIC and UIPI
4.1 Mandatory Integrity Control
An access-check evaluator that compares the integrity level of a subject token to the integrity level of a target object before consulting the object's DACL. MIC denials short-circuit the access check; a Low-IL principal cannot write to a Medium-IL object regardless of what the DACL says.
The load-bearing fact about MIC is in a single sentence on the Microsoft Learn reference page, and the entire architectural difference between MIC and "just another ACE" lives in that sentence. MIC "evaluates access before access checks against an object's discretionary access control list (DACL) are evaluated" [3].
Pause on that ordering. Before the DACL. Not together with it. Not after it. The integrity-level check is a separate evaluator that runs first, and its denial is final. If the IL check denies access, the DACL is never consulted, no matter what the DACL says. That is what the word mandatory in Mandatory Integrity Control means.
A well-known SID, carried on every Windows access token and every securable object, that orders subjects and objects on a seven-level integrity lattice (Untrusted, Low, Medium, Medium Plus, High, System, Protected Process).
The seven well-known integrity-level SIDs are defined in the Well-known SIDs reference page on Microsoft Learn [2].
| Integrity level | RID (S-1-16-X) | Typical use |
|---|---|---|
| Untrusted | S-1-16-0 | Most-restricted sandboxes; rare on consumer Windows |
| Low | S-1-16-4096 | IE Protected Mode, AppContainer, Edge / Chrome renderers |
| Medium | S-1-16-8192 | Default for interactive user processes |
| Medium Plus | S-1-16-8448 | UI-Access processes (uiAccess=true manifest, Windows 7+) |
| High | S-1-16-12288 | Elevated administrative processes |
| System | S-1-16-16384 | Kernel-mode and LocalSystem services |
| Protected Process | S-1-16-20480 | PPL-protected processes (LSASS with RunAsPPL, antimalware) |
The IL lives on a token in the TokenIntegrityLevel field, retrievable through GetTokenInformation and the TOKEN_MANDATORY_LABEL structure [3]. The IL lives on an object in the system access control list (SACL) as a SYSTEM_MANDATORY_LABEL_ACE, a special ACE type that carries the object's IL SID and a mandatory-policy mask [18]. Three policy bits are defined in the winnt.h header [18].
SYSTEM_MANDATORY_LABEL_NO_WRITE_UP(0x1) -- default. A subject at lower IL cannot write to this object.SYSTEM_MANDATORY_LABEL_NO_READ_UP(0x2) -- opt-in. A subject at lower IL cannot read this object.SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP(0x4) -- opt-in. A subject at lower IL cannot execute this object.
The "regardless of DACL" property is the part to read slowly. A Low-IL principal cannot write to a Medium-IL object "even if that object's DACL allows write access to the principal," because the IL check runs first and short-circuits the access decision before the DACL evaluator ever sees the request [3]. This is the difference between adding "another ACE" for integrity and adding a separate evaluator that runs first. An integrity ACE in the DACL would have been overridable by the object owner, because DACLs are discretionary. A mandatory-label ACE in the SACL is enforced by SeAccessCheck itself, independently of any other ACE in the DACL.
Diagram source
flowchart TD
A["Subject requests access
(SID set, IL, desired access)"] --> B["MIC evaluator
compares subject IL to object IL
against NO_WRITE_UP / NO_READ_UP policy"]
B --> C{"IL check allows
requested access?"}
C -- "No" --> D["ACCESS_DENIED
(DACL not consulted)"]
C -- "Yes" --> E["DACL evaluator
walks ACEs in order
(deny first, then allow)"]
E --> F{"DACL grants
requested access?"}
F -- "Yes" --> G["ACCESS_GRANTED"]
F -- "No" --> H["ACCESS_DENIED"] The architectural payoff is in the pseudocode of the access-check decision itself. Strip the API noise away and the decision reduces to two evaluators in series. The conceptual ordering is exact.
// Pseudocode of the Windows access-check ordering (Vista+).
// See Microsoft Learn: Mandatory Integrity Control.
function seAccessCheck(subjectToken, object, desiredAccess) {
// Step 1: Mandatory Integrity Control. Runs before the DACL.
const subjectIL = subjectToken.integrityLevel; // e.g. Medium = 0x2000
const objectIL = object.mandatoryLabel.integrityLevel; // e.g. High = 0x3000
const policy = object.mandatoryLabel.policy; // bitmask
if (subjectIL < objectIL) {
if ((policy & NO_WRITE_UP) && (desiredAccess & WRITE_BITS)) return 'ACCESS_DENIED';
if ((policy & NO_READ_UP) && (desiredAccess & READ_BITS)) return 'ACCESS_DENIED';
if ((policy & NO_EXECUTE_UP) && (desiredAccess & EXECUTE_BITS)) return 'ACCESS_DENIED';
}
// Step 2: only if MIC allowed do we consult the DACL.
for (const ace of object.dacl.aces) {
if (ace.sid in subjectToken.sids) {
if (ace.type === 'DENY' && (ace.mask & desiredAccess)) return 'ACCESS_DENIED';
if (ace.type === 'ALLOW') desiredAccess &= ~ace.mask;
if (desiredAccess === 0) return 'ACCESS_GRANTED';
}
}
return 'ACCESS_DENIED'; // implicit deny if no ACE grants
} The naive reading of MIC is "they added another ACE for integrity." The correct reading is that they added a separate axis with its own evaluator that the DACL cannot override. The reader who internalises that ordering can re-derive almost every subsequent design decision Vista made about UAC, AppContainer, IE Protected Mode, and Administrator Protection. A MIC denial is final. The DACL is not consulted. That is what mandatory means.
MIC adds a second axis to the access check. The first axis is identity (DACL plus token SIDs); the second is integrity (IL). The two axes are evaluated in order: integrity first, identity second. A failure on the integrity axis short-circuits the entire check, regardless of what the identity axis would have said.
MIC bounds file, registry, and most other securable-object writes across IL boundaries. But the XP-era shatter attacks Paget published in 2002 were not about file writes. They were about cross-desktop message injection in the Win32 windowing layer, and MIC cannot help with that, because window messages do not pass through SeAccessCheck. So Vista shipped a second primitive specifically for the windowing layer.
4.2 User Interface Privilege Isolation
The windowing-layer analog of MIC. UIPI blocks a defined subset of window messages and hook APIs sent from a lower-IL process to a window owned by a higher-IL process on the same desktop, terminating the cross-IL variant of the shatter-attack class.
If MIC is mandatory integrity for objects, UIPI is mandatory integrity for windows. Same idea, different layer of the OS. Same principle: a separate evaluator that runs in the window manager and blocks cross-IL operations regardless of the window's own configuration [20].
The canonical failed-shatter scenario is short and exact. A Medium-IL malware process calls SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)"some-attacker-controlled-string") against a window handle (hwnd) belonging to a High-IL elevated PowerShell on the same desktop. On Windows XP, the message would arrive and the elevated PowerShell's edit control would update its text, with no authentication check anywhere in the path. On Vista and every subsequent Windows release, the call returns zero. GetLastError returns ERROR_ACCESS_DENIED. The message is silently dropped by win32k.sys before the receiving process's window procedure ever sees it. The window manager noticed that the sender's IL was lower than the receiver's IL and dropped the message [20][5].
The "silently dropped" part matters operationally. Legacy applications written before Vista did not check the return value of SendMessage. When Vista shipped UIPI, those applications kept "working" in the sense that they did not crash. They just stopped being effective at any cross-IL interaction they may have previously relied on. This is the same compatibility shape Microsoft used everywhere in Vista: the new bound was real, but the API surface returned plausible failure codes rather than raising new errors that broke legacy callers.
What UIPI blocks, precisely
UIPI does not block every window message. It blocks a specific dangerous subset, and a complete reading of the article requires reading the list slowly.
| Operation | UIPI behaviour from lower IL to higher IL |
|---|---|
SendMessage / PostMessage for WM_SETTEXT, edit-control mutators, combo-box mutators | Blocked; returns 0 / ERROR_ACCESS_DENIED |
Posted messages above WM_USER (0x0400) | Blocked |
WM_TIMER with a callback function pointer | Blocked (the original Paget vector) |
SetWindowsHookEx against a higher-IL thread or process | Blocked |
AttachThreadInput to a higher-IL thread | Blocked |
SendInput targeting a higher-IL window | Blocked |
| Journal record / journal playback hooks | Blocked |
| Mouse and most keyboard input from the OS itself | Allowed (the user is the principal) |
Most paint messages (WM_PAINT, WM_ERASEBKGND) | Allowed |
Read-only window queries (GetWindowText, EnumWindows) | Allowed (return empty / minimal data rather than failing) |
"UIPI blocks all WM_* messages" is one of the most common misconceptions in Windows-security literature. It does not. It blocks the dangerous subset: the messages and hooks that allow a sender to alter the receiving process's state or execute code in it [5][20].
Diagram source
sequenceDiagram
participant M as Medium-IL malware
participant W as win32k.sys
participant P as High-IL PowerShell
M->>W: SendMessage(hwnd, WM_SETTEXT, ...)
W->>W: Compare sender IL (Medium) to target window IL (High)
Note over W: Sender IL lower than target IL, WM_SETTEXT in dangerous subset
W-->>M: Returns 0, ERROR_ACCESS_DENIED
Note over P: Window procedure never invoked, text unchanged ChangeWindowMessageFilterEx function reference [21]. It is the closest thing to a first-party UIPI conceptual page on Microsoft Learn. There is no standalone Microsoft Learn page titled "User Interface Privilege Isolation" at the winmsg path: the Wikipedia UIPI article is the standard secondary anchor for the concept itself [20], and Russinovich's February 2007 TechNet Blogs post introduces UIPI by name in the original architectural canon [5].
The opt-in exemption: ChangeWindowMessageFilterEx
The UIPI block is per-window and per-message. When a higher-IL window has a legitimate reason to accept a specific message from lower-IL senders (for example, a developer tool that needs to receive WM_COPYDATA from a Medium-IL client), the higher-IL process can call ChangeWindowMessageFilterEx to add the specific message to its window's allow-list [21].
The action constants are documented as MSGFLT_ALLOW (add the message to the allow-list), MSGFLT_RESET (remove explicit policy and inherit defaults), and MSGFLT_DISALLOW (explicitly block the message even if defaults would allow it) [21]. The function returns BOOL; failure is non-fatal and the caller is expected to validate the result.
WM_SETTEXT into the cross-IL allowed list inherits the responsibility to validate the contents of every message it then receives. The filter is the gate. It is not the validator. A higher-IL process that takes attacker-controlled text and pastes it into a system shell has bypassed UIPI in the same way a service that takes attacker-controlled input and passes it to system() has bypassed least privilege. The mechanism cannot make the higher-IL process safe; it can only make the higher-IL process aware.
The uiAccess=true carve-out
The single largest residual exemption from UIPI is the uiAccess=true manifest flag, designed to support accessibility software (screen readers, on-screen keyboards, remote-control tools) that needs to interact with windows above its own IL [22]. A process that asserts uiAccess=true in its application manifest gets, at process creation, a token flag (TokenUIAccess) that exempts the process from UIPI's cross-IL blocks for the outbound direction. A Medium-IL UI-Access process can post WM_SETTEXT to a High-IL elevated PowerShell window, because the Medium-IL process is acting on behalf of an accessibility client.
The gating conditions for uiAccess=true are tight, by design. Microsoft Learn enumerates three [22]. The manifest must assert uiAccess="true" in the requestedExecutionLevel element. The binary must carry a valid Authenticode signature. The binary must reside in a directory writable only by administrators, which in practice means %SystemRoot%\System32, %ProgramFiles%, or a similarly admin-only path. The three conditions together are intended to bound uiAccess to vetted, signed, install-time-protected binaries.
We will return to the uiAccess carve-out in §9, because Forshaw's February 2026 Project Zero retrospective documents that five of nine pre-GA Administrator Protection bypasses operated entirely through this surface [23]. The Vista-era exemption inherited unchanged into 2026 is, nearly twenty years later, the single largest residual cross-IL attack class in the Windows integrity stack.
What UIPI killed, precisely
UIPI killed the cross-IL variant of the Brett-Moore-2002 shatter-attack class. Same-IL shatter attacks (two Medium-IL processes on the user's Default desktop, both belonging to the same user, both running with the user's authority) are not blocked by UIPI, because UIPI is an IL-based filter. Two same-IL processes can still send each other arbitrary window messages, and this is exactly why every modern browser sandbox layers AppContainer and a restricted-token sandbox on top of MIC [24]: the integrity primitives are correct, but they are integrity primitives, not identity primitives, and same-IL same-desktop processes need a different isolation mechanism.
Together, MIC and UIPI provide an integrity bound on access (objects) and on user-interface manipulation (windows). Both are mandatory, default-on, and constant-overhead. They are the load-bearing primitive pair of the entire integrity-level stack. But how does the OS decide which processes get which IL? When you log in as Administrator and open a PowerShell, why is that PowerShell Medium and not High?
5. The Split-Token Breakthrough
The integrity-level pair (MIC plus UIPI) is the access-control primitive. The split-token model is the policy decision that wires those primitives into the administrator's everyday experience. Without the split-token policy, an administrator's interactive shell would hold a High-IL token at logon and UAC would never need to exist. With it, every administrator on Windows 11 today has two tokens. One is in use. The other is dormant. The yellow dialog is the negotiation that toggles between them.
The Vista policy in which an Administrators-group user logging on receives a Medium-IL filtered token plus a dormant High-IL linked token. The filtered token becomes the primary token of the interactive shell; the linked token is used only after consent or auto-elevation, and only when the Application Information service brokers a process creation with it.
What the LSA does at logon
When EnableLUA=1 in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System (the default since Vista), and an Administrators-group user logs on, the Local Security Authority subsystem (LSASS) constructs three things during logon processing [1].
The first is the full token: an access token that contains all of the user's administrator group SIDs (enabled, not deny-only), all of the privileges the user is authorised to hold, and an integrity level of High. This is the token that, on XP, would have been the user's primary token from logon onward.
The second is the filtered token: a copy of the full token with all administrator-equivalent group SIDs marked SE_GROUP_USE_FOR_DENY_ONLY, all privileges except a small user-mode subset removed, and the integrity level reduced to Medium. The administrator group SIDs are not removed; they are marked deny-only so they still match deny ACEs but do not satisfy allow ACEs. The privileges are not zeroed; the powerful ones (SeDebug, SeTakeOwnership, SeLoadDriver, SeAssignPrimaryToken, and others) are dropped from the filtered token entirely.
The third is the linked relationship: the LSA stamps each token with a reference to the other via the TokenLinkedToken information class, so that a holder of the filtered token can, with the right privileges, retrieve a handle to the dormant full token by calling NtQueryInformationToken(filteredToken, TokenLinkedToken, &linkedToken, ...) [1].
The filtered token then becomes the primary access token of the user's interactive shell (explorer.exe). Every process the user launches by clicking, by Win+R, by typing in a console, inherits the filtered token as its primary token. The dormant full token sits in the LSA, addressable through TokenLinkedToken. The verbatim Microsoft Learn statement is exact: "When an administrator logs on, two separate access tokens are created for the user: a standard user access token and an administrator access token" [1].
Diagram source
flowchart TD
A["User authenticates
as a member of Administrators"] --> B["LSA logon processing
(LsaLogonUser)"]
B --> C["Full token
(admin SIDs enabled, all privileges, IL = High)"]
B --> D["Filtered token
(admin SIDs deny-only, privileges stripped, IL = Medium)"]
C -.->|"linked via TokenLinkedToken"| D
D --> E["Primary token of explorer.exe
and all interactive child processes"]
C --> F["Dormant in LSA, used only by Appinfo after consent or auto-elevation"] The TokenElevationType API surface
Three values of the TOKEN_ELEVATION_TYPE enumeration describe what state the current process is in [25].
TokenElevationTypeDefault(1) -- no split-token policy is in effect for this token. This is the legacy case (EnableLUA=0) or the case where the user is not a member of any administrators-equivalent group at all. The single token is the only token, and no linked token exists. On a default consumer or enterprise Windows 11 install with an admin account, this value is rare.TokenElevationTypeFull(2) -- the current process is running with the unfiltered admin token. Admin Approval Mode is in force; this process either was launched via elevation (and holds the linked full token) or was created in a context where the filtered/full distinction is collapsed (some service contexts).TokenElevationTypeLimited(3) -- the current process is running with the filtered token, Admin Approval Mode is in force, and a dormant full token exists. This is the typical state of an interactive admin shell on Windows 11.
TokenElevationTypeDefault (value 1) is the legacy or domain-controller case in which EnableLUA=0 and the user has no filtered token at all. On a default consumer Windows install, administrators are always TokenElevationTypeLimited or TokenElevationTypeFull, never Default. The Default case is what reverting EnableLUA to 0 produces, and it is the configuration the FAQ in §11 warns against.
What the consent prompt actually does
The behaviour of the consent prompt now resolves to a single operation, and the operation is not "elevate." When the user invokes "Run as administrator" on a binary, the shell calls ShellExecuteEx with the "runas" verb [26]. The Application Information service (the topic of §6.2) receives the request via RPC. Appinfo, running as LocalSystem, retrieves the linked full token of the calling user via TokenLinkedToken. Appinfo shows the consent prompt on the Secure Desktop (§6.1). If the user clicks "Yes," Appinfo creates a new process using the full token as the new process's primary token, by calling CreateProcessAsUser with the privileges Appinfo holds because it is LocalSystem [4].
The bits that move are the kernel-level handle for the new process and the assignment of the linked token as that process's primary token. The bits the prompt itself moves are zero. The prompt is the consent surface; the token swap is the primitive.
The consent prompt does not create authority. It uses authority that was already constructed at logon and held dormant in the linked token. The same primitive can move bits without the prompt -- that is exactly what auto-elevation does.
Diagram source
sequenceDiagram
participant U as User
participant E as explorer.exe (Medium IL)
participant A as Appinfo (LocalSystem)
participant C as consent.exe (Secure Desktop)
participant P as New process (High IL)
U->>E: Right-click, Run as administrator
E->>A: RPC: request elevation of target.exe
A->>A: Look up TokenLinkedToken of caller's filtered token
A->>C: Show consent prompt
U->>C: Click Yes
C-->>A: Consent granted
A->>P: CreateProcessAsUser(linked full token, target.exe)
Note over P: New process runs at High IL, full admin SIDs enabled, full privileges "Split-token administrator in UAC just means MS get to annoy you with prompts unnecessarily but serves very little, if not zero security benefit." -- James Forshaw, Reading Your Way Around UAC (Part 1), Tyranid's Lair, May 2017
Forshaw's 2017 critique is the load-bearing observation that frames the rest of the article [19]. Even with the elegant split-token policy in place, there is a structural problem the design did not solve. The filtered token and the linked token share the same user SID. They write to the same %USERPROFILE%. They consult the same HKCU registry hive. They live in the same logon-session LUID. From an integrity-isolation point of view, the two tokens are bounded against each other; from an identity-isolation point of view, they are the same user.
6. The Full UAC Stack on a Modern Windows Box
The reader now knows the four load-bearing primitives. This section walks every supporting piece that surrounds them on a 2026 Windows install, in the order needed to follow an elevation event end-to-end. There are four pieces: the Secure Desktop the prompt renders on, the Appinfo service that brokers the token swap, the two distinct activation surfaces that trigger an elevation, and the auto-elevation allowlist that shaped fifteen years of bypass research.
6.1 The Secure Desktop, not Session 0
A separate desktop object at the Object-Manager path \Sessions\<n>\Windows\WindowStations\WinSta0\Winlogon, within the user's interactive session, on which consent.exe runs the UAC prompt. Isolated from the user's Default desktop by Object-Manager DACL and the SwitchDesktop API.
When you click "Run as administrator" and the screen dims and the prompt appears, the screen dims because you have just been switched to a different desktop. Not a different session, not Session 0, not a different window station. A different desktop within the same window station, accessed through the SwitchDesktop API [4].
The Object-Manager path is exact. Inside the user's interactive session (Session 1 if the user is the first interactive logon, higher numbers for subsequent users), there is a window station named WinSta0. Inside WinSta0 there are several desktop objects: Default (where the user's normal interactive processes paint), Winlogon (where consent.exe runs the prompt), and Disconnect and Screen-saver for related uses. The full path of the Secure Desktop is \Sessions\<n>\Windows\WindowStations\WinSta0\Winlogon.
The Winlogon desktop is protected by an Object-Manager DACL that the user's normal interactive processes (running on Default) cannot open for DESKTOP_CREATEWINDOW or DESKTOP_HOOKCONTROL. A Medium-IL malware process on Default cannot draw into the Winlogon desktop, cannot enumerate its windows, and cannot send messages to them. The OS performs the desktop switch in win32k.sys and renders consent.exe's window on the new desktop with a snapshot of the previous desktop as a dimmed background, so the user has visual continuity but consent.exe is the only process accepting input [4].
A separate Vista feature, architecturally independent of the Secure Desktop, that moved all Windows services off the interactive desktop and into Session 0. The two features ship together in Vista and are constantly confused, but they live at different layers of the Object Manager hierarchy.
Diagram source
flowchart TD
A["Session 1
(interactive logon for user 'admin')"] --> B["WinSta0
interactive window station"]
A --> C["Service-0x0-3e7$
non-interactive WinSta (services)"]
B --> D["Default desktop
explorer.exe, browsers, console windows
(Medium IL processes)"]
B --> E["Winlogon desktop
consent.exe renders here
(Secure Desktop)"]
B --> F["Disconnect / Screen-saver
desktops"]
D -. "blocked by Object-Manager DACL
and SwitchDesktop" .-> E The Secure Desktop addresses UI spoofing and input injection against the prompt itself. It does not address whether elevation can happen without a Secure Desktop prompt; that is the territory of the auto-elevation allowlist (§6.4) and of the bypass-research class (§7).
6.2 The Application Information service (Appinfo)
The SYSTEM-trusted Windows service (appinfo.dll, hosted in svchost.exe, runs under LocalSystem) that mediates the token swap between filtered and linked tokens at elevation time. Required service: "Run as administrator" fails without it. The modern process-creation entry point is RAiLaunchAdminProcess.
Every UAC elevation on Windows goes through one service: Appinfo (display name "Application Information"). Its image is C:\Windows\System32\appinfo.dll, loaded into a shared svchost.exe host process, running as LocalSystem [4].
The job is single-purpose: be the SYSTEM-trusted broker that performs the token swap. A Medium-IL caller cannot, by definition, create a process holding a token the caller does not possess. Creating a process under a token with privileges the caller lacks requires two privileges Medium-IL filtered admin tokens do not hold: SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege. LocalSystem has both [4]. The broker therefore has to run as LocalSystem, and that is what Appinfo is for.
The modern entry point on Appinfo's RPC interface is RAiLaunchAdminProcess, documented verbatim in Forshaw's February 2026 Project Zero post on Administrator Protection [23]. The Medium-IL caller invokes ShellExecuteEx with "runas"; the shell marshalls the request across to Appinfo; Appinfo retrieves the caller's TokenLinkedToken; if a prompt is needed, Appinfo shows consent.exe on the Secure Desktop; if the user clicks "Yes," Appinfo calls RAiLaunchAdminProcess to create the new process under the linked full token.
Disable Appinfo and "Run as administrator" returns an error. It is the single point of trust in the elevation pipeline, which is exactly why the bypass-research industry pays attention to it: anything that can trick Appinfo into auto-elevating an attacker-influenced binary, without the consent prompt, becomes a fileless UAC bypass (§7.1).
6.3 Two activation surfaces
The first activation surface is ShellExecuteEx with the "runas" verb. The OS launches consent.exe, asks the user, and if approved, Appinfo creates a brand-new process under the caller's linked full token. The new process is High-IL for its entire lifetime, with the entire administrator privilege set and all the admin group SIDs enabled. The Windows Explorer "Run as administrator" context menu uses this verb. So does the runas /trustlevel: command. So does any program that calls ShellExecuteEx and sets the lpVerb member of SHELLEXECUTEINFO to the string "runas" [26].
A COM activation surface (Elevation:Administrator!new:{CLSID}) that asks the OS to instantiate a single COM out-of-process server in a new elevated dllhost.exe, exposing only that one CLSID's methods at High IL. Per-object elevation, distinct from ShellExecuteEx "runas" whole-process elevation.
The second activation surface is the COM Elevation Moniker. A Medium-IL caller invokes CoGetObject (or CoCreateInstance via a moniker) with the display name "Elevation:Administrator!new:{CLSID}" (or "Elevation:Highest!new:{CLSID}"). This asks the OS to instantiate a single COM out-of-process server in a new elevated dllhost.exe host process, exposing only that one CLSID's methods at High IL. The caller stays at Medium. Only the COM object's host process is elevated, and only for the lifetime of the object [27].
The semantics are deliberately narrow. The COM Elevation Moniker requires the target CLSID to opt in via two registry values under HKCR\CLSID\{CLSID}: Elevation\Enabled = 1 and an LocalizedString value that names the elevation prompt's display string. Not every COM class is moniker-eligible; the registry enables elevation per CLSID.
| Property | ShellExecuteEx "runas" | COM Elevation Moniker |
|---|---|---|
| Granularity | Whole process | One COM object |
| Lifetime | Entire process lifetime | Object lifetime only |
| Caller IL after | Caller stays Medium; new process High | Caller stays Medium |
| New process | Target executable | dllhost.exe host |
| Authority surface | All admin SIDs and privileges, broad | Methods of one CLSID, narrow |
| Typical use | "Run as administrator" context menu, MSI installers | Programmatic file copy, Wmi management, registry edits |
| Primary canonical bypass class | DLL-search-order against the new process | Auto-elevated COM behaviour abuse |
The distinction matters because most of the canonical UAC bypasses do not touch ShellExecuteEx "runas" at all. Leo Davidson's December 2009 essay attacked the COM Elevation Moniker by invoking the IFileOperation COM class (auto-elevation-eligible, registered under the right CLSID) from a Medium-IL caller, and using its CopyItem method to overwrite a system file at High IL [28][29]. The ICMLuaUtil and IColorDataProxy interfaces follow the same shape: a Medium-IL caller instantiates an auto-elevatable COM class via the moniker, and then calls a method on the High-IL object that performs an attacker-chosen action [30].
6.4 The auto-elevation allowlist
Vista's prompt fatigue was a usability disaster. Beta reviewers described users clicking through three or four prompts per common task. Windows 7, shipped in October 2009, tried to cut the noise by quietly elevating a curated set of Microsoft-signed binaries with no prompt at all. That single decision shaped the next fifteen years of UAC bypass research, because every "bypass" you have ever read about lives inside the gap between which binary gets elevated and what the binary does after elevation.
The set of Microsoft-signed binaries in trusted system directories on Appinfo's internal allowlist that elevate without a consent prompt. Four gating conditions: autoElevate=true manifest element, Microsoft Authenticode signature, trusted directory path, and an internal Appinfo allowlist entry enforced inside appinfo.dll.
The manifest element is a single string. Inside the application's side-by-side manifest, under the <trustInfo> / <security> / <requestedPrivileges> element, the binary asserts <autoElevate>true</autoElevate> [31]. That assertion was discovered and publicly documented by independent UK developer Leo Davidson in December 2009 [28].
The autoElevate=true manifest assertion is necessary but not sufficient. Appinfo enforces three additional gating conditions before honouring an auto-elevation request [28].
- The binary must carry a valid Authenticode signature chained to a Microsoft root certificate.
- The binary's path must reside under a trusted system directory, in practice
%SystemRoot%\System32or%SystemRoot%\SysWOW64(or the localized variants for non-English locales). - The binary's name must appear on an internal allowlist enforced in code in
appinfo.dll, not in any user-visible policy file.
The fourth gate (the internal allowlist) is the one that surprises practitioners. A binary can be Microsoft-signed, located in System32, and carry autoElevate=true in its manifest, and Appinfo can still refuse to auto-elevate it, because the binary's name is not on the hard-coded allowlist inside appinfo.dll. There is no public Microsoft-published file enumerating the allowlist; the only way to enumerate it operationally is to scan the manifests of every binary in System32 and cross-check which ones actually auto-elevate.
// Pseudocode of Appinfo's auto-elevation decision (Win7+).
// All four gates must pass for auto-elevation without a consent prompt.
function shouldAutoElevate(binaryPath) {
// Gate 1: the application manifest must assert autoElevate=true.
const manifest = readEmbeddedManifest(binaryPath);
if (manifest?.requestedPrivileges?.autoElevate !== true) return false;
// Gate 2: the binary must carry a valid Microsoft Authenticode signature.
const sig = verifyAuthenticodeSignature(binaryPath);
if (sig.status !== 'valid' || sig.rootCA !== 'Microsoft') return false;
// Gate 3: the binary must reside under a trusted system directory.
const trustedDirs = ['C:\\Windows\\System32\\', 'C:\\Windows\\SysWOW64\\'];
if (!trustedDirs.some(d => binaryPath.toLowerCase().startsWith(d.toLowerCase()))) return false;
// Gate 4: the binary name must appear on Appinfo's internal allowlist.
// This is the one enforced in code in appinfo.dll, not exposed as policy.
if (!APPINFO_INTERNAL_ALLOWLIST.includes(baseName(binaryPath).toLowerCase())) return false;
return true;
} Four gating conditions. Three of them constrain which binary gets elevated. None of them constrain what the binary does after elevation. The fourth gap, the behavioural one, is the space the bypass-research industry has occupied for fifteen years. That is §7.
7. Twenty Years of Bypass Research as Empirical Test
In February 2007, eleven days after Vista's consumer launch, Mark Russinovich published a TechNet Blogs post titled PsExec, User Account Control and Security Boundaries. The post walked through a quirk of how PsExec's -l switch interacted with restricted tokens on Windows XP, used the walkthrough to introduce Vista's integrity-level model, and then dropped a single sentence the entire later debate would rest on [5].
"Neither UAC elevations nor Protected Mode IE define new Windows security boundaries... potential avenues of attack, regardless of ease or scope, are not security bugs." -- Mark Russinovich, PsExec, User Account Control and Security Boundaries, TechNet Blogs, February 12, 2007
That sentence, in the public record from week one, is the architectural reason every "UAC bypass" published from 2009 onward was classified by Microsoft as a non-vulnerability. The bypass-research literature is the empirical proof of the disclaimer, not a counterargument to it. Three durable bypass classes carry the empirical weight.
7.1 The ms-settings / DelegateExecute registry-hijack class
The first durable class is the registry-hijack bypass of auto-elevated binaries. Mechanism: an auto-elevated binary (eventvwr.exe, fodhelper.exe, ComputerDefaults.exe, certain sdclt.exe variants) executes a handler for a custom file extension or URL protocol on launch. The relevant handler mapping is in HKCR, but Windows resolves HKCR by first consulting HKCU\Software\Classes and only falling back to HKLM\Software\Classes if no per-user mapping exists. A Medium-IL user can write to HKCU without elevation. So the user writes a HKCU\Software\Classes\<scheme>\shell\open\command key whose default value is an arbitrary command line and whose DelegateExecute value is the empty string. Then the user launches the auto-elevated binary. The binary loads, Appinfo elevates it to High IL, the binary resolves its registered handler, walks HKCU\Software\Classes first, finds the attacker-controlled command line, and executes it. The attacker's command runs at High IL [32][33].
The first public canonical demonstration was Matt Nelson's August 15, 2016 post Fileless UAC Bypass Using eventvwr.exe and Registry Hijacking, published on his blog under the handle enigma0x3. Nelson hijacked the mscfile association by writing HKCU\Software\Classes\mscfile\shell\open\command with cmd.exe as the default value, then launched eventvwr.exe. The Event Viewer auto-elevates because of its manifest, resolves the mscfile association to load eventvwr.msc, walks the HKCU mapping first, finds cmd.exe instead of mmc.exe, and launches an attacker-controlled cmd.exe at High IL [32]. The technique required no file on disk except the registry value itself; this is what fileless means in this context.
Nelson productised the class through 2017. The March 14, 2017 Bypassing UAC Using App Paths post generalised to HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\control.exe, exploited by sdclt.exe [34]. The March 17, 2017 'Fileless' UAC Bypass Using sdclt.exe post showed a fileless variant of the same attack using the IsolatedCommand REG_SZ value on HKCU:\Software\Classes\Folder\shell\open\command, with sdclt.exe /KickOffElev as the trigger [35]. The same post referenced WikiLeaks's March 2017 Vault7 disclosures, in which the CIA's "Vault7" cache contained operationalised versions of the technique, confirming nation-state adoption of the bypass class [35].
The fodhelper variant was published on May 12, 2017 under the title Bypassing UAC Using fodhelper.exe. The canonical URL enigma0x3.net/2017/05/12/bypassing-uac-using-fodhelper-exe/ currently returns HTTP 404; the technique is anchored by UACMe Method 33 and MITRE ATT&CK T1548.002, both of which preserve the date, author, and registry path verbatim [30][33].
Diagram source
sequenceDiagram
participant U as User (Medium IL)
participant R as HKCU registry
participant F as fodhelper.exe (auto-elevated)
participant A as Appinfo (LocalSystem)
participant C as Attacker payload (High IL)
U->>R: Write HKCU Software Classes ms-settings shell open command with attacker cmd
U->>F: ShellExecute("fodhelper.exe")
F->>A: Request elevation (autoElevate gate passes)
A->>F: New process at High IL, no consent prompt
F->>R: Resolve ms-settings handler via HKCU first
R-->>F: Returns attacker command
F->>C: Spawn attacker payload at High IL Microsoft's response to the eventvwr bypass was to ship a fix in the Windows 10 Creators Update (1703) that made eventvwr.exe not consult the registered association the technique exploited. The fix was technique-specific, not class-specific: the ms-settings (fodhelper), App Paths (sdclt), and IsolatedCommand (sdclt) variants remained exploitable through subsequent Windows 10 builds and into Windows 11 [30][33]. None of these were patched as security vulnerabilities, because, per Russinovich 2007, UAC is not a security boundary [5].
7.2 The DLL-search-order class
The second durable class is the DLL-search-order attack against auto-elevated binaries. Mechanism: an auto-elevated binary calls LoadLibrary on a DLL name resolved via the standard Windows search order: the application directory, the system directory, the current directory, the PATH environment variable, and so on. If any path on that search order earlier than the legitimate one is writable by the Medium-IL caller, the caller can plant an attacker DLL at that path. When the auto-elevated binary loads the legitimate name, the search order returns the attacker's DLL first, and the DLL is loaded at the binary's elevated IL [28].
The foundational canonical example is the December 2009 Leo Davidson essay Windows 7 UAC whitelist: Code injection issue (and more). Davidson demonstrated that sysprep.exe (Microsoft-signed, in System32, auto-elevation-allowlisted) loads cryptbase.dll from its working directory before the system directory. By copying sysprep.exe and a malicious cryptbase.dll into a writable directory and launching sysprep.exe from there, an attacker could load the malicious DLL into a High-IL process [28]. The same essay introduced the IFileOperation COM-object technique that founded the second durable class (§7.3), making the December 2009 Davidson essay the single most-cited primary in the entire UAC bypass literature.
Coverage in the trade press confirmed the class's significance immediately. In February 2009, The Register reported on a related Long Zheng / Rafael Rivera disclosure that demonstrated piggybacking on auto-elevation via rundll32.exe [36], establishing that the auto-elevation surface had been understood as exploitable from the moment Windows 7 shipped.
Microsoft's mitigations against the DLL-search-order class have been incremental. SafeDllSearchMode was made the default in Windows XP SP2 and reshuffled the search order so the application directory came before the current directory. The LOAD_LIBRARY_SEARCH_* flags introduced in Windows Vista let applications opt into stricter search behaviour. Side-by-side manifest pinning and the KnownDLLs mechanism shrink the surface further. All of these are application-author opt-ins; an auto-elevated binary that does not use them remains exploitable, and UACMe's catalogue of 81 methods includes numerous DLL-search-order entries across Windows versions [30].
7.3 The auto-elevated COM-object behaviour-abuse class
The third durable class abuses the behaviour of auto-elevation-eligible COM classes. Mechanism: a COM class registered as auto-elevation-eligible (the IFileOperation / ICMLuaUtil / IColorDataProxy family historically, then the explicit COMAutoApprovalList registry surface introduced in Windows 10 RS1 / build 14393 in August 2016) can be instantiated High-IL by a Medium-IL caller via the COM Elevation Moniker. Once instantiated, the High-IL object exposes methods (file copy, registry write, executable launch) that perform actions at High IL using whatever parameters the caller passes [28][29].
Davidson's IFileOperation proof of concept from December 2009 is the canonical example. A Medium-IL caller instantiates IFileOperation via the COM Elevation Moniker. The resulting dllhost.exe runs at High IL and exposes IFileOperation::CopyItem and related methods. The caller invokes CopyItem("evil.dll", "C:\\Windows\\System32\\"). The High-IL dllhost.exe performs the copy, because the High-IL token has write access to %SystemRoot%\System32. The caller has now planted a DLL in System32 without ever holding a High-IL token itself [28][29].
The COMAutoApprovalList era began in August 2016 with the Windows 10 Anniversary Update (RS1, build 14393). Microsoft added a dedicated registry surface at HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList enumerating which CLSIDs consent.exe would auto-elevate without a prompt. The change was unannounced: there is no Microsoft-published security bulletin naming the introduction. The community anchor is UACMe Method 49, whose fix-note carries the verbatim "Side effect of consent.exe COMAutoApprovalList introduction" against the TpmInit.exe ICreateNewLink technique, dated to RS1 / build 14393 [30]. Method 27 captures the subsequent narrowing in RS3 / 16299, when Microsoft removed the UninstallStringLauncher interface from the list.
| Class | Mechanism | Canonical research | Microsoft response |
|---|---|---|---|
| Registry-hijack (DelegateExecute) | Auto-elevated binary resolves user-writable HKCU handler | Nelson, eventvwr Aug 2016; sdclt and fodhelper 2017 | Patched individual binaries; class never classified as security vulnerability |
| DLL-search-order | Auto-elevated binary loads attacker DLL via standard search path | Davidson, December 2009 (sysprep + cryptbase) | SafeDllSearchMode, LOAD_LIBRARY_SEARCH_*, KnownDLLs; shrunk but not eliminated |
| Auto-elevated COM behaviour | Medium-IL caller invokes High-IL methods via moniker | Davidson, December 2009 (IFileOperation); COMAutoApprovalList RS1 Aug 2016 | Curated allowlist; entries added or removed in feature updates without CVEs |
7.4 The doctrine and the aha
The two distinct 2007 sources need precise attribution, because the citation chain is the load-bearing artifact of the entire UAC-as-not-a-boundary argument.
The Microsoft Security Response Center's published servicing criteria define a security boundary as one that "provides a logical separation between the code and data of security domains with different levels of trust" [37]. The MSRC servicing-criteria page enumerates which Windows boundaries qualify under that definition. Through the Vista-through-Windows-10 era (2007-2024), UAC was explicitly classified as a security feature, not a boundary, in the enumeration table on that page. The enumeration is rendered client-side (in JavaScript) and not visible through static fetches; the canonical confirmation for the classification is the Russinovich February 2007 sentence above, repeated and re-affirmed in Microsoft public statements throughout the period [5].
Forshaw's January 2026 Project Zero post on Administrator Protection reads the doctrine clearly in retrospect: "due to the way it was designed, it was quickly apparent it didn't represent a hard security boundary, and Microsoft downgraded it to a security feature" [38]. The "downgraded" framing is exact. UAC was promoted to a security feature, not a security boundary, from the moment it shipped. The reclassification in November 2024 was a re-promotion with new architecture, not a fix to the old architecture.
For the Windows administrator who has watched the bypass-research industry produce a new fileless bypass every six to twelve months, the reframing is the load-bearing aha of the entire article. The bypasses are not bugs Microsoft has failed to fix. They are the empirical map of the access-control versus information-flow gap that any access-control primitive runs into in a backward-compatible OS. The empirical record from 2009 forward (Davidson, Nelson, hfiref0x, Forshaw) is the cumulative confirmation that the disclaimer was honest.
If MIC, UIPI, and the split-token model are sound primitives, and the bypasses do not violate Microsoft's own classification of them, what are the actual theoretical limits of integrity-level systems? What can MIC and UIPI never do, by design?
8. Theoretical Limits: What MIC and UIPI Cannot Do, by Design
The 2007 disclaimer was not just an admission of weakness. It was an accurate statement of the theoretical limits of any access-control primitive in a backward-compatible operating system. The bypass-research industry of 2009 to 2026 has empirically traced out those limits one technique at a time, and a careful reading of the theory tells us why the trace looks the way it does.
Biba 1977 and the three rules
The integrity model MIC implements comes from Kenneth J. Biba's 1977 MITRE technical report MTR-3153 [14]. Biba's model is the integrity-side mirror of the better-known Bell-LaPadula confidentiality model [13]: where Bell-LaPadula's "no read up" prevents confidentiality leaks, Biba's "no write up" prevents integrity contamination. The Biba model defines three rules.
- Simple Integrity Property (no read down): a subject at integrity level cannot read an object at integrity level . A High-IL subject cannot read Low-IL data, because Low-IL data may have been written by an untrusted source and might contaminate the subject's state.
- Star Integrity Property (no write up): a subject at integrity level cannot write an object at integrity level . A Low-IL subject cannot write to a High-IL object, because the Low-IL subject's writes would degrade the High-IL object's integrity.
- Invocation Property: a subject at integrity level cannot invoke (call, request services from) a subject at integrity level . A Low-IL caller cannot ask a High-IL server to perform an action on the caller's behalf, because the High-IL server would then act on Low-IL inputs.
MIC implements the Star Integrity Property as the default NO_WRITE_UP policy. Every object that does not explicitly request a different policy is protected against lower-IL writes [3][18]. That is the one Biba rule MIC actually enforces.
MIC does not implement Biba's Simple Integrity Property at all. There is no NO_READ_DOWN policy in the winnt.h mandatory-label-policy enumeration. The opt-in NO_READ_UP bit MIC exposes points the other way: it stops a lower-IL subject from reading a higher-IL object, which is structurally Bell-LaPadula's Simple Security Property (no read up for confidentiality) repurposed onto an integrity SID rather than a confidentiality label [13][18]. By default, a Low-IL process can read a High-IL file. This is the design choice Forshaw's Reading Your Way Around UAC series turned into a research program in 2017 [19].
MIC does not implement the Invocation Property either. A Medium-IL process can invoke a High-IL service via the COM Elevation Moniker, via ShellExecuteEx "runas", via any of the auto-elevated binaries, via RPC to Appinfo. The absence of the Invocation Property is exactly what makes UAC operationally usable: a strict reading of Biba would forbid every brokered elevation surface in Windows, and the OS would be unbearable to use. The omission is deliberate, and it is the theoretical reason why every "bypass" of UAC is technically a use of an architectural surface, not a violation of it.
Diagram source
flowchart LR
subgraph BIBA ["Biba 1977 -- integrity model"]
A["Biba 1977
integrity model"] --> B["Simple Integrity
(no read down)"]
A --> C["Star Integrity
(no write up)"]
A --> D["Invocation Property
(no invoke up)"]
B --> E["MIC: not implemented
(no NO_READ_DOWN policy in winnt.h)"]
C --> F["MIC: default NO_WRITE_UP
(on by default)"]
D --> G["MIC: not implemented
(COM moniker, runas verb, Appinfo)"]
end
subgraph BLP ["Bell-LaPadula 1973 -- confidentiality model"]
H["Bell-LaPadula 1973
confidentiality model"] --> I["Simple Security
(no read up)"]
I --> J["MIC: opt-in NO_READ_UP
(off by default, repurposed onto IL)"]
end The access-control versus information-flow gap
The deeper bound is information-flow. Dorothy Denning's May 1976 Communications of the ACM paper A lattice model of secure information flow established the formal framework [39]. Denning's result is fundamental: information-flow enforcement is undecidable in the general case, because verifying that a program never leaks information from class to class requires deciding properties of arbitrary programs, which reduces to the halting problem.
MIC enforces access control, not information flow. The distinction is essential. Access control answers "can this subject perform this operation on this object?" in O(1) at operation time. Information flow asks "does the final state of this system contain any information derived from data the subject was not authorised to read?" That is undecidable.
What this means for UAC: even when MIC perfectly enforces NO_WRITE_UP, a Low-IL process can still influence a High-IL process via shared state the High-IL process reads. Forshaw's January 2026 lazy DOS device directory hijack [38] is exactly such an attack: it places attacker-controlled state in a location a High-IL process will later read, without ever writing up directly. MIC cannot prevent this; no access-control primitive can. Closing the gap requires information-flow analysis, which is provably undecidable for arbitrary code.
The five concrete limits
The theoretical bounds map onto five concrete limits any practitioner can observe on a default Windows 11 install.
The first limit is that no-write-up does not imply no-influence-up. A Low-IL process cannot write to High-IL objects directly, but it can place state (registry keys, files, environment variables, named objects) that a High-IL process will subsequently read or be influenced by. Every fileless UAC bypass in §7.1 walks through this gap.
The second limit is that NO_READ_UP is opt-in [3]. By default, a Low-IL process can read a High-IL file. This is intentional: accessibility tools, antivirus, and diagnostic utilities depend on cross-IL reads. The cost is that any High-IL data placed at a default-policy location is readable by every Medium-IL or lower process on the system.
The third limit is that UIPI covers only the windowing layer. Sockets, named pipes, COM, RPC, shared memory, MIDL-defined RPC interfaces, and every other inter-process channel that does not go through win32k.sys is out of scope [20]. UIPI is necessary, but it is not sufficient for cross-IL isolation; the full bound requires MIC on the file system, the registry, and every named object the higher-IL process might consume.
The fourth limit is the same-IL same-desktop attack surface. Two Medium-IL processes on the user's Default desktop are not isolated from each other by either MIC or UIPI. They have the same IL (no MIC bound) and they own windows on the same desktop with the same IL (no UIPI bound). Every modern browser sandbox addresses this separately, by combining MIC (the renderer runs at Low IL or Untrusted IL) with AppContainer (capability-based identity isolation) and restricted tokens (CreateRestrictedToken-style SID denial) [40][24]. Where MIC alone is insufficient, the stack layers additional primitives, but those primitives are additions to MIC, not replacements for it.
The fifth limit is the auto-elevated-binary surface. As long as a Medium-IL process can cause a High-IL process to come into existence executing user-controllable inputs (registry handlers, DLL search-order resolution, COM moniker activation, command-line arguments), the bypass-research industry has architectural space to operate. The fix would be to apply the Invocation Property strictly, which would break elevation.
Why MIC has to be a separate evaluator
The Harrison-Ruzzo-Ullman 1976 result is the theoretical reason MIC could not be implemented as discretionary ACEs [41]. HRU prove that the safety question (given an initial access matrix, will any future sequence of operations cause subject to acquire permission on object ?) is undecidable for the general access-matrix model. That undecidability is what makes mandatory policy necessary as a separate evaluator: if integrity were encoded as discretionary ACEs, the safety of an object's integrity label would inherit HRU undecidability through every principal with rights over the ACE.
By making MIC a separate evaluator with non-discretionary semantics, Windows answers the integrity-safety question in O(1) per access check: compare two SIDs, consult three policy bits, decide. The decidability comes from the separation. MIC is bounded because it is structurally simpler.
None of the bypass classes in §7 violate any of these limits. They all operate within them. The registry-hijack class places Low-IL state where a High-IL reader will consume it (limit #1). The DLL-search-order class abuses the auto-elevated-binary surface (limit #5). The COM-behaviour-abuse class operates on the absent Invocation Property. Microsoft's response, repeated for sixteen years, was to acknowledge these as architectural realities of the design rather than as bugs to fix. The bypass-research literature is the empirical map of the access-control versus information-flow gap that no mainstream OS has closed.
Did Microsoft ever try to actually move the boundary? What does it look like when a security feature finally becomes a security boundary?
9. The Adminless Successor and the Open Problems
In November 2024, Microsoft did something it had not done in seventeen years. It moved the security-boundary line. Administrator Protection, announced as a Windows 11 platform feature, became the first generation in the integrity-level lineage that Microsoft classifies as a security boundary [42][43]. The reclassification is structurally substantial. It is not Microsoft renaming UAC; it is Microsoft adding the architectural primitives a boundary classification requires.
What the split-token model shared, and what Administrator Protection separates
The per-user separate identity Windows 11 Administrator Protection provisions at first elevation. Has a different SID, %USERPROFILE%, HKCU hive, and LUID from the calling user, defeating the registry-hijack class of UAC bypasses by structurally separating elevated-process state from the caller's state.
| Property | 2007 Split-Token (UAC) | 2024 Administrator Protection |
|---|---|---|
| User SID | Same as caller | Different (per-user System Managed Administrator Account, SMAA) |
%USERPROFILE% | Same as caller | Different: C:\Users\ADMIN_<random>\ |
HKCU registry hive | Same hive as caller | Different hive (per-SMAA) |
| Logon session LUID | Same session as caller | Fresh logon session per elevation |
| Authentication | Consent click only | Windows Hello integrated authentication |
| Classification | Security feature, not boundary | Security boundary |
The concrete operational consequence of the SMAA identity change is structural defeat of the entire registry-hijack class. When an attacker writes the canonical fodhelper bypass key to HKCU\Software\Classes\ms-settings\shell\open\command, the attacker writes to the caller's HKCU hive. When fodhelper.exe is then elevated under Administrator Protection, the elevated process runs under the SMAA identity, with the SMAA's own HKCU hive, which does not contain the attacker's key. The auto-elevated binary resolves the ms-settings association via the SMAA's HKCU, falls through to HKLM, and gets the legitimate handler. The attacker's bypass is structurally defeated by the identity change, not by a per-binary fix [42][38].
The 2025 timeline
Administrator Protection's rollout has been incremental. Microsoft released it as an opt-in toggle in early 2024 Insider Preview builds, then shipped a generally-available implementation in optional update KB5067036 on October 28, 2025 [38]. The Microsoft Learn Administrator protection page acknowledges a temporary revert on December 1, 2025 "while an application compatibility issue is dealt with" [42][38]. The expected re-enablement is in 2026.
Forshaw's January 26, 2026 Project Zero post Bypassing Windows Administrator Protection documents the application-compatibility revert with verbatim precision. He notes that "the issue is unlikely to be related to anything described in this blog post," meaning that the December 2025 revert was a third-party application compatibility regression rather than a security issue with the feature itself [38]. The revert is operational, not architectural.
The 2026 retrospective: nine bypasses, five via UI Access
Forshaw's January and February 2026 Project Zero pair is the canonical modern retrospective on Administrator Protection's architectural maturity. The January post documents nine separate Administrator Protection bypasses Forshaw reported to Microsoft during the Insider Preview cycle, all of which were fixed before general availability [38]. The post details one in depth (the lazy DOS device directory hijack) and summarises the rest.
"If the weaknesses in UAC can be mitigated then it can be made a secure boundary." -- James Forshaw, Bypassing Windows Administrator Protection, Project Zero, January 26, 2026
The February 2026 follow-on post, Bypassing Administrator Protection by Abusing UI Access, is the more architecturally significant of the pair. It documents that five of the nine pre-GA Administrator Protection bypasses operated entirely through the uiAccess=true exemption, the long-standing UIPI carve-out for accessibility software inherited unchanged from Vista 2007 [23].
The reading is structural. Administrator Protection successfully closes the bypass surface that the split-token model's shared identity created (limit #1 through limit #4 in §8). It does not close the bypass surface created by the UI Access carve-out, because UI Access is a deliberate exemption from UIPI. Closing UI Access would break screen readers, on-screen keyboards, remote-control tools, and every accessibility utility that depends on cross-IL window-message access. The exemption is necessary; the residual attack surface is the cost of accessibility.
The three gating conditions for uiAccess=true (manifest assertion, valid Authenticode signature, admin-only install location) are documented in the Security Considerations for Assistive Technologies Microsoft Learn page [22]. Forshaw's February 2026 post enumerates them verbatim and describes the RAiLaunchAdminProcess Appinfo RPC entry point the UI-Access bypasses operate through [23]. The trade press picked up the story immediately: The Register covered Forshaw's January 2026 post under the headline "Google researcher sits on UAC bypass for ages, only for it to become valid with new security feature" on January 28, 2026 [44].
The downstream legacy
MIC and UIPI outlived UAC. The integrity-SID primitive is the connective tissue of every later sandbox model on Windows.
Diagram source
flowchart TD
A["Integrity-SID primitive
(MIC + UIPI, Vista 2006/2007)"] --> B["AppContainer
(Windows 8, 2012)"]
A --> C["IE Protected Mode
(IE7, Vista 2006)"]
A --> D["Edge / Chrome / Firefox sandbox tiers
(2008-present)"]
A --> E["Protected Process Light
(Windows 8.1, 2013)"]
A --> F["Administrator Protection / SMAA
(Windows 11, 2024)"]
A --> G["RunAsPPL for LSASS
(Windows 8.1, 2013)"]
A --> H["Office Protected View
(Office 2010+)"] AppContainer (Windows 8, 2012) layers package SIDs above the integrity SID and rides the same SeAccessCheck infrastructure [24]. IE Protected Mode (Windows Vista IE7, 2006) was the first non-UAC consumer of Low IL, running browser-rendered content as a Low-IL process before the user's Medium-IL interactive shell. Modern browser sandbox tiers (Chrome, Edge, Firefox content processes) use Low-IL or Untrusted-IL sandbox processes, layered with AppContainer and restricted tokens [40]. Protected Process Light (Windows 8.1, 2013) is a signature-based generalisation of the integrity-SID concept that PPL-protects LSASS against OpenProcess by lower-IL callers. Administrator Protection itself uses the integrity-SID primitive: SMAA processes run at High IL while the calling Medium-IL admin shell stays Medium [42].
The twenty-year experiment was a success. The integrity-level stack did exactly what it was designed to do: bound integrity, not authority. The consent prompt was honestly never the security boundary. Microsoft's November 2024 reclassification finally promotes a feature to a boundary by adding the architectural support the boundary classification requires (separate identity, separate profile, separate hive, separate LUID, Windows Hello-mediated transition). The bypass-research literature is the empirical proof that the 2007 disclaimer was honest, and the proof that the architecture worked exactly as architected.
MIC and UIPI outlived UAC. The integrity-SID primitive is the connective tissue of AppContainer, every modern browser sandbox, Protected Mode, Protected Process Light, and the Administrator Protection successor. The yellow dialog is the smallest, most replaceable piece of the system.
10. Inspecting the Stack on a Real Box
Every primitive in this article is observable on the Windows install you are reading on. Here are the five commands and the two tools that will let you walk the stack yourself.
Inspecting integrity levels
whoami /groups | findstr Mandatory prints the mandatory label of the current process token. From an unelevated PowerShell on an administrator account, it will read Mandatory Label\Medium Mandatory Level. From an elevated PowerShell, it will read Mandatory Label\High Mandatory Level. From a renderer-process command inside a Chromium-based browser, it would read Mandatory Label\Low Mandatory Level or Untrusted Mandatory Level, depending on the sandbox tier.
whoami /all is the longer view. It prints every group SID, every privilege, and the full mandatory label. Process Explorer (and System Informer) will show you the same data graphically, but whoami is the canonical first-party command for getting at the same kernel information from the shell. Run it twice -- once from an unelevated PowerShell, once from an elevated PowerShell on the same admin account -- and diff the outputs to see what the elevation actually changed. That is the empirical re-creation of §1's hook.
Sysinternals' Process Explorer has an Integrity column you can add via View / Select Columns / Process Image. Once enabled, it shows the IL of every running process at a glance. System Informer (the open-source Process Explorer successor) supports the same column plus richer SACL inspection. The accesschk -e -l <object> Sysinternals command prints the mandatory label of a file, registry key, or other securable object: accesschk -e -l C:\Windows\System32\drivers\ reveals the System-IL label that protects the driver directory.
The exact PowerShell one-liner for token inspection
The PowerShell-native equivalent of whoami /all that programs can consume is:
[System.Security.Principal.WindowsIdentity]::GetCurrent() |
Select-Object -ExpandProperty Groups |
ForEach-Object { $_.Translate([System.Security.Principal.NTAccount]) }This produces the same SID-to-account-name resolution whoami /groups does, and is useful inside automation that needs to test deny-only group membership programmatically.
Inspecting UIPI
UIPI is harder to observe directly because the OS does not log dropped messages. The practical demonstration is to run Spy++ (the Visual Studio windowing inspector) from a Medium-IL process and attempt to subclass a window owned by an elevated High-IL process. The subclass call silently fails. SendMessage returns 0 with GetLastError reading ERROR_ACCESS_DENIED. The ChangeWindowMessageFilterEx documentation page is the Microsoft Learn entry point for understanding the per-window, per-message exemption surface [21].
Enumerating the auto-elevation list
sigcheck -m C:\Windows\System32\*.exe | findstr /i autoelevate walks every executable in System32 and prints the manifest of each. The findstr filter narrows to lines containing autoElevate, surfacing the binaries that assert the manifest flag. On a Windows 11 25H2 install, the resulting list runs to thirty to forty binaries. Remember that the manifest-asserting list is not the same as Appinfo's internal operational allowlist; the operational subset is what UACMe enumerates [30].
Watching Appinfo in action
Procmon (Sysinternals Process Monitor) filtered on consent.exe shows every elevation event: the registry reads against the manifest, the SACL reads on the binary, the token-information queries against the caller's filtered token. The Windows Event Viewer's Applications and Services Logs / Microsoft / Windows / User Account Control channel logs elevation events at the OS level. The combination of Procmon (mechanism) and the Event Viewer (audit trail) is the standard observability surface for elevation operations.
A safe lab for the bypass classes
UACMe is the community catalogue of 81 documented UAC bypass methods, each with author, technique, target binary, and Windows-version applicability annotations [30]. For inspection of the integrity-level state of running processes from an analyst's workstation, James Forshaw's sandbox-attacksurface-analysis-tools repository (the NtObjectManager, TokenViewer, and NtCoreLib PowerShell modules) is the standard research toolchain [45]. The UACMe reference implementations (akagi32.exe, akagi64.exe) are flagged by Microsoft Defender as HackTool:Win32/Welevate, the detection name Davidson noted as early as 2009 [28]. This is research tooling, not endpoint operations: run UACMe only on a snapshot VM with Defender exclusions documented, and treat the output as an empirical confirmation of the bypass-research record rather than as an offensive primitive.
11. Five Misconceptions That Will Not Die
Five UAC misconceptions come up so often in practitioner discussions that any complete treatment owes the reader explicit corrections. Two practical questions round out the FAQ.
Frequently asked questions about UAC, MIC, and UIPI
Is UAC a security boundary?
No, and Microsoft's own documentation has said so since February 2007. The canonical "Neither UAC elevations nor Protected Mode IE define new Windows security boundaries" sentence appears in the PsExec, User Account Control and Security Boundaries TechNet Blogs post by Mark Russinovich, dated February 12, 2007 -- see §7.4 for the full quote and the citation-chain disambiguation against the Inside Windows Vista User Account Control TechNet Magazine architectural article [5][4]. The boundary line was finally moved in November 2024 with Administrator Protection, which Microsoft does classify as a security boundary [42][38]. The original split-token UAC was never a boundary, by design, and the bypass-research record from 2009 to 2024 is the empirical confirmation that the disclaimer was honest.
Is the Secure Desktop in Session 0?
No. The Secure Desktop is on the Winlogon desktop within WinSta0, within the user's interactive session (Session 1, 2, ...) -- §6.1 walks the full Object-Manager hierarchy and contrasts it with the separate Vista Session 0 Isolation feature that moved Windows services into a non-interactive Session 0 [4]. The two features ship together in Vista and are constantly confused, but they live at different layers of the Object Manager hierarchy and address different threats.
Is `<autoElevate>true</autoElevate>` in a manifest all you need to make a binary auto-elevate?
No. The manifest entry is necessary but not sufficient. Appinfo enforces three additional gates: the binary must carry a valid Microsoft Authenticode signature, the binary must reside under a trusted system directory (%SystemRoot%\System32 or %SystemRoot%\SysWOW64), and the binary's name must appear on an internal allowlist enforced in code in appinfo.dll, not in any user-visible policy file [28][31]. Copying autoElevate=true into your own binary's manifest does nothing on its own. The community-standard enumeration technique is sigcheck -m C:\Windows\System32\*.exe | findstr /i autoelevate, but that enumerates the manifest-asserting set, not the operational allowlist.
Does UIPI block all WM_* messages?
No -- UIPI blocks a specific dangerous subset (window-state mutators, hooks, input injection, journal record / playback); mouse messages, most paint messages, and read-only window queries pass. The complete row-by-row enumeration of blocked vs allowed vs degraded-but-passes message classes is in the §4.2 table. The "blocks all WM_*" misconception is one of the most common errors in Windows-security literature [20][5].
Are ShellExecute 'runas' and the COM Elevation Moniker the same thing?
No. ShellExecuteEx with the "runas" verb is whole-process elevation: Appinfo creates a new process under the caller's linked full token, and the new process runs at High IL for its entire lifetime [26]. The COM Elevation Moniker is per-object elevation: a Medium-IL caller instantiates a single COM object in a new elevated dllhost.exe exposing only that one CLSID's methods at High IL [27]. The caller stays Medium; only the COM object's host process is elevated. The bypass-research literature attacks the second surface far more than the first, because per-object elevation exposes a narrower, more abusable behavioural surface (the methods of one CLSID), while whole-process elevation requires a path-class bypass like DLL-search-order to weaponise.
Does Administrator Protection make UACMe obsolete?
Partially. The registry-hijack class (the eventvwr / fodhelper / sdclt / ComputerDefaults family from 2016-2017) is structurally defeated by the SMAA identity change: the attacker writes to the caller's HKCU hive, but the elevated process runs under the SMAA's different HKCU hive and never consults the attacker's key [42]. The DLL-search-order class is partially mitigated by the SMAA's different %USERPROFILE% and different working directory. The UI Access class is not mitigated: it is the long-standing carve-out for accessibility software, inherited unchanged from Vista 2007, and Forshaw's February 2026 Project Zero post documents that this carve-out carried five of nine pre-GA Administrator Protection bypasses [23]. UACMe remains the canonical operational catalogue for the bypass classes that survive Administrator Protection.
Is EnableLUA=0 a reasonable security setting?
No. Setting EnableLUA=0 in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System reverts the OS to the pre-Vista posture: no split-token model, every admin-account process running High-IL by default, every interactive shell holding the full admin SID set and the complete privilege list. The integrity-level primitive (MIC) remains in the kernel; the policy that makes it operationally useful (the default-Medium-IL filtered token for interactive admins) is disabled. Browser sandbox tiers still function, because they construct restricted Low-IL tokens explicitly. The admin's daily shell does not benefit, and a malware drop in any process under the admin's interactive session immediately holds High IL [1]. This is structurally the XP situation. Leaving EnableLUA=1 (the default) is correct on every modern Windows install.
12. The Plumbing Outlived the Yellow Dialog
Return to the two whoami outputs from §1. The user is the same. The session is the same. The clock has barely moved. Read them again, and now read what each line means.
The administrator group SID was present in both tokens, marked deny-only on the filtered token and enabled on the elevated token. The integrity level changed from Medium (S-1-16-8192) to High (S-1-16-12288). The privilege set expanded from the small user-mode subset to the full administrator set. The bits that moved were the kernel-level token-assignment bits in the new process Appinfo created via CreateProcessAsUser, using the dormant linked token that LSA had constructed thirty minutes earlier at logon. The yellow dialog was the consent surface on top of a token-swap primitive that existed before the dialog rendered and that can move bits without the dialog (via auto-elevation).
Four primitives carried the work. Mandatory Integrity Control added an axis to the access check that runs before the DACL and short-circuits on a Low-to-High write attempt, regardless of what the DACL says. User Interface Privilege Isolation closed the cross-IL variant of the shatter-attack class that Paget published in 2002, by dropping the dangerous subset of window messages and hook calls from lower-IL senders to higher-IL receivers. The split-token model gave every administrator a Medium-IL filtered token at logon and held the full token dormant. The Appinfo SYSTEM-trusted broker mediated the token swap when consent or auto-elevation called for it.
The bypass-research industry of 2009 to 2024 was the empirical confirmation of the architect's 2007 disclaimer. Davidson's December 2009 essay opened the auto-elevation surface; Nelson's 2016-2017 series productised the registry-hijack class; hfiref0x's UACMe catalogued 81 methods and counting; Forshaw's 2017 Reading Your Way Around UAC series named the read-side surface; the cumulative record was a sixteen-year demonstration that UAC was not a security boundary, exactly as Russinovich had publicly stated in February 2007. Microsoft never classified any of these as security vulnerabilities, because the architectural commitment from week one had been that they would not be [5][37].
The November 2024 Administrator Protection reclassification is the line finally moving. The split-token model's four shared properties between filtered and linked tokens (same SID, same %USERPROFILE%, same HKCU, same LUID) are replaced by an SMAA identity that differs on all four dimensions, plus Windows Hello-mediated authentication for every elevation [42][43]. The registry-hijack class is structurally defeated; the residual surface is the UI Access carve-out inherited unchanged from Vista 2007, which Forshaw's February 2026 Project Zero post documents as the source of five of nine pre-GA bypasses [23].
The yellow dialog is the only piece of UAC most users will ever see. It is also the one piece the OS could replace tomorrow without changing what UAC is. MIC and UIPI outlived UAC. AppContainer, every modern browser sandbox, IE Protected Mode, Office Protected View, Protected Process Light, and Administrator Protection itself all ride on the integrity-SID and WinSta0 primitives that shipped on January 30, 2007 [9][24][40]. The quiet plumbing did the work.
Next time you click "Yes" on the consent prompt, the bits that move are the same bits that move when Edge spawns a renderer at Low IL, when Defender protects LSASS as PPL, and when a SMAA process shadows your administrator identity on a Windows 11 25H2 install with Administrator Protection enabled. The dialog is the smallest part of the system. Twenty years of empirical research proved the architect right: UAC was never the boundary. The integrity-level stack was, and is [5][42].
Study guide
Key terms
- Mandatory Integrity Control (MIC)
- An access-check evaluator that compares the integrity level of a subject token to the integrity level of a target object before consulting the object's DACL. Denials short-circuit the access check; integrity beats identity.
- Integrity Level (IL)
- A well-known SID under S-1-16-X carried on every token and securable object. Seven values: Untrusted, Low, Medium, Medium Plus, High, System, Protected Process.
- User Interface Privilege Isolation (UIPI)
- The windowing-layer analog of MIC. Blocks dangerous window messages, hooks, and input injection from lower-IL processes targeting higher-IL windows on the same desktop.
- Split-token model
- Admin Approval Mode: an administrator's logon produces a Medium-IL filtered token plus a dormant High-IL linked token. The filtered token runs the interactive shell.
- Secure Desktop
- A separate desktop object (Winlogon) within WinSta0 in the user's interactive session, on which consent.exe renders the UAC prompt. Not Session 0.
- Application Information service (Appinfo)
- The SYSTEM-trusted Windows service that mediates the filtered-to-linked token swap at elevation time. Exposes RAiLaunchAdminProcess.
- Auto-elevation allowlist
- The internal Appinfo allowlist of Microsoft-signed binaries in trusted directories whose manifests assert autoElevate=true. Four gates: manifest, signature, path, allowlist entry.
- COM Elevation Moniker
- Per-object elevation via Elevation:Administrator!new:{CLSID}. Spins up a single CLSID in an isolated High-IL dllhost.exe while the caller stays Medium.
- System Managed Administrator Account (SMAA)
- The per-user separate identity Administrator Protection provisions at first elevation. Different SID, profile, HKCU, LUID from the calling user.
- Biba Star Integrity Property
- No write up: a subject at integrity level Is cannot write an object at integrity level Io > Is. MIC implements this as the default NO_WRITE_UP policy.
Comprehension questions
Why is MIC a separate evaluator rather than another ACE in the DACL?
Because DACLs are discretionary by definition; an object owner can rewrite the DACL. MIC's mandatory semantics require an evaluator that runs before the DACL and cannot be overridden by it. The HRU 1976 undecidability of the access-matrix safety question is the formal reason mandatory policy cannot be encoded as discretionary ACEs and remain decidable.
Why doesn't the consent prompt elevate?
Because the elevated token was constructed at logon by LSA, not at consent time by the prompt. The prompt asks the user whether the OS may use the already-existing linked full token to launch a new process. The token swap is performed by Appinfo, not by consent.exe; the prompt is the consent surface on top of a token-swap primitive.
Why does UIPI block WM_SETTEXT but not WM_PAINT?
Because WM_SETTEXT mutates the receiving window's state (the message replaces the window's text), and an attacker who can mutate a higher-IL window's state has gained influence over the higher-IL process. WM_PAINT only asks the window to redraw itself; it carries no attacker-controlled mutation, so allowing it from lower-IL senders is safe.
Which Biba rules does MIC actually implement?
Just one. MIC implements the Star Integrity Property (NO_WRITE_UP) as the default policy on every object that does not specify otherwise. MIC does not implement Biba's Simple Integrity Property (no read down) at all -- there is no NO_READ_DOWN policy in winnt.h. The opt-in NO_READ_UP bit MIC exposes is structurally a Bell-LaPadula simple-security analog applied to integrity SIDs, not a Biba rule. MIC does not implement the Invocation Property either; brokered elevation (COM Elevation Moniker, ShellExecuteEx 'runas', Appinfo) is the operationally usable workaround.
What changed in November 2024 that turned UAC's elevation transition into a security boundary?
Administrator Protection replaced the split-token model's four shared properties (same SID, same %USERPROFILE%, same HKCU, same LUID) with a System Managed Administrator Account that differs on all four dimensions, and required Windows Hello integrated authentication for every elevation. The structural identity separation defeats the entire registry-hijack bypass class. The residual surface is the UI Access carve-out inherited unchanged from Vista 2007.
References
- (2026). How User Account Control works. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/user-account-control/how-it-works - Microsoft Learn primary for the verbatim 'two separate access tokens are created' split-token construction at logon. ↩
- (2025). Well-known SIDs - Win32 apps. https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids - Microsoft Learn enumeration of the seven mandatory-integrity-level RIDs including S-1-16-8448 Medium Plus. ↩
- (2025). Mandatory Integrity Control - Win32 apps. https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control - Microsoft Learn primary for the verbatim 'evaluates access before access checks against an object's DACL are evaluated' MIC-runs-before-DACL claim and the four operational integrity levels. ↩
- (2007). Security: Inside Windows Vista User Account Control. https://learn.microsoft.com/en-us/previous-versions/technet-magazine/cc138019(v=msdn.10) - The June 2007 TechNet Magazine architectural canon for Vista UAC, file/registry virtualization, and the split-token model. ↩
- (2007). PsExec, User Account Control and Security Boundaries. https://web.archive.org/web/20110518125531/http://blogs.technet.com/b/markrussinovich/archive/2007/02/12/638372.aspx - Wayback snapshot of the February 12 2007 TechNet Blogs post containing the canonical 'Neither UAC elevations nor Protected Mode IE define new Windows security boundaries' disclaimer. ↩
- (2026). Aaron Margosis's Non-Admin and App-Compat WebLog (Archive). https://learn.microsoft.com/en-us/archive/blogs/aaron_margosis/ - The Microsoft Learn archive of Aaron Margosis's 'Non-Admin' MSDN blog including LUA Buglight, MakeMeAdmin, PrivBar, and the dataset that motivated Vista's file/registry virtualisation shims. ↩
- (2002). Exploiting design flaws in the Win32 API for privilege escalation (Shatter Attacks). https://www.helpnetsecurity.com/2002/08/08/exploiting-design-flaws-in-the-win32-api-for-privilege-escalation-shatter-attacks-how-to-break-windows/ - Help Net Security's stable mirror of Chris Paget's August 2002 Bugtraq paper that coined the term 'shatter attack'. ↩
- (2026). Shatter attack. https://en.wikipedia.org/wiki/Shatter_attack - Standard secondary anchor for Paget's August 2002 shatter-attack term coinage and Moore's 2004 Black Hat productisation; documents Vista UIPI and Session 0 Isolation as separate features. ↩
- (2007). Microsoft Launches Windows Vista and Microsoft Office 2007 to Consumers Worldwide. https://news.microsoft.com/source/2007/01/29/microsoft-launches-windows-vista-and-microsoft-office-2007-to-consumers-worldwide/ - Microsoft News Center primary for the Vista consumer launch on January 30 2007. ↩
- (2025). Access Control - Win32 apps. https://learn.microsoft.com/en-us/windows/win32/secauthz/access-control - Microsoft Learn primary for the NT Win32 access-control authorisation model. ↩
- (2017). Windows Internals, 7th Edition, Part 1. Microsoft Press. ISBN 978-0735684188. - The standard textbook treatment of MIC, integrity SIDs, the access-check algorithm, the split-token model, and the Appinfo service (Chapter 7, Security). ↩
- (2025). How DACLs Control Access to an Object. https://learn.microsoft.com/en-us/windows/win32/secauthz/how-dacls-control-access-to-an-object - Microsoft Learn primary for the deny-first / allow-second / implicit-deny DACL evaluation order in SeAccessCheck. ↩
- (2026). Bell-LaPadula model. https://en.wikipedia.org/wiki/Bell%E2%80%93LaPadula_model - Standard secondary anchor for the 1973 Bell-LaPadula confidentiality model; the Simple Security Property MIC repurposes as the opt-in NO_READ_UP. ↩
- (2026). Biba Model. https://en.wikipedia.org/wiki/Biba_Model - Standard secondary anchor for Kenneth J. Biba's 1977 integrity model and the three integrity rules (Simple Integrity, Star Integrity, Invocation Property). ↩
- (2024). CreateRestrictedToken function. https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-createrestrictedtoken - Microsoft Learn primary for CreateRestrictedToken and the restricting-SIDs additional-satisfaction property used by every modern Windows sandbox. ↩
- (2003). Software Restriction Policies (Windows Server 2003). https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc779607(v=ws.10) - Microsoft Learn archived primary for Software Restriction Policies on Windows Server 2003, one of the Generation-1 voluntary-least-privilege mechanisms that failed at consumer scale. ↩
- (2024). Winsafer.h header reference. https://learn.microsoft.com/en-us/windows/win32/api/winsafer/ - Microsoft Learn primary for the SAFER (Winsafer.h) substrate that SRP used for policy-driven restricted-token construction. ↩
- (2024). SYSTEM_MANDATORY_LABEL_ACE structure. https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_mandatory_label_ace - Microsoft Learn primary for the SYSTEM_MANDATORY_LABEL_ACE structure and the three policy bits NO_WRITE_UP / NO_READ_UP / NO_EXECUTE_UP defined in winnt.h. ↩
- (2017). Reading Your Way Around UAC (Part 1). https://tyranidslair.blogspot.com/2017/05/reading-your-way-around-uac-part-1.html - Forshaw's May 2017 Tyranid's Lair walkthrough containing the verbatim 'split-token administrator in UAC just means MS get to annoy you with prompts unnecessarily but serves very little, if not zero security benefit' assessment. ↩
- (2026). User Interface Privilege Isolation. https://en.wikipedia.org/wiki/User_Interface_Privilege_Isolation - The standard secondary anchor for UIPI behaviour, the MSRC 'security feature but not a security boundary' classification, and the uiAccess=true exemption. ↩
- (2024). ChangeWindowMessageFilterEx function. https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changewindowmessagefilterex - Microsoft Learn ChangeWindowMessageFilterEx page; the closest first-party Microsoft Learn UIPI conceptual surface with MSGFLT_ALLOW / MSGFLT_RESET / MSGFLT_DISALLOW action constants. ↩
- (2026). Security Considerations for Assistive Technologies. https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-securityoverview - Microsoft Learn primary for the three UI Access gating conditions (uiAccess=true manifest, Authenticode signature, admin-only install path). ↩
- (2026). Bypassing Administrator Protection by Abusing UI Access. https://projectzero.google/2026/02/windows-administrator-protection.html - Forshaw's February 2026 Project Zero follow-on documenting that five of nine pre-GA Administrator Protection bypasses operated through the UI Access carve-out inherited unchanged from Vista 2007. ↩
- (2024). AppContainer Isolation. https://learn.microsoft.com/en-us/windows/win32/secauthz/appcontainer-isolation - Microsoft Learn primary for AppContainer capability-based isolation built on top of the integrity-SID primitive. ↩
- (2024). TOKEN_ELEVATION_TYPE enumeration. https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-token_elevation_type - Microsoft Learn primary for the TOKEN_ELEVATION_TYPE enumeration (Default / Full / Limited). ↩
- (2024). ShellExecuteExA function. https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecuteexa - Microsoft Learn primary for ShellExecuteEx and the 'runas' verb activation surface used for whole-process elevation. ↩
- (2024). The COM Elevation Moniker. https://learn.microsoft.com/en-us/windows/win32/com/the-com-elevation-moniker - Microsoft Learn primary for the Elevation:Administrator!new:{CLSID} per-COM-object elevation surface and the HKCR\CLSID\{CLSID}\Elevation\Enabled=1 registry requirement. ↩
- (2009). Windows 7 UAC whitelist: Code injection issue (and more). https://pretentiousname.com/misc/win7_uac_whitelist2.html - The December 2009 foundational essay introducing both the DLL-search-order bypass (sysprep + cryptbase.dll) and the IFileOperation auto-elevated COM-class technique; flagged by Defender as HackTool:Win32/Welevate. ↩
- (2024). IFileOperation interface. https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ifileoperation - Microsoft Learn primary for the IFileOperation COM interface that replaced SHFileOperation and the CopyItem method weaponised by Davidson 2009. ↩
- (2026). UACME: Defeating Windows User Account Control. https://github.com/hfiref0x/UACME - hfiref0x's GitHub catalogue of 81 documented UAC bypass methods, the operational reference for which manifest-asserting binaries Appinfo actually honours and the COMAutoApprovalList changes by build. ↩
- (2025). Application Manifests. https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests - Microsoft Learn primary for application manifests with autoElevate listed in the requestedExecutionLevel element table. ↩
- (2016). Fileless UAC Bypass Using eventvwr.exe and Registry Hijacking. https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/ - The August 15 2016 first canonical fileless UAC bypass via HKCU mscfile registry hijack against the auto-elevated eventvwr.exe. ↩
- (2026). Abuse Elevation Control Mechanism: Bypass User Account Control (T1548.002). https://attack.mitre.org/techniques/T1548/002/ - MITRE ATT&CK T1548.002 threat-taxonomy entry for the registry-hijack, DLL-search-order, and auto-elevated COM bypass classes; surviving anchor for the dead fodhelper enigma0x3 link. ↩
- (2017). Bypassing UAC Using App Paths. https://enigma0x3.net/2017/03/14/bypassing-uac-using-app-paths/ - The March 14 2017 generalisation of the registry-hijack class to HKCU App Paths, weaponised against sdclt.exe. ↩
- (2017). 'Fileless' UAC Bypass Using sdclt.exe. https://enigma0x3.net/2017/03/17/fileless-uac-bypass-using-sdclt-exe/ - The March 17 2017 fileless sdclt /KickOffElev bypass using IsolatedCommand; references WikiLeaks Vault7 operationalised versions. ↩
- (2009). Windows 7 UAC flaw silently elevates malware access. https://www.theregister.com/software/2009/02/04/windows-7-uac-flaw-silently-elevates-malware-access/790560 - The Register's February 4 2009 report on the Rivera/Long Zheng Win7 UAC piggyback PoC against rundll32.exe. ↩
- (2025). Windows Security Servicing Criteria. https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria - The Microsoft Security Response Center criteria page with the verbatim 'A security boundary provides a logical separation between the code and data of security domains with different levels of trust' definition. ↩
- (2026). Bypassing Windows Administrator Protection. https://projectzero.google/2026/26/windows-administrator-protection.html - Forshaw's January 2026 Project Zero retrospective documenting nine pre-GA Administrator Protection bypasses, the lazy DOS device directory hijack, KB5067036, and the December 1 2025 revert. ↩
- (1976). A lattice model of secure information flow. Communications of the ACM, 19(5), 236-243. https://doi.org/10.1145/360051.360056 - Dorothy E. Denning's May 1976 Communications of the ACM lattice-information-flow paper; the formal framework for why information-flow enforcement is undecidable in the general case. ↩
- (2025). Sandbox - Chromium Design Documents. https://chromium.googlesource.com/chromium/src/+/main/docs/design/sandbox.md - Chromium project design document for the layered renderer sandbox built on MIC, AppContainer, and CreateRestrictedToken. ↩
- (1976). Protection in Operating Systems. Communications of the ACM, 19(8), 461-471. https://doi.org/10.1145/360303.360333 - Harrison, Ruzzo, and Ullman's August 1976 Communications of the ACM proof that the safety question is undecidable for the general access-matrix model; the theoretical reason MIC must be a separate evaluator. ↩
- (2025). Administrator protection. https://learn.microsoft.com/en-us/windows/security/application-security/application-control/administrator-protection/ - Microsoft Learn primary for Administrator Protection / SMAA, Windows Hello integrated authentication, and the December 1 2025 temporary revert. ↩
- (2025). Enhance your application security with Administrator protection. https://blogs.windows.com/windowsdeveloper/2025/05/19/enhance-your-application-security-with-administrator-protection/ - The May 19 2025 Microsoft Windows Developer Blog explainer for Administrator Protection with the 39,000 token-theft-per-day figure and SMAA lifecycle. ↩
- (2026). Google researcher sits on UAC bypass for ages, only for it to become valid with new security feature. https://www.theregister.com/security/2026/01/28/old-windows-quirks-help-punch-through-new-admin-defenses/4440743 - The Register's January 28 2026 coverage of Forshaw's first Project Zero post on Administrator Protection bypasses. ↩
- (2024). sandbox-attacksurface-analysis-tools. https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools - James Forshaw's sandbox-attacksurface-analysis-tools GitHub repository (NtObjectManager, TokenViewer, NtCoreLib); the standard research toolchain for token and integrity-level inspection. ↩