# 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.

*Published: 2026-05-31*
*Canonical: https://paragmali.com/blog/the-integrity-level-stack-mic-uipi-and-twenty-years-of-uacs-*
*License: CC BY 4.0 - https://creativecommons.org/licenses/by/4.0/*

---
<TLDR>
**UAC has never been the consent prompt.** Two Vista-era primitives, Mandatory Integrity Control (MIC) and User Interface Privilege Isolation (UIPI), add an integrity axis to the access check and a windowing-layer analog that blocks cross-IL message injection. The split-token model gives every administrator a Medium-IL filtered token at logon and holds the full admin token dormant. The yellow dialog is the smallest part of the system. Its architect, Mark Russinovich, publicly disclaimed it as "not a security boundary" in February 2007, and twenty years of bypass research has been the empirical confirmation. In November 2024, Microsoft finally moved the boundary line with Administrator Protection. The MIC + UIPI plumbing outlived UAC itself: it is still the substrate of every browser sandbox, every AppContainer, and the Adminless successor in 2026.
</TLDR>

## 1. 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.

```text
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)
```

```text
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 [@uac-how-it-works]. 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) [@well-known-sids]. 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 [@mic-doc].

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 [@russinovich-tnm-2007].

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.

> **Note:** The yellow dialog moves no bits. It asks permission to use authority that was already constructed at logon and held dormant. The integrity primitives, MIC and UIPI, do the bounding work whether or not a prompt ever renders.

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 [@russinovich-blog-2007]. 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 [@margosis-archive]. 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" [@russinovich-tnm-2007]. 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 [@helpnet-paget]. 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 [@shatter-wiki].

<Sidenote>The shatter-attack term is sometimes attributed to Brett Moore alone. Paget's August 2002 Bugtraq paper actually coined the term; Moore's Black Hat USA 2004 talk *Shoot The Messenger: Win32 Shatter Attacks* productised the technique class and brought it to a wider conference audience. Both attributions are correct for different artifacts.</Sidenote>

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 [@shatter-wiki].

<Aside label="Aside: Paget and Moore on the shatter-attack attribution">
The popular history of the shatter-attack class collapses two separate authorship events into one. Chris Paget's August 2002 Bugtraq paper coined the term and produced the original demonstration tool (which Paget called "Shatter") [@helpnet-paget]. Brett Moore's Black Hat USA 2004 talk *Shoot The Messenger: Win32 Shatter Attacks*, eighteen months later, productised the technique into a conference-grade reference talk and contributed additional disclosure work at Security-Assessment.com.

Both attributions are accurate for different artifacts: Paget for the term and the August 2002 paper, Moore for the Black Hat 2004 productisation. The Wikipedia *Shatter attack* article preserves both authorships verbatim [@shatter-wiki]. The reason the disambiguation matters: any historical account of Vista's UIPI design decision must attribute the threat-model framing correctly, because Microsoft cited Paget's 2002 paper, not Moore's 2004 talk, in the internal architectural discussions Russinovich later summarised [@russinovich-blog-2007].
</Aside>

### 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 [@vista-press-release]. 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* [@russinovich-tnm-2007]. 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 [@russinovich-blog-2007]. 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](/blog/windows-access-control-25-years-of-attacks/) 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 [@access-control][@windows-internals]. 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 [@russinovich-tnm-2007].

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 [@dacls-control]. 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 [@blp-wiki], Biba 1977 [@biba-wiki]) 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` [@createrestrictedtoken].

### 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 [@createrestrictedtoken]. 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 [@srp-2003]. SRP was a policy mechanism on top of the SAFER substrate [@winsafer]. 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) [@margosis-archive]. The blog became required reading inside Microsoft and the Windows-admin community.

<Aside label="Aside: the Margosis failure mode and what it taught Vista">
Margosis's writing documents the daily friction of being a non-admin on XP. A printer-driver installer fails because it writes a per-user setting to `HKLM`. A game launcher fails because it writes save files to `%ProgramFiles%`. A 1998 line-of-business app fails because it stores its INI file under its install directory. Each failure was the application's fault; in aggregate, the application population rendered non-admin operation untenable for the typical user [@margosis-archive].

Margosis's own pattern, openly discussed on the blog, was to give up on per-application diagnosis and log in as Administrator full-time, while documenting the friction professionally so Microsoft could harvest the data for Vista's compatibility shims. The primitives existed (`CreateRestrictedToken`, SRP, the SAFER substrate). The third-party software base rendered them unusable. That dataset is the reason Vista shipped file and registry virtualisation as a built-in shim [@russinovich-tnm-2007]: the only alternative was for every application vendor to fix their software, and Margosis's blog had documented for half a decade that this was not happening.
</Aside>

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 [@vista-press-release].

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

<Definition term="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. MIC denials short-circuit the access check; a Low-IL principal cannot write to a Medium-IL object regardless of what the DACL says.
</Definition>

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" [@mic-doc].

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.

<Definition term="Integrity Level (IL)">
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).
</Definition>

The seven well-known integrity-level SIDs are defined in the *Well-known SIDs* reference page on Microsoft Learn [@well-known-sids].

| 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) |

<Sidenote>The Microsoft Learn MIC reference page describes the operational set as four integrity levels (low, medium, high, system) [@mic-doc]. The Well-known SIDs reference page enumerates seven [@well-known-sids]. Both framings are correct: Untrusted is rare on consumer systems, Medium Plus is a UI-Access-only quirk used by accessibility software, and Protected Process overlaps with Protected Process Light signing-level semantics rather than the canonical IL pipeline. The four-vs-seven discrepancy is a documentation artifact, not an inconsistency in the kernel.</Sidenote>

The IL lives on a token in the `TokenIntegrityLevel` field, retrievable through `GetTokenInformation` and the `TOKEN_MANDATORY_LABEL` structure [@mic-doc]. 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 [@mandatory-label-ace]. Three policy bits are defined in the `winnt.h` header [@mandatory-label-ace].

- `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.

Object authors who do not specify a mandatory label inherit the default, which is `NO_WRITE_UP` only [@mic-doc]. The opt-in policies are exactly that: opt-in. A High-IL process that wants its files invisible to a Medium-IL process must explicitly request `NO_READ_UP` on the SACL. By default, MIC bounds writes, not reads, and that is one of the structural shapes Forshaw's 2017 "Reading Your Way Around UAC" series exploited [@forshaw-reading-uac].

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 [@mic-doc]. 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.

<Mermaid caption="The SeAccessCheck pipeline: MIC evaluator first, DACL evaluator second">
flowchart TD
    A["Subject requests access<br/>(SID set, IL, desired access)"] --> B["MIC evaluator<br/>compares subject IL to object IL<br/>against NO_WRITE_UP / NO_READ_UP policy"]
    B --> C&#123;"IL check allows<br/>requested access?"&#125;
    C -- "No" --> D["ACCESS_DENIED<br/>(DACL not consulted)"]
    C -- "Yes" --> E["DACL evaluator<br/>walks ACEs in order<br/>(deny first, then allow)"]
    E --> F&#123;"DACL grants<br/>requested access?"&#125;
    F -- "Yes" --> G["ACCESS_GRANTED"]
    F -- "No" --> H["ACCESS_DENIED"]
</Mermaid>

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.

<RunnableCode lang="js" title="The conceptual MIC-then-DACL ordering of SeAccessCheck">{`
// 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
}
`}</RunnableCode>

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.

> **Key idea:** 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

<Definition term="User Interface Privilege Isolation (UIPI)">
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.
</Definition>

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 [@uipi-wiki].

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 [@uipi-wiki][@russinovich-blog-2007].

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 [@russinovich-blog-2007][@uipi-wiki].

<Mermaid caption="UIPI silently drops a cross-IL WM_SETTEXT from a Medium-IL malware process to a High-IL elevated PowerShell window">
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
</Mermaid>

<Sidenote>The Microsoft Learn page that opens "Modifies the User Interface Privilege Isolation (UIPI) message filter for a specified window" is the `ChangeWindowMessageFilterEx` function reference [@changewindowfilter]. 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 [@uipi-wiki], and Russinovich's February 2007 TechNet Blogs post introduces UIPI by name in the original architectural canon [@russinovich-blog-2007].</Sidenote>

### 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 [@changewindowfilter].

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) [@changewindowfilter]. The function returns `BOOL`; failure is non-fatal and the caller is expected to validate the result.

<Sidenote>A High-IL window that opts `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*.</Sidenote>

### 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 [@uia-security]. 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 [@uia-security]. 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 [@forshaw-adminprot-feb26]. 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](/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/) and a restricted-token sandbox on top of MIC [@appcontainer-isolation]: 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.

<Definition term="Split-token model (Admin Approval Mode)">
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.
</Definition>

### 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 [@uac-how-it-works].

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, ...)` [@uac-how-it-works].

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" [@uac-how-it-works].

<Mermaid caption="LSA constructs three things at administrator logon: a full token, a filtered token, and the linked relationship between them">
flowchart TD
    A["User authenticates<br/>as a member of Administrators"] --> B["LSA logon processing<br/>(LsaLogonUser)"]
    B --> C["Full token<br/>(admin SIDs enabled, all privileges, IL = High)"]
    B --> D["Filtered token<br/>(admin SIDs deny-only, privileges stripped, IL = Medium)"]
    C -.->|"linked via TokenLinkedToken"| D
    D --> E["Primary token of explorer.exe<br/>and all interactive child processes"]
    C --> F["Dormant in LSA, used only by Appinfo after consent or auto-elevation"]
</Mermaid>

### The TokenElevationType API surface

Three values of the `TOKEN_ELEVATION_TYPE` enumeration describe what state the current process is in [@token-elevation-type].

- `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.

<Sidenote>`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.</Sidenote>

### 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 [@shellexecuteexa]. 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` [@russinovich-tnm-2007].

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.

> **Key idea:** 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.

<Mermaid caption="End-to-end elevation sequence: user clicks Run as administrator, Appinfo retrieves the linked full token, consent.exe runs on the Secure Desktop, Appinfo creates the new High-IL process">
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
</Mermaid>

<PullQuote>
"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
</PullQuote>

Forshaw's 2017 critique is the load-bearing observation that frames the rest of the article [@forshaw-reading-uac]. 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.

That shared-identity property is what made the bypass-research industry possible, and what Administrator Protection finally attacks in 2024 (§9). We will return to it. First, let us tour the rest of the stack the consent prompt sits on. If Appinfo is the SYSTEM-trusted broker that does the token swap, where does it live? And what stops malware from spoofing the consent prompt itself?

## 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

<Definition term="Secure Desktop">
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.
</Definition>

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 [@russinovich-tnm-2007].

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 [@russinovich-tnm-2007].

> **Note:** The Secure Desktop is *not* in Session 0. Session 0 Isolation is a different Vista feature that moved all Windows services off the interactive desktop into a non-interactive session (Session 0), separately from the per-user interactive sessions (Sessions 1, 2, ...). The Secure Desktop is *within* the user's interactive session: a different desktop object inside the same window station, not a different session. The two features ship together in Vista and are constantly confused, because they are both 2006-era hardening primitives. They are architecturally independent: Session 0 Isolation prevents services from drawing on the user's desktop, and the Secure Desktop prevents the user's processes from drawing on the prompt's desktop. Conflating them mis-describes how either one works. The corpus's [Object Manager Namespace](/blog/the-object-manager-namespace/) article (#46) covers Session 0 Isolation directly; this article treats only the Secure Desktop.

<Definition term="Session 0 Isolation">
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.
</Definition>

<Mermaid caption="The Window Station and Desktop hierarchy inside a single interactive session: WinSta0 contains both Default and Winlogon desktops, but Default cannot draw on Winlogon">
flowchart TD
    A["Session 1<br/>(interactive logon for user 'admin')"] --> B["WinSta0<br/>interactive window station"]
    A --> C["Service-0x0-3e7$<br/>non-interactive WinSta (services)"]
    B --> D["Default desktop<br/>explorer.exe, browsers, console windows<br/>(Medium IL processes)"]
    B --> E["Winlogon desktop<br/>consent.exe renders here<br/>(Secure Desktop)"]
    B --> F["Disconnect / Screen-saver<br/>desktops"]
    D -. "blocked by Object-Manager DACL<br/>and SwitchDesktop" .-> E
</Mermaid>

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)

<Definition term="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`.
</Definition>

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` [@russinovich-tnm-2007].

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 [@russinovich-tnm-2007]. The broker therefore has to run as `LocalSystem`, and that is what Appinfo is for.

The modern entry point on [Appinfo's RPC interface](/blog/every-uac-prompt-is-an-alpc-handshake-a-field-guide-to-windo/) is `RAiLaunchAdminProcess`, documented verbatim in Forshaw's February 2026 Project Zero post on Administrator Protection [@forshaw-adminprot-feb26]. 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

> **Note:** When you say "elevate a thing," the operating system understands *two* distinct primitives, not one. `ShellExecuteEx "runas"` is whole-process elevation: the OS launches a new process and runs the entire process at High IL. The COM Elevation Moniker is per-object elevation: the OS spins up an isolated `dllhost.exe` that exposes exactly one COM CLSID's methods at High IL while the caller stays at Medium. The bypass-research literature attacks these two surfaces in very different ways. Conflating them mis-describes both the attack surface and the fix surface.

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"` [@shellexecuteexa].

<Definition term="COM Elevation Moniker">
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.
</Definition>

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 [@com-elevation-moniker].

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 [@davidson-2009][@ifileoperation]. 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 [@uacme].

Both surfaces share the same backend: Appinfo brokers the token swap, and `RAiLaunchAdminProcess` (or its COM equivalent) creates the new process. The difference is whether the elevated child is a whole new process (broad authority for a long time) or a COM object's host (narrow authority for a single activation). The bypass-research literature exploits the second class far more than the first, because the second class exposes a narrower, more abusable *behavioural* surface: the CLSID's methods.

### 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*.

<Definition term="Auto-elevation allowlist">
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`.
</Definition>

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>` [@app-manifests]. That assertion was discovered and publicly documented by independent UK developer Leo Davidson in December 2009 [@davidson-2009].

The `autoElevate=true` manifest assertion is *necessary but not sufficient*. Appinfo enforces three additional gating conditions before honouring an auto-elevation request [@davidson-2009].

1. The binary must carry a valid Authenticode signature chained to a Microsoft root certificate.
2. The binary's path must reside under a trusted system directory, in practice `%SystemRoot%\System32` or `%SystemRoot%\SysWOW64` (or the localized variants for non-English locales).
3. 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.

<Aside label="Aside: how to actually enumerate the auto-elevation allowlist on your box">
The community-standard way to enumerate the manifest-asserting subset of the allowlist is to run Sysinternals `sigcheck -m C:\Windows\System32\*.exe` and pipe the output to `findstr /i autoelevate`. That gives you every binary in `System32` whose embedded manifest asserts `autoElevate=true`. On a Windows 11 25H2 install, the list runs to thirty to forty binaries: `mmc.exe`, `eventvwr.exe`, `fodhelper.exe`, `ComputerDefaults.exe`, `sdclt.exe`, `slui.exe`, and others.

The list of *names* in the manifest is not the same as the set Appinfo actually auto-elevates. UACMe's research README enumerates the operational subset: which manifest-asserting binaries Appinfo actually honours, by Windows build, with the technique class and the catalogued bypass method [@uacme]. The canonical observation is that of the manifest-asserting list, only the operationally-allowlisted subset is exploitable, and the operational subset changes silently across feature updates without any security bulletin because none of the resulting bypasses are classified as security vulnerabilities.
</Aside>

<RunnableCode lang="js" title="The conceptual logic of the four-gate Appinfo auto-elevation check">{`
// 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;
}
`}</RunnableCode>

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 [@russinovich-blog-2007].

<PullQuote>
"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
</PullQuote>

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 [@enigma-eventvwr][@mitre-t1548002].

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 [@enigma-eventvwr]. 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` [@enigma-apppaths]. 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 [@enigma-sdclt]. 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 [@enigma-sdclt].

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 [@uacme][@mitre-t1548002].

<Mermaid caption="The fodhelper ms-settings registry-hijack UAC bypass: attacker writes a HKCU key, launches the auto-elevated binary, Appinfo elevates without a prompt, the binary resolves the user-controlled command at High IL">
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
</Mermaid>

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 [@uacme][@mitre-t1548002]. None of these were patched as security vulnerabilities, because, per Russinovich 2007, UAC is not a security boundary [@russinovich-blog-2007].

### 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 [@davidson-2009].

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 [@davidson-2009]. 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` [@register-2009], 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 [@uacme].

### 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 [@davidson-2009][@ifileoperation].

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 [@davidson-2009][@ifileoperation].

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 [@uacme]. 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.

> **Note:** The verbatim "Neither UAC elevations nor Protected Mode IE define new Windows security boundaries" sentence lives in the *PsExec, User Account Control and Security Boundaries* TechNet Blogs post by Mark Russinovich, dated February 12, 2007 [@russinovich-blog-2007]. The architectural reference that most practitioners cite, *Security: Inside Windows Vista User Account Control*, was published in the June 2007 issue of *TechNet Magazine* and is the canonical reference for the integrity model, file/registry virtualisation, and the elevation pipeline [@russinovich-tnm-2007]. The architectural article does not contain the "not a security boundary" sentence; the February blog post does. Conflating the two is a citation error and gives the wrong impression of when Microsoft committed to the boundary classification.

The Microsoft Security Response Center's [published servicing criteria](/blog/windows-security-boundaries-the-document-that-decides-what-g/) define a security boundary as one that "provides a logical separation between the code and data of security domains with different levels of trust" [@msrc-criteria]. 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 [@russinovich-blog-2007].

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" [@forshaw-adminprot-jan26]. 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.

> **Note:** The twenty-year UAC bypass-research record is empirical confirmation, not counterargument, of the architect's 2007 disclaimer. Microsoft did not fix the bypasses as security vulnerabilities because Russinovich had already said in writing that there was nothing to "fix": the consent prompt was a convenience, not a boundary. The bypass record is the proof that the disclaimer was honest from week one.

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 [@biba-wiki]. Biba's model is the integrity-side mirror of the better-known Bell-LaPadula confidentiality model [@blp-wiki]: 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 $I_s$ cannot read an object at integrity level $I_o < I_s$. 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 $I_s$ cannot write an object at integrity level $I_o > I_s$. 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 $I_s$ cannot invoke (call, request services from) a subject at integrity level $I_o > I_s$. 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 [@mic-doc][@mandatory-label-ace]. 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 [@blp-wiki][@mandatory-label-ace]. 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 [@forshaw-reading-uac].

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.

<Mermaid caption="Biba 1977's three integrity rules (top subgraph) and what MIC actually does with each; the opt-in NO_READ_UP bit is a Bell-LaPadula Simple Security analog and is shown in a separate subgraph below to make the model boundary visually explicit.">
flowchart LR
    subgraph BIBA ["Biba 1977 -- integrity model"]
        A["Biba 1977<br/>integrity model"] --> B["Simple Integrity<br/>(no read down)"]
        A --> C["Star Integrity<br/>(no write up)"]
        A --> D["Invocation Property<br/>(no invoke up)"]
        B --> E["MIC: not implemented<br/>(no NO_READ_DOWN policy in winnt.h)"]
        C --> F["MIC: default NO_WRITE_UP<br/>(on by default)"]
        D --> G["MIC: not implemented<br/>(COM moniker, runas verb, Appinfo)"]
    end
    subgraph BLP ["Bell-LaPadula 1973 -- confidentiality model"]
        H["Bell-LaPadula 1973<br/>confidentiality model"] --> I["Simple Security<br/>(no read up)"]
        I --> J["MIC: opt-in NO_READ_UP<br/>(off by default, repurposed onto IL)"]
    end
</Mermaid>

<Aside label="Aside: why MIC didn't implement Biba's Invocation Property">
Strict Biba would forbid every brokered-elevation primitive in Vista. The COM Elevation Moniker, `ShellExecuteEx "runas"`, the entire RPC interface to Appinfo, the `IFileOperation`-class auto-elevated COM objects, the manifest-based elevation request: all of these are explicitly *invocations* by a lower-IL caller of a higher-IL server [@biba-wiki].

Microsoft's architectural decision was that brokered elevation is the operationally usable workaround. A Medium-IL caller cannot invoke a High-IL server directly, but a Medium-IL caller can ask the SYSTEM-trusted Appinfo broker to create a High-IL process whose initial state the broker controls. The broker is the mediation point. The brokered model is structurally weaker than strict Biba, and that weakness is exactly the surface the bypass-research industry has operated in for sixteen years. Every COM-elevation moniker bypass, every auto-elevation registry hijack, every DLL-search-order attack is a refinement of the same observation: brokered elevation lets Medium-IL inputs influence High-IL outputs in ways the broker cannot fully validate.
</Aside>

### 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 [@denning-1976]. Denning's result is fundamental: information-flow enforcement is undecidable in the general case, because verifying that a program never leaks information from class $A$ to class $B$ 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 [@forshaw-adminprot-jan26] 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 [@mic-doc]. 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 [@uipi-wiki]. 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) [@chromium-sandbox][@appcontainer-isolation]. 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 [@hru-1976]. HRU prove that the *safety question* (given an initial access matrix, will any future sequence of operations cause subject $s$ to acquire permission $p$ on object $o$?) 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](/blog/adminless-how-windows-finally-made-elevation-a-security-boun/), announced as a Windows 11 platform feature, became the first generation in the integrity-level lineage that Microsoft classifies as a security boundary [@admin-protection][@msft-devblog-adminprot]. 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 four shared properties between the filtered token and the linked token were the structural reason UAC could not be a security boundary. They are listed verbatim in Forshaw's May 2017 *Reading Your Way Around UAC* framing [@forshaw-reading-uac]: same user SID, same `%USERPROFILE%`, same `HKCU` hive, same logon-session LUID. Administrator Protection attacks all four.

<Definition term="System Managed Administrator Account (SMAA)">
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.
</Definition>

| 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 |

<Sidenote>SMAA expands as "System Managed Administrator Account" per the May 19, 2025 Microsoft Windows Developer Blog explainer *Enhance your application security with Administrator protection* [@msft-devblog-adminprot]. Earlier Microsoft Learn documentation from 2024 used the working name "Adminless" without the SMAA acronym. The corpus's Adminless / Administrator Protection article (#52) covers the SMAA lifecycle and the Insider-Preview timeline in more depth than this article does.</Sidenote>

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 [@admin-protection][@forshaw-adminprot-jan26].

### 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 [@forshaw-adminprot-jan26]. The Microsoft Learn *Administrator protection* page acknowledges a temporary revert on December 1, 2025 "while an application compatibility issue is dealt with" [@admin-protection][@forshaw-adminprot-jan26]. 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 [@forshaw-adminprot-jan26]. 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 [@forshaw-adminprot-jan26]. The post details one in depth (the lazy DOS device directory hijack) and summarises the rest.

<PullQuote>
"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
</PullQuote>

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 [@forshaw-adminprot-feb26].

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 [@uia-security]. Forshaw's February 2026 post enumerates them verbatim and describes the `RAiLaunchAdminProcess` Appinfo RPC entry point the UI-Access bypasses operate through [@forshaw-adminprot-feb26]. 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 [@register-2026].

### The downstream legacy

MIC and UIPI outlived UAC. The integrity-SID primitive is the connective tissue of every later sandbox model on Windows.

<Mermaid caption="The integrity-SID primitive at the centre, with its downstream consumers. MIC and UIPI outlived UAC.">
flowchart TD
    A["Integrity-SID primitive<br/>(MIC + UIPI, Vista 2006/2007)"] --> B["AppContainer<br/>(Windows 8, 2012)"]
    A --> C["IE Protected Mode<br/>(IE7, Vista 2006)"]
    A --> D["Edge / Chrome / Firefox sandbox tiers<br/>(2008-present)"]
    A --> E["Protected Process Light<br/>(Windows 8.1, 2013)"]
    A --> F["Administrator Protection / SMAA<br/>(Windows 11, 2024)"]
    A --> G["RunAsPPL for LSASS<br/>(Windows 8.1, 2013)"]
    A --> H["Office Protected View<br/>(Office 2010+)"]
</Mermaid>

AppContainer (Windows 8, 2012) layers package SIDs above the integrity SID and rides the same `SeAccessCheck` infrastructure [@appcontainer-isolation]. 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 [@chromium-sandbox]. [Protected Process Light](/blog/protected-process-light-when-the-administrator-isnt-enough/) (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 [@admin-protection].

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.

> **Key idea:** 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.

<Aside label="Aside: the December 2025 Administrator Protection revert">
On December 1, 2025, Microsoft temporarily reverted Administrator Protection in KB5067036 pending an application-compatibility fix [@admin-protection][@forshaw-adminprot-jan26]. Forshaw's exact framing matters: "the issue is unlikely to be related to anything described in this blog post." The revert was *not* a security regression; it was a third-party application-compatibility issue, with re-enablement expected in 2026. As of May 2026, Administrator Protection can be enabled manually on Windows 11 24H2 and later but is not the default on consumer or enterprise SKUs pending re-enablement [@admin-protection].
</Aside>

## 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.<MarginNote>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.</MarginNote> 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.

<Spoiler kind="solution" label="The exact PowerShell one-liner for token inspection">
The PowerShell-native equivalent of `whoami /all` that programs can consume is:

```powershell
[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.
</Spoiler>

### 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 [@changewindowfilter].

### 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 [@uacme].

### 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 [@uacme]. 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 [@forshaw-tools]. 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 [@davidson-2009]. 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.

> **Note:** The minimum five commands a reader can run on their own Windows box to verify everything in this article: 1. `whoami /all` (run twice: once unelevated, once elevated; diff the outputs) 2. `whoami /groups | findstr Mandatory` (inspect the IL of the current token) 3. `sigcheck -m C:\Windows\System32\eventvwr.exe` (read the autoElevate manifest) 4. `tasklist /v /fi "imagename eq svchost.exe" | findstr Appinfo` (confirm the Appinfo service host) 5. Process Explorer with the Integrity column enabled, sorted by IL (the entire stack at a glance) The whole tour takes ten minutes. By the end you will have seen the split-token model, the integrity-level lattice, the auto-elevation allowlist, the Appinfo broker, and the Medium-vs-High distribution of your interactive desktop, with your own eyes.

## 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.

<FAQ title="Frequently asked questions about UAC, MIC, and UIPI">

<FAQItem question="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 [@russinovich-blog-2007][@russinovich-tnm-2007]. The boundary line was finally moved in November 2024 with Administrator Protection, which Microsoft does classify as a security boundary [@admin-protection][@forshaw-adminprot-jan26]. 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.
</FAQItem>

<FAQItem question="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 [@russinovich-tnm-2007]. 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.
</FAQItem>

<FAQItem question="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 [@davidson-2009][@app-manifests]. 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.
</FAQItem>

<FAQItem question="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 [@uipi-wiki][@russinovich-blog-2007].
</FAQItem>

<FAQItem question="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 [@shellexecuteexa]. 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 [@com-elevation-moniker]. 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.
</FAQItem>

<FAQItem question="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 [@admin-protection]. 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 [@forshaw-adminprot-feb26]. UACMe remains the canonical operational catalogue for the bypass classes that survive Administrator Protection.
</FAQItem>

<FAQItem question="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 [@uac-how-it-works]. This is structurally the XP situation. Leaving `EnableLUA=1` (the default) is correct on every modern Windows install.
</FAQItem>

</FAQ>

## 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 [@russinovich-blog-2007][@msrc-criteria].

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 [@admin-protection][@msft-devblog-adminprot]. 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 [@forshaw-adminprot-feb26].

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 [@vista-press-release][@appcontainer-isolation][@chromium-sandbox]. 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 [@russinovich-blog-2007][@admin-protection].

<StudyGuide slug="integrity-level-stack-mic-uipi-uac" keyTerms={[
  { term: "Mandatory Integrity Control (MIC)", definition: "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." },
  { term: "Integrity Level (IL)", definition: "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." },
  { term: "User Interface Privilege Isolation (UIPI)", definition: "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." },
  { term: "Split-token model", definition: "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." },
  { term: "Secure Desktop", definition: "A separate desktop object (Winlogon) within WinSta0 in the user's interactive session, on which consent.exe renders the UAC prompt. Not Session 0." },
  { term: "Application Information service (Appinfo)", definition: "The SYSTEM-trusted Windows service that mediates the filtered-to-linked token swap at elevation time. Exposes RAiLaunchAdminProcess." },
  { term: "Auto-elevation allowlist", definition: "The internal Appinfo allowlist of Microsoft-signed binaries in trusted directories whose manifests assert autoElevate=true. Four gates: manifest, signature, path, allowlist entry." },
  { term: "COM Elevation Moniker", definition: "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." },
  { term: "System Managed Administrator Account (SMAA)", definition: "The per-user separate identity Administrator Protection provisions at first elevation. Different SID, profile, HKCU, LUID from the calling user." },
  { term: "Biba Star Integrity Property", definition: "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." }
]} questions={[
  { q: "Why is MIC a separate evaluator rather than another ACE in the DACL?", a: "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." },
  { q: "Why doesn't the consent prompt elevate?", a: "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." },
  { q: "Why does UIPI block WM_SETTEXT but not WM_PAINT?", a: "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." },
  { q: "Which Biba rules does MIC actually implement?", a: "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." },
  { q: "What changed in November 2024 that turned UAC's elevation transition into a security boundary?", a: "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." }
]} />
