<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Parag Mali - tag: access-control</title><description>Posts tagged access-control.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sun, 07 Jun 2026 04:13:17 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/access-control/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Integrity-Level Stack: MIC, UIPI, and Twenty Years of UAC&apos;s Quiet Plumbing</title><link>https://paragmali.com/blog/the-integrity-level-stack-mic-uipi-and-twenty-years-of-uacs-/</link><guid isPermaLink="true">https://paragmali.com/blog/the-integrity-level-stack-mic-uipi-and-twenty-years-of-uacs-/</guid><description>What UAC actually is beneath the consent prompt: Mandatory Integrity Control, UIPI, the split-token model, and twenty years of bypass research as proof.</description><pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate><content:encoded>
**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 &quot;not a security boundary&quot; 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.
&lt;h2&gt;1. Two whoami Outputs, Sixty Seconds Apart&lt;/h2&gt;
&lt;p&gt;Open an unelevated PowerShell on a Windows 11 administrator account. Run &lt;code&gt;whoami /groups /priv&lt;/code&gt;. Click &quot;Yes&quot; on the yellow prompt. Open an elevated PowerShell on the &lt;em&gt;same&lt;/em&gt; 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.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;PS C:\Users\admin&amp;gt; whoami /groups /priv | findstr /i &quot;Mandatory Administrators SeDebug&quot;
BUILTIN\Administrators                Group used for deny only
Mandatory Label\Medium Mandatory Level Label
(SeDebugPrivilege not present)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;PS C:\Users\admin&amp;gt; whoami /groups /priv | findstr /i &quot;Mandatory Administrators SeDebug&quot;
BUILTIN\Administrators                Enabled by default, Enabled group, Group owner
Mandatory Label\High Mandatory Level   Label
SeDebugPrivilege                       Disabled
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Four facts fall out of those two outputs, and each one of them is a foothold for the rest of this article.&lt;/p&gt;
&lt;p&gt;The first fact is that the administrator group SID is &lt;em&gt;present in both tokens&lt;/em&gt;. It is not added by the elevation. In the filtered token it carries the flag &lt;code&gt;SE_GROUP_USE_FOR_DENY_ONLY&lt;/code&gt;, 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.&lt;/p&gt;
&lt;p&gt;The second fact is the integrity level. In the filtered token, the mandatory label reads &lt;code&gt;Mandatory Label\Medium Mandatory Level&lt;/code&gt;. In the elevated token, the same label reads &lt;code&gt;Mandatory Label\High Mandatory Level&lt;/code&gt;. That label corresponds to a well-known SID under the &lt;code&gt;S-1-16-X&lt;/code&gt; family (&lt;code&gt;S-1-16-8192&lt;/code&gt; for Medium and &lt;code&gt;S-1-16-12288&lt;/code&gt; 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 &lt;em&gt;before&lt;/em&gt; the discretionary access check [@mic-doc].&lt;/p&gt;
&lt;p&gt;The third fact is the privilege set. The filtered token holds a small set of user-mode privileges (&lt;code&gt;SeChangeNotifyPrivilege&lt;/code&gt;, &lt;code&gt;SeShutdownPrivilege&lt;/code&gt;, a handful of others). The elevated token holds the full administrator privilege set, including the named ones the security press writes about: &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, &lt;code&gt;SeTakeOwnershipPrivilege&lt;/code&gt;, &lt;code&gt;SeLoadDriverPrivilege&lt;/code&gt;, &lt;code&gt;SeBackupPrivilege&lt;/code&gt;, &lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt;, and twenty or so others, depending on the Windows build [@russinovich-tnm-2007].&lt;/p&gt;
&lt;p&gt;The fourth fact is the most subtle, and the one this whole article exists to make rigorous. The yellow dialog did not &lt;em&gt;create&lt;/em&gt; 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: &lt;em&gt;may I, the operating system, use the token I already have?&lt;/em&gt; It did not ask: &lt;em&gt;may I, the operating system, mint a more privileged token now?&lt;/em&gt; That distinction is the difference between how every Windows user &lt;em&gt;talks&lt;/em&gt; about UAC and how UAC actually works.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The four primitives we are about to tour are the substrate beneath everything in those two &lt;code&gt;whoami&lt;/code&gt; outputs. Mandatory Integrity Control (MIC) is the access-check evaluator that decided your Medium-IL PowerShell could not write into &lt;code&gt;%SystemRoot%\System32&lt;/code&gt; before any DACL was consulted. User Interface Privilege Isolation (UIPI) is the windowing-layer analog that prevented your Medium-IL Edge tab from injecting &lt;code&gt;WM_SETTEXT&lt;/code&gt; 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 &quot;Yes.&quot;&lt;/p&gt;
&lt;p&gt;This article walks every one of those layers, then ends at the empirical proof: twenty years of &quot;UAC bypasses,&quot; and Microsoft&apos;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?&lt;/p&gt;
&lt;h2&gt;2. The XP Problem and the Vista Bet&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Sysinternals co-founder Mark Russinovich, then a Microsoft engineer following the 2006 Winternals acquisition, framed the problem precisely in the June 2007 issue of &lt;em&gt;TechNet Magazine&lt;/em&gt;: &quot;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&quot; [@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.&lt;/p&gt;
&lt;p&gt;Two distinct threat models drove the architectural response Vista shipped four years later.&lt;/p&gt;
&lt;h3&gt;Threat model one: the runaway admin&lt;/h3&gt;
&lt;p&gt;The first threat model was the &lt;em&gt;runaway admin&lt;/em&gt;. Default-admin consumer installs meant malware silently inherited admin authority because the user &lt;em&gt;was&lt;/em&gt; 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.&lt;/p&gt;
&lt;h3&gt;Threat model two: the shatter-attack class&lt;/h3&gt;
&lt;p&gt;The second threat model was the &lt;em&gt;shatter-attack class&lt;/em&gt;. In August 2002, security researcher Chris Paget published a paper titled &quot;Exploiting design flaws in the Win32 API for privilege escalation&quot; on the Bugtraq mailing list, immediately mirrored on Help Net Security [@helpnet-paget]. The paper coined the term &lt;em&gt;shatter attack&lt;/em&gt; and demonstrated that on Windows NT, 2000, and XP, any process running on a user&apos;s interactive desktop could send a &lt;code&gt;WM_TIMER&lt;/code&gt; message carrying a callback function pointer to any other process&apos;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].&lt;/p&gt;
&lt;p&gt;The shatter-attack term is sometimes attributed to Brett Moore alone. Paget&apos;s August 2002 Bugtraq paper actually coined the term; Moore&apos;s Black Hat USA 2004 talk &lt;em&gt;Shoot The Messenger: Win32 Shatter Attacks&lt;/em&gt; productised the technique class and brought it to a wider conference audience. Both attributions are correct for different artifacts.&lt;/p&gt;
&lt;p&gt;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&apos;s interactive desktop with &lt;code&gt;LocalSystem&lt;/code&gt; authority, and the user&apos;s browser could send them messages.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;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].&lt;/p&gt;

The popular history of the shatter-attack class collapses two separate authorship events into one. Chris Paget&apos;s August 2002 Bugtraq paper coined the term and produced the original demonstration tool (which Paget called &quot;Shatter&quot;) [@helpnet-paget]. Brett Moore&apos;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.&lt;p&gt;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 &lt;em&gt;Shatter attack&lt;/em&gt; article preserves both authorships verbatim [@shatter-wiki]. The reason the disambiguation matters: any historical account of Vista&apos;s UIPI design decision must attribute the threat-model framing correctly, because Microsoft cited Paget&apos;s 2002 paper, not Moore&apos;s 2004 talk, in the internal architectural discussions Russinovich later summarised [@russinovich-blog-2007].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The Vista bet, stated as four design decisions&lt;/h3&gt;
&lt;p&gt;Between 2005 and 2006, Microsoft made four decisions about how Vista would respond. The first was to split the administrator&apos;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.&lt;/p&gt;
&lt;p&gt;All four shipped together. Vista RTM&apos;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 &quot;the most significant product launch in Microsoft Corp.&apos;s history.&quot;&lt;/p&gt;
&lt;p&gt;The architectural canon was published five months later, in the June 2007 issue of &lt;em&gt;TechNet Magazine&lt;/em&gt; under the title &lt;em&gt;Security: Inside Windows Vista User Account Control&lt;/em&gt; [@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 &lt;em&gt;earlier&lt;/em&gt;, 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&apos;t NT&apos;s existing access-control model handle any of this on its own?&lt;/p&gt;
&lt;h2&gt;3. Why the DACL and the Privilege Were Not Enough&lt;/h2&gt;
&lt;p&gt;Windows NT had the &lt;a href=&quot;https://paragmali.com/blog/windows-access-control-25-years-of-attacks/&quot; rel=&quot;noopener&quot;&gt;access-control model&lt;/a&gt; from day one. It had Security Identifiers (SIDs), access tokens, discretionary access control lists (DACLs), privileges, and an access-check algorithm with a name (&lt;code&gt;SeAccessCheck&lt;/code&gt;) 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.&lt;/p&gt;
&lt;p&gt;The user double-clicks the document. Word starts. Word loads the document&apos;s embedded macro. The macro calls &lt;code&gt;URLDownloadToFile&lt;/code&gt; and writes &lt;code&gt;evil.exe&lt;/code&gt; into &lt;code&gt;%TEMP%&lt;/code&gt;. Then it calls &lt;code&gt;CreateProcess&lt;/code&gt; on &lt;code&gt;evil.exe&lt;/code&gt;. The new process inherits its parent&apos;s primary access token, which is the user&apos;s interactive token, which carries the administrator group SID, enabled, with the full administrator privilege set. The DACL on &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Services&lt;/code&gt; grants Full Control to &lt;code&gt;BUILTIN\Administrators&lt;/code&gt;. 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].&lt;/p&gt;
&lt;p&gt;The first problem is in the &lt;em&gt;D&lt;/em&gt; of DACL. Discretionary access control lists are &lt;em&gt;discretionary&lt;/em&gt; 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 &lt;em&gt;discretionary&lt;/em&gt;. 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&apos;s authority.&lt;/p&gt;
&lt;p&gt;The second problem is in the privilege model. A Windows access token carries a list of named &lt;em&gt;privileges&lt;/em&gt; such as &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, &lt;code&gt;SeTakeOwnershipPrivilege&lt;/code&gt;, &lt;code&gt;SeLoadDriverPrivilege&lt;/code&gt;. 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 &quot;this Word process holds the admin&apos;s identity but should not be trusted to use &lt;code&gt;SeDebugPrivilege&lt;/code&gt;.&quot; 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 &lt;code&gt;CreateRestrictedToken&lt;/code&gt; [@createrestrictedtoken].&lt;/p&gt;
&lt;h3&gt;Generation 1: the seven-year failure to make least-privilege voluntary&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CreateRestrictedToken&lt;/code&gt; 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&apos;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.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;runas.exe&lt;/code&gt;, 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 &lt;code&gt;runas&lt;/code&gt; an administrator account when needed. In practice, the user logged in as the administrator and forgot the standard account existed.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Aaron Margosis, then a Microsoft consultant, ran a years-long blog campaign called &quot;Non-Admin&quot; 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 &lt;code&gt;runas&lt;/code&gt; 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.&lt;/p&gt;

Margosis&apos;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&apos;s fault; in aggregate, the application population rendered non-admin operation untenable for the typical user [@margosis-archive].&lt;p&gt;Margosis&apos;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&apos;s compatibility shims. The primitives existed (&lt;code&gt;CreateRestrictedToken&lt;/code&gt;, 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&apos;s blog had documented for half a decade that this was not happening.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;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].&lt;/p&gt;
&lt;p&gt;What does an integrity primitive look like, and how is it different from &quot;another ACE&quot;?&lt;/p&gt;
&lt;h2&gt;4. The Twin Primitives: MIC and UIPI&lt;/h2&gt;
&lt;h3&gt;4.1 Mandatory Integrity Control&lt;/h3&gt;

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&apos;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.
&lt;p&gt;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 &quot;just another ACE&quot; lives in that sentence. MIC &quot;evaluates access before access checks against an object&apos;s discretionary access control list (DACL) are evaluated&quot; [@mic-doc].&lt;/p&gt;
&lt;p&gt;Pause on that ordering. &lt;em&gt;Before&lt;/em&gt; the DACL. Not &lt;em&gt;together with&lt;/em&gt; it. Not &lt;em&gt;after&lt;/em&gt; 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 &lt;em&gt;mandatory&lt;/em&gt; in &lt;em&gt;Mandatory Integrity Control&lt;/em&gt; means.&lt;/p&gt;

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).
&lt;p&gt;The seven well-known integrity-level SIDs are defined in the &lt;em&gt;Well-known SIDs&lt;/em&gt; reference page on Microsoft Learn [@well-known-sids].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Integrity level&lt;/th&gt;
&lt;th&gt;RID (S-1-16-X)&lt;/th&gt;
&lt;th&gt;Typical use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Untrusted&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Most-restricted sandboxes; rare on consumer Windows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-4096&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IE Protected Mode, AppContainer, Edge / Chrome renderers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-8192&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default for interactive user processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium Plus&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-8448&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UI-Access processes (&lt;code&gt;uiAccess=true&lt;/code&gt; manifest, Windows 7+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-12288&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Elevated administrative processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-16384&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel-mode and &lt;code&gt;LocalSystem&lt;/code&gt; services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protected Process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;S-1-16-20480&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PPL-protected processes (LSASS with &lt;code&gt;RunAsPPL&lt;/code&gt;, antimalware)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The IL lives on a token in the &lt;code&gt;TokenIntegrityLevel&lt;/code&gt; field, retrievable through &lt;code&gt;GetTokenInformation&lt;/code&gt; and the &lt;code&gt;TOKEN_MANDATORY_LABEL&lt;/code&gt; structure [@mic-doc]. The IL lives on an object in the system access control list (SACL) as a &lt;code&gt;SYSTEM_MANDATORY_LABEL_ACE&lt;/code&gt;, a special ACE type that carries the object&apos;s IL SID and a mandatory-policy mask [@mandatory-label-ace]. Three policy bits are defined in the &lt;code&gt;winnt.h&lt;/code&gt; header [@mandatory-label-ace].&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_WRITE_UP&lt;/code&gt; (0x1) -- default. A subject at lower IL cannot write to this object.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_READ_UP&lt;/code&gt; (0x2) -- opt-in. A subject at lower IL cannot read this object.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP&lt;/code&gt; (0x4) -- opt-in. A subject at lower IL cannot execute this object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Object authors who do not specify a mandatory label inherit the default, which is &lt;code&gt;NO_WRITE_UP&lt;/code&gt; 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 &lt;code&gt;NO_READ_UP&lt;/code&gt; on the SACL. By default, MIC bounds writes, not reads, and that is one of the structural shapes Forshaw&apos;s 2017 &quot;Reading Your Way Around UAC&quot; series exploited [@forshaw-reading-uac].&lt;/p&gt;
&lt;p&gt;The &quot;regardless of DACL&quot; property is the part to read slowly. A Low-IL principal cannot write to a Medium-IL object &quot;even if that object&apos;s DACL allows write access to the principal,&quot; 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 &quot;another ACE&quot; 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 &lt;code&gt;SeAccessCheck&lt;/code&gt; itself, independently of any other ACE in the DACL.&lt;/p&gt;

flowchart TD
    A[&quot;Subject requests access&lt;br /&gt;(SID set, IL, desired access)&quot;] --&amp;gt; B[&quot;MIC evaluator&lt;br /&gt;compares subject IL to object IL&lt;br /&gt;against NO_WRITE_UP / NO_READ_UP policy&quot;]
    B --&amp;gt; C{&quot;IL check allows&lt;br /&gt;requested access?&quot;}
    C -- &quot;No&quot; --&amp;gt; D[&quot;ACCESS_DENIED&lt;br /&gt;(DACL not consulted)&quot;]
    C -- &quot;Yes&quot; --&amp;gt; E[&quot;DACL evaluator&lt;br /&gt;walks ACEs in order&lt;br /&gt;(deny first, then allow)&quot;]
    E --&amp;gt; F{&quot;DACL grants&lt;br /&gt;requested access?&quot;}
    F -- &quot;Yes&quot; --&amp;gt; G[&quot;ACCESS_GRANTED&quot;]
    F -- &quot;No&quot; --&amp;gt; H[&quot;ACCESS_DENIED&quot;]
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;{`
// Pseudocode of the Windows access-check ordering (Vista+).
// See Microsoft Learn: Mandatory Integrity Control.&lt;/p&gt;
&lt;p&gt;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&lt;/p&gt;
&lt;p&gt;  if (subjectIL &amp;lt; objectIL) {
    if ((policy &amp;amp; NO_WRITE_UP)   &amp;amp;&amp;amp; (desiredAccess &amp;amp; WRITE_BITS))   return &apos;ACCESS_DENIED&apos;;
    if ((policy &amp;amp; NO_READ_UP)    &amp;amp;&amp;amp; (desiredAccess &amp;amp; READ_BITS))    return &apos;ACCESS_DENIED&apos;;
    if ((policy &amp;amp; NO_EXECUTE_UP) &amp;amp;&amp;amp; (desiredAccess &amp;amp; EXECUTE_BITS)) return &apos;ACCESS_DENIED&apos;;
  }&lt;/p&gt;
&lt;p&gt;  // 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 === &apos;DENY&apos;  &amp;amp;&amp;amp; (ace.mask &amp;amp; desiredAccess)) return &apos;ACCESS_DENIED&apos;;
      if (ace.type === &apos;ALLOW&apos;) desiredAccess &amp;amp;= ~ace.mask;
      if (desiredAccess === 0) return &apos;ACCESS_GRANTED&apos;;
    }
  }
  return &apos;ACCESS_DENIED&apos;; // implicit deny if no ACE grants
}
`}&lt;/p&gt;
&lt;p&gt;The naive reading of MIC is &quot;they added another ACE for integrity.&quot; 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 &lt;em&gt;mandatory&lt;/em&gt; means.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;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 &lt;code&gt;SeAccessCheck&lt;/code&gt;. So Vista shipped a second primitive specifically for the windowing layer.&lt;/p&gt;
&lt;h3&gt;4.2 User Interface Privilege Isolation&lt;/h3&gt;

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.
&lt;p&gt;If MIC is mandatory integrity for &lt;em&gt;objects&lt;/em&gt;, UIPI is mandatory integrity for &lt;em&gt;windows&lt;/em&gt;. 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&apos;s own configuration [@uipi-wiki].&lt;/p&gt;
&lt;p&gt;The canonical failed-shatter scenario is short and exact. A Medium-IL malware process calls &lt;code&gt;SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)&quot;some-attacker-controlled-string&quot;)&lt;/code&gt; against a window handle (&lt;code&gt;hwnd&lt;/code&gt;) belonging to a High-IL elevated PowerShell on the same desktop. On Windows XP, the message would arrive and the elevated PowerShell&apos;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. &lt;code&gt;GetLastError&lt;/code&gt; returns &lt;code&gt;ERROR_ACCESS_DENIED&lt;/code&gt;. The message is silently dropped by &lt;code&gt;win32k.sys&lt;/code&gt; before the receiving process&apos;s window procedure ever sees it. The window manager noticed that the sender&apos;s IL was lower than the receiver&apos;s IL and dropped the message [@uipi-wiki][@russinovich-blog-2007].&lt;/p&gt;
&lt;p&gt;The &quot;silently dropped&quot; part matters operationally. Legacy applications written before Vista did not check the return value of &lt;code&gt;SendMessage&lt;/code&gt;. When Vista shipped UIPI, those applications kept &quot;working&quot; 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.&lt;/p&gt;
&lt;h3&gt;What UIPI blocks, precisely&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;UIPI behaviour from lower IL to higher IL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SendMessage&lt;/code&gt; / &lt;code&gt;PostMessage&lt;/code&gt; for &lt;code&gt;WM_SETTEXT&lt;/code&gt;, edit-control mutators, combo-box mutators&lt;/td&gt;
&lt;td&gt;Blocked; returns 0 / &lt;code&gt;ERROR_ACCESS_DENIED&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Posted messages above &lt;code&gt;WM_USER&lt;/code&gt; (0x0400)&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WM_TIMER&lt;/code&gt; with a callback function pointer&lt;/td&gt;
&lt;td&gt;Blocked (the original Paget vector)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetWindowsHookEx&lt;/code&gt; against a higher-IL thread or process&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AttachThreadInput&lt;/code&gt; to a higher-IL thread&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SendInput&lt;/code&gt; targeting a higher-IL window&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Journal record / journal playback hooks&lt;/td&gt;
&lt;td&gt;Blocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mouse and most keyboard input from the OS itself&lt;/td&gt;
&lt;td&gt;Allowed (the user is the principal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Most paint messages (&lt;code&gt;WM_PAINT&lt;/code&gt;, &lt;code&gt;WM_ERASEBKGND&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read-only window queries (&lt;code&gt;GetWindowText&lt;/code&gt;, &lt;code&gt;EnumWindows&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Allowed (return empty / minimal data rather than failing)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&quot;UIPI blocks all &lt;code&gt;WM_*&lt;/code&gt; messages&quot; is one of the most common misconceptions in Windows-security literature. It does not. It blocks the &lt;em&gt;dangerous subset&lt;/em&gt;: the messages and hooks that allow a sender to alter the receiving process&apos;s state or execute code in it [@russinovich-blog-2007][@uipi-wiki].&lt;/p&gt;

sequenceDiagram
    participant M as Medium-IL malware
    participant W as win32k.sys
    participant P as High-IL PowerShell
    M-&amp;gt;&amp;gt;W: SendMessage(hwnd, WM_SETTEXT, ...)
    W-&amp;gt;&amp;gt;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--&amp;gt;&amp;gt;M: Returns 0, ERROR_ACCESS_DENIED
    Note over P: Window procedure never invoked, text unchanged
&lt;p&gt;The Microsoft Learn page that opens &quot;Modifies the User Interface Privilege Isolation (UIPI) message filter for a specified window&quot; is the &lt;code&gt;ChangeWindowMessageFilterEx&lt;/code&gt; 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 &quot;User Interface Privilege Isolation&quot; at the &lt;code&gt;winmsg&lt;/code&gt; path: the Wikipedia UIPI article is the standard secondary anchor for the concept itself [@uipi-wiki], and Russinovich&apos;s February 2007 TechNet Blogs post introduces UIPI by name in the original architectural canon [@russinovich-blog-2007].&lt;/p&gt;
&lt;h3&gt;The opt-in exemption: &lt;code&gt;ChangeWindowMessageFilterEx&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;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 &lt;code&gt;WM_COPYDATA&lt;/code&gt; from a Medium-IL client), the higher-IL process can call &lt;code&gt;ChangeWindowMessageFilterEx&lt;/code&gt; to add the specific message to its window&apos;s allow-list [@changewindowfilter].&lt;/p&gt;
&lt;p&gt;The action constants are documented as &lt;code&gt;MSGFLT_ALLOW&lt;/code&gt; (add the message to the allow-list), &lt;code&gt;MSGFLT_RESET&lt;/code&gt; (remove explicit policy and inherit defaults), and &lt;code&gt;MSGFLT_DISALLOW&lt;/code&gt; (explicitly block the message even if defaults would allow it) [@changewindowfilter]. The function returns &lt;code&gt;BOOL&lt;/code&gt;; failure is non-fatal and the caller is expected to validate the result.&lt;/p&gt;
&lt;p&gt;A High-IL window that opts &lt;code&gt;WM_SETTEXT&lt;/code&gt; 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 &lt;code&gt;system()&lt;/code&gt; has bypassed least privilege. The mechanism cannot make the higher-IL process safe; it can only make the higher-IL process &lt;em&gt;aware&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;The &lt;code&gt;uiAccess=true&lt;/code&gt; carve-out&lt;/h3&gt;
&lt;p&gt;The single largest residual exemption from UIPI is the &lt;code&gt;uiAccess=true&lt;/code&gt; 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 &lt;code&gt;uiAccess=true&lt;/code&gt; in its application manifest gets, at process creation, a token flag (&lt;code&gt;TokenUIAccess&lt;/code&gt;) that exempts the process from UIPI&apos;s cross-IL blocks for the &lt;em&gt;outbound&lt;/em&gt; direction. A Medium-IL UI-Access process can post &lt;code&gt;WM_SETTEXT&lt;/code&gt; to a High-IL elevated PowerShell window, because the Medium-IL process is acting on behalf of an accessibility client.&lt;/p&gt;
&lt;p&gt;The gating conditions for &lt;code&gt;uiAccess=true&lt;/code&gt; are tight, by design. Microsoft Learn enumerates three [@uia-security]. The manifest must assert &lt;code&gt;uiAccess=&quot;true&quot;&lt;/code&gt; in the &lt;code&gt;requestedExecutionLevel&lt;/code&gt; element. The binary must carry a valid Authenticode signature. The binary must reside in a directory writable only by administrators, which in practice means &lt;code&gt;%SystemRoot%\System32&lt;/code&gt;, &lt;code&gt;%ProgramFiles%&lt;/code&gt;, or a similarly admin-only path. The three conditions together are intended to bound &lt;code&gt;uiAccess&lt;/code&gt; to vetted, signed, install-time-protected binaries.&lt;/p&gt;
&lt;p&gt;We will return to the &lt;code&gt;uiAccess&lt;/code&gt; carve-out in §9, because Forshaw&apos;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.&lt;/p&gt;
&lt;h3&gt;What UIPI killed, precisely&lt;/h3&gt;
&lt;p&gt;UIPI killed the &lt;em&gt;cross-IL&lt;/em&gt; variant of the Brett-Moore-2002 shatter-attack class. Same-IL shatter attacks (two Medium-IL processes on the user&apos;s &lt;code&gt;Default&lt;/code&gt; desktop, both belonging to the same user, both running with the user&apos;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 &lt;a href=&quot;https://paragmali.com/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/&quot; rel=&quot;noopener&quot;&gt;AppContainer&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;Together, MIC and UIPI provide an integrity bound on &lt;em&gt;access&lt;/em&gt; (objects) and on &lt;em&gt;user-interface manipulation&lt;/em&gt; (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?&lt;/p&gt;
&lt;h2&gt;5. The Split-Token Breakthrough&lt;/h2&gt;
&lt;p&gt;The integrity-level pair (MIC plus UIPI) is the access-control primitive. The split-token model is the &lt;em&gt;policy decision&lt;/em&gt; that wires those primitives into the administrator&apos;s everyday experience. Without the split-token policy, an administrator&apos;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 &lt;em&gt;two&lt;/em&gt; tokens. One is in use. The other is dormant. The yellow dialog is the negotiation that toggles between them.&lt;/p&gt;

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.
&lt;h3&gt;What the LSA does at logon&lt;/h3&gt;
&lt;p&gt;When &lt;code&gt;EnableLUA=1&lt;/code&gt; in &lt;code&gt;HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System&lt;/code&gt; (the default since Vista), and an Administrators-group user logs on, the Local Security Authority subsystem (&lt;code&gt;LSASS&lt;/code&gt;) constructs three things during logon processing [@uac-how-it-works].&lt;/p&gt;
&lt;p&gt;The first is the &lt;em&gt;full token&lt;/em&gt;: an access token that contains all of the user&apos;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&apos;s primary token from logon onward.&lt;/p&gt;
&lt;p&gt;The second is the &lt;em&gt;filtered token&lt;/em&gt;: a copy of the full token with all administrator-equivalent group SIDs marked &lt;code&gt;SE_GROUP_USE_FOR_DENY_ONLY&lt;/code&gt;, 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 (&lt;code&gt;SeDebug&lt;/code&gt;, &lt;code&gt;SeTakeOwnership&lt;/code&gt;, &lt;code&gt;SeLoadDriver&lt;/code&gt;, &lt;code&gt;SeAssignPrimaryToken&lt;/code&gt;, and others) are dropped from the filtered token entirely.&lt;/p&gt;
&lt;p&gt;The third is the &lt;em&gt;linked relationship&lt;/em&gt;: the LSA stamps each token with a reference to the other via the &lt;code&gt;TokenLinkedToken&lt;/code&gt; 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 &lt;code&gt;NtQueryInformationToken(filteredToken, TokenLinkedToken, &amp;amp;linkedToken, ...)&lt;/code&gt; [@uac-how-it-works].&lt;/p&gt;
&lt;p&gt;The filtered token then becomes the primary access token of the user&apos;s interactive shell (&lt;code&gt;explorer.exe&lt;/code&gt;). Every process the user launches by clicking, by &lt;code&gt;Win+R&lt;/code&gt;, by typing in a console, inherits the filtered token as its primary token. The dormant full token sits in the LSA, addressable through &lt;code&gt;TokenLinkedToken&lt;/code&gt;. The verbatim Microsoft Learn statement is exact: &quot;When an administrator logs on, two separate access tokens are created for the user: a standard user access token and an administrator access token&quot; [@uac-how-it-works].&lt;/p&gt;

flowchart TD
    A[&quot;User authenticates&lt;br /&gt;as a member of Administrators&quot;] --&amp;gt; B[&quot;LSA logon processing&lt;br /&gt;(LsaLogonUser)&quot;]
    B --&amp;gt; C[&quot;Full token&lt;br /&gt;(admin SIDs enabled, all privileges, IL = High)&quot;]
    B --&amp;gt; D[&quot;Filtered token&lt;br /&gt;(admin SIDs deny-only, privileges stripped, IL = Medium)&quot;]
    C -.-&amp;gt;|&quot;linked via TokenLinkedToken&quot;| D
    D --&amp;gt; E[&quot;Primary token of explorer.exe&lt;br /&gt;and all interactive child processes&quot;]
    C --&amp;gt; F[&quot;Dormant in LSA, used only by Appinfo after consent or auto-elevation&quot;]
&lt;h3&gt;The TokenElevationType API surface&lt;/h3&gt;
&lt;p&gt;Three values of the &lt;code&gt;TOKEN_ELEVATION_TYPE&lt;/code&gt; enumeration describe what state the current process is in [@token-elevation-type].&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TokenElevationTypeDefault&lt;/code&gt; (1) -- no split-token policy is in effect for this token. This is the legacy case (&lt;code&gt;EnableLUA=0&lt;/code&gt;) or the case where the user is not a member of any administrators-equivalent group at all. The single token &lt;em&gt;is&lt;/em&gt; 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.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TokenElevationTypeFull&lt;/code&gt; (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).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TokenElevationTypeLimited&lt;/code&gt; (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.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;TokenElevationTypeDefault&lt;/code&gt; (value 1) is the legacy or domain-controller case in which &lt;code&gt;EnableLUA=0&lt;/code&gt; and the user has no filtered token at all. On a default consumer Windows install, administrators are always &lt;code&gt;TokenElevationTypeLimited&lt;/code&gt; or &lt;code&gt;TokenElevationTypeFull&lt;/code&gt;, never &lt;code&gt;Default&lt;/code&gt;. The &lt;code&gt;Default&lt;/code&gt; case is what reverting &lt;code&gt;EnableLUA&lt;/code&gt; to 0 produces, and it is the configuration the FAQ in §11 warns against.&lt;/p&gt;
&lt;h3&gt;What the consent prompt actually does&lt;/h3&gt;
&lt;p&gt;The behaviour of the consent prompt now resolves to a single operation, and the operation is not &quot;elevate.&quot; When the user invokes &quot;Run as administrator&quot; on a binary, the shell calls &lt;code&gt;ShellExecuteEx&lt;/code&gt; with the &lt;code&gt;&quot;runas&quot;&lt;/code&gt; verb [@shellexecuteexa]. The Application Information service (the topic of §6.2) receives the request via RPC. Appinfo, running as &lt;code&gt;LocalSystem&lt;/code&gt;, retrieves the linked full token of the calling user via &lt;code&gt;TokenLinkedToken&lt;/code&gt;. Appinfo shows the consent prompt on the Secure Desktop (§6.1). If the user clicks &quot;Yes,&quot; Appinfo creates a new process using the full token as the new process&apos;s primary token, by calling &lt;code&gt;CreateProcessAsUser&lt;/code&gt; with the privileges Appinfo holds because it is &lt;code&gt;LocalSystem&lt;/code&gt; [@russinovich-tnm-2007].&lt;/p&gt;
&lt;p&gt;The bits that move are the kernel-level handle for the new process and the assignment of the linked token as that process&apos;s primary token. The bits the prompt itself moves are zero. The prompt is the consent surface; the token swap is the primitive.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;

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-&amp;gt;&amp;gt;E: Right-click, Run as administrator
    E-&amp;gt;&amp;gt;A: RPC: request elevation of target.exe
    A-&amp;gt;&amp;gt;A: Look up TokenLinkedToken of caller&apos;s filtered token
    A-&amp;gt;&amp;gt;C: Show consent prompt
    U-&amp;gt;&amp;gt;C: Click Yes
    C--&amp;gt;&amp;gt;A: Consent granted
    A-&amp;gt;&amp;gt;P: CreateProcessAsUser(linked full token, target.exe)
    Note over P: New process runs at High IL, full admin SIDs enabled, full privileges

Split-token administrator in UAC just means MS get to annoy you with prompts unnecessarily but serves very little, if not zero security benefit. -- James Forshaw, *Reading Your Way Around UAC (Part 1)*, Tyranid&apos;s Lair, May 2017
&lt;p&gt;Forshaw&apos;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 &lt;em&gt;same user SID&lt;/em&gt;. They write to the &lt;em&gt;same &lt;code&gt;%USERPROFILE%&lt;/code&gt;&lt;/em&gt;. They consult the &lt;em&gt;same &lt;code&gt;HKCU&lt;/code&gt; registry hive&lt;/em&gt;. They live in the &lt;em&gt;same logon-session LUID&lt;/em&gt;. 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.&lt;/p&gt;
&lt;p&gt;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?&lt;/p&gt;
&lt;h2&gt;6. The Full UAC Stack on a Modern Windows Box&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;6.1 The Secure Desktop, not Session 0&lt;/h3&gt;

A separate desktop object at the Object-Manager path `\Sessions\\Windows\WindowStations\WinSta0\Winlogon`, within the user&apos;s interactive session, on which `consent.exe` runs the UAC prompt. Isolated from the user&apos;s `Default` desktop by Object-Manager DACL and the `SwitchDesktop` API.
&lt;p&gt;When you click &quot;Run as administrator&quot; and the screen dims and the prompt appears, the screen dims because you have just been switched to a different &lt;em&gt;desktop&lt;/em&gt;. Not a different session, not Session 0, not a different window station. A different desktop within the same window station, accessed through the &lt;code&gt;SwitchDesktop&lt;/code&gt; API [@russinovich-tnm-2007].&lt;/p&gt;
&lt;p&gt;The Object-Manager path is exact. Inside the user&apos;s interactive session (Session 1 if the user is the first interactive logon, higher numbers for subsequent users), there is a window station named &lt;code&gt;WinSta0&lt;/code&gt;. Inside &lt;code&gt;WinSta0&lt;/code&gt; there are several desktop objects: &lt;code&gt;Default&lt;/code&gt; (where the user&apos;s normal interactive processes paint), &lt;code&gt;Winlogon&lt;/code&gt; (where &lt;code&gt;consent.exe&lt;/code&gt; runs the prompt), and &lt;code&gt;Disconnect&lt;/code&gt; and &lt;code&gt;Screen-saver&lt;/code&gt; for related uses. The full path of the Secure Desktop is &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\Windows\WindowStations\WinSta0\Winlogon&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Winlogon&lt;/code&gt; desktop is protected by an Object-Manager DACL that the user&apos;s normal interactive processes (running on &lt;code&gt;Default&lt;/code&gt;) cannot open for &lt;code&gt;DESKTOP_CREATEWINDOW&lt;/code&gt; or &lt;code&gt;DESKTOP_HOOKCONTROL&lt;/code&gt;. A Medium-IL malware process on &lt;code&gt;Default&lt;/code&gt; cannot draw into the &lt;code&gt;Winlogon&lt;/code&gt; desktop, cannot enumerate its windows, and cannot send messages to them. The OS performs the desktop switch in &lt;code&gt;win32k.sys&lt;/code&gt; and renders &lt;code&gt;consent.exe&lt;/code&gt;&apos;s window on the new desktop with a snapshot of the previous desktop as a dimmed background, so the user has visual continuity but &lt;code&gt;consent.exe&lt;/code&gt; is the only process accepting input [@russinovich-tnm-2007].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Secure Desktop is &lt;em&gt;not&lt;/em&gt; 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 &lt;em&gt;within&lt;/em&gt; the user&apos;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&apos;s desktop, and the Secure Desktop prevents the user&apos;s processes from drawing on the prompt&apos;s desktop. Conflating them mis-describes how either one works. The corpus&apos;s &lt;a href=&quot;https://paragmali.com/blog/the-object-manager-namespace/&quot; rel=&quot;noopener&quot;&gt;Object Manager Namespace&lt;/a&gt; article (#46) covers Session 0 Isolation directly; this article treats only the Secure Desktop.&lt;/p&gt;
&lt;/blockquote&gt;

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.

flowchart TD
    A[&quot;Session 1&lt;br /&gt;(interactive logon for user &apos;admin&apos;)&quot;] --&amp;gt; B[&quot;WinSta0&lt;br /&gt;interactive window station&quot;]
    A --&amp;gt; C[&quot;Service-0x0-3e7$&lt;br /&gt;non-interactive WinSta (services)&quot;]
    B --&amp;gt; D[&quot;Default desktop&lt;br /&gt;explorer.exe, browsers, console windows&lt;br /&gt;(Medium IL processes)&quot;]
    B --&amp;gt; E[&quot;Winlogon desktop&lt;br /&gt;consent.exe renders here&lt;br /&gt;(Secure Desktop)&quot;]
    B --&amp;gt; F[&quot;Disconnect / Screen-saver&lt;br /&gt;desktops&quot;]
    D -. &quot;blocked by Object-Manager DACL&lt;br /&gt;and SwitchDesktop&quot; .-&amp;gt; E
&lt;p&gt;The Secure Desktop addresses UI spoofing and input injection against the prompt itself. It does not address whether elevation can happen &lt;em&gt;without&lt;/em&gt; a Secure Desktop prompt; that is the territory of the auto-elevation allowlist (§6.4) and of the bypass-research class (§7).&lt;/p&gt;
&lt;h3&gt;6.2 The Application Information service (Appinfo)&lt;/h3&gt;

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: &quot;Run as administrator&quot; fails without it. The modern process-creation entry point is `RAiLaunchAdminProcess`.
&lt;p&gt;Every UAC elevation on Windows goes through one service: Appinfo (display name &quot;Application Information&quot;). Its image is &lt;code&gt;C:\Windows\System32\appinfo.dll&lt;/code&gt;, loaded into a shared &lt;code&gt;svchost.exe&lt;/code&gt; host process, running as &lt;code&gt;LocalSystem&lt;/code&gt; [@russinovich-tnm-2007].&lt;/p&gt;
&lt;p&gt;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: &lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt; and &lt;code&gt;SeIncreaseQuotaPrivilege&lt;/code&gt;. &lt;code&gt;LocalSystem&lt;/code&gt; has both [@russinovich-tnm-2007]. The broker therefore has to run as &lt;code&gt;LocalSystem&lt;/code&gt;, and that is what Appinfo is for.&lt;/p&gt;
&lt;p&gt;The modern entry point on &lt;a href=&quot;https://paragmali.com/blog/every-uac-prompt-is-an-alpc-handshake-a-field-guide-to-windo/&quot; rel=&quot;noopener&quot;&gt;Appinfo&apos;s RPC interface&lt;/a&gt; is &lt;code&gt;RAiLaunchAdminProcess&lt;/code&gt;, documented verbatim in Forshaw&apos;s February 2026 Project Zero post on Administrator Protection [@forshaw-adminprot-feb26]. The Medium-IL caller invokes &lt;code&gt;ShellExecuteEx&lt;/code&gt; with &lt;code&gt;&quot;runas&quot;&lt;/code&gt;; the shell marshalls the request across to Appinfo; Appinfo retrieves the caller&apos;s &lt;code&gt;TokenLinkedToken&lt;/code&gt;; if a prompt is needed, Appinfo shows &lt;code&gt;consent.exe&lt;/code&gt; on the Secure Desktop; if the user clicks &quot;Yes,&quot; Appinfo calls &lt;code&gt;RAiLaunchAdminProcess&lt;/code&gt; to create the new process under the linked full token.&lt;/p&gt;
&lt;p&gt;Disable Appinfo and &quot;Run as administrator&quot; 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).&lt;/p&gt;
&lt;h3&gt;6.3 Two activation surfaces&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When you say &quot;elevate a thing,&quot; the operating system understands &lt;em&gt;two&lt;/em&gt; distinct primitives, not one. &lt;code&gt;ShellExecuteEx &quot;runas&quot;&lt;/code&gt; 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 &lt;code&gt;dllhost.exe&lt;/code&gt; that exposes exactly one COM CLSID&apos;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.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first activation surface is &lt;code&gt;ShellExecuteEx&lt;/code&gt; with the &lt;code&gt;&quot;runas&quot;&lt;/code&gt; verb. The OS launches &lt;code&gt;consent.exe&lt;/code&gt;, asks the user, and if approved, Appinfo creates a brand-new process under the caller&apos;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 &quot;Run as administrator&quot; context menu uses this verb. So does the &lt;code&gt;runas /trustlevel:&lt;/code&gt; command. So does any program that calls &lt;code&gt;ShellExecuteEx&lt;/code&gt; and sets the &lt;code&gt;lpVerb&lt;/code&gt; member of &lt;code&gt;SHELLEXECUTEINFO&lt;/code&gt; to the string &lt;code&gt;&quot;runas&quot;&lt;/code&gt; [@shellexecuteexa].&lt;/p&gt;

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&apos;s methods at High IL. Per-object elevation, distinct from `ShellExecuteEx &quot;runas&quot;` whole-process elevation.
&lt;p&gt;The second activation surface is the COM Elevation Moniker. A Medium-IL caller invokes &lt;code&gt;CoGetObject&lt;/code&gt; (or &lt;code&gt;CoCreateInstance&lt;/code&gt; via a moniker) with the display name &lt;code&gt;&quot;Elevation:Administrator!new:{CLSID}&quot;&lt;/code&gt; (or &lt;code&gt;&quot;Elevation:Highest!new:{CLSID}&quot;&lt;/code&gt;). This asks the OS to instantiate a &lt;em&gt;single COM out-of-process server&lt;/em&gt; in a new elevated &lt;code&gt;dllhost.exe&lt;/code&gt; host process, exposing only that one CLSID&apos;s methods at High IL. The caller stays at Medium. Only the COM object&apos;s host process is elevated, and only for the lifetime of the object [@com-elevation-moniker].&lt;/p&gt;
&lt;p&gt;The semantics are deliberately narrow. The COM Elevation Moniker requires the target CLSID to opt in via two registry values under &lt;code&gt;HKCR\CLSID\{CLSID}&lt;/code&gt;: &lt;code&gt;Elevation\Enabled = 1&lt;/code&gt; and an &lt;code&gt;LocalizedString&lt;/code&gt; value that names the elevation prompt&apos;s display string. Not every COM class is moniker-eligible; the registry enables elevation per CLSID.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;&lt;code&gt;ShellExecuteEx &quot;runas&quot;&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;COM Elevation Moniker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Granularity&lt;/td&gt;
&lt;td&gt;Whole process&lt;/td&gt;
&lt;td&gt;One COM object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifetime&lt;/td&gt;
&lt;td&gt;Entire process lifetime&lt;/td&gt;
&lt;td&gt;Object lifetime only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caller IL after&lt;/td&gt;
&lt;td&gt;Caller stays Medium; new process High&lt;/td&gt;
&lt;td&gt;Caller stays Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New process&lt;/td&gt;
&lt;td&gt;Target executable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dllhost.exe&lt;/code&gt; host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authority surface&lt;/td&gt;
&lt;td&gt;All admin SIDs and privileges, broad&lt;/td&gt;
&lt;td&gt;Methods of one CLSID, narrow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical use&lt;/td&gt;
&lt;td&gt;&quot;Run as administrator&quot; context menu, MSI installers&lt;/td&gt;
&lt;td&gt;Programmatic file copy, Wmi management, registry edits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary canonical bypass class&lt;/td&gt;
&lt;td&gt;DLL-search-order against the new process&lt;/td&gt;
&lt;td&gt;Auto-elevated COM behaviour abuse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The distinction matters because most of the canonical UAC bypasses do not touch &lt;code&gt;ShellExecuteEx &quot;runas&quot;&lt;/code&gt; at all. Leo Davidson&apos;s December 2009 essay attacked the COM Elevation Moniker by invoking the &lt;code&gt;IFileOperation&lt;/code&gt; COM class (auto-elevation-eligible, registered under the right CLSID) from a Medium-IL caller, and using its &lt;code&gt;CopyItem&lt;/code&gt; method to overwrite a system file at High IL [@davidson-2009][@ifileoperation]. The &lt;code&gt;ICMLuaUtil&lt;/code&gt; and &lt;code&gt;IColorDataProxy&lt;/code&gt; 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].&lt;/p&gt;
&lt;p&gt;Both surfaces share the same backend: Appinfo brokers the token swap, and &lt;code&gt;RAiLaunchAdminProcess&lt;/code&gt; (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&apos;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 &lt;em&gt;behavioural&lt;/em&gt; surface: the CLSID&apos;s methods.&lt;/p&gt;
&lt;h3&gt;6.4 The auto-elevation allowlist&lt;/h3&gt;
&lt;p&gt;Vista&apos;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 &quot;bypass&quot; you have ever read about lives inside the gap between &lt;em&gt;which binary&lt;/em&gt; gets elevated and &lt;em&gt;what the binary does after elevation&lt;/em&gt;.&lt;/p&gt;

The set of Microsoft-signed binaries in trusted system directories on Appinfo&apos;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`.
&lt;p&gt;The manifest element is a single string. Inside the application&apos;s side-by-side manifest, under the &lt;code&gt;&amp;lt;trustInfo&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;security&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;requestedPrivileges&amp;gt;&lt;/code&gt; element, the binary asserts &lt;code&gt;&amp;lt;autoElevate&amp;gt;true&amp;lt;/autoElevate&amp;gt;&lt;/code&gt; [@app-manifests]. That assertion was discovered and publicly documented by independent UK developer Leo Davidson in December 2009 [@davidson-2009].&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;autoElevate=true&lt;/code&gt; manifest assertion is &lt;em&gt;necessary but not sufficient&lt;/em&gt;. Appinfo enforces three additional gating conditions before honouring an auto-elevation request [@davidson-2009].&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The binary must carry a valid Authenticode signature chained to a Microsoft root certificate.&lt;/li&gt;
&lt;li&gt;The binary&apos;s path must reside under a trusted system directory, in practice &lt;code&gt;%SystemRoot%\System32&lt;/code&gt; or &lt;code&gt;%SystemRoot%\SysWOW64&lt;/code&gt; (or the localized variants for non-English locales).&lt;/li&gt;
&lt;li&gt;The binary&apos;s name must appear on an internal allowlist enforced in code in &lt;code&gt;appinfo.dll&lt;/code&gt;, not in any user-visible policy file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The fourth gate (the internal allowlist) is the one that surprises practitioners. A binary can be Microsoft-signed, located in &lt;code&gt;System32&lt;/code&gt;, and carry &lt;code&gt;autoElevate=true&lt;/code&gt; in its manifest, and Appinfo can still refuse to auto-elevate it, because the binary&apos;s name is not on the hard-coded allowlist inside &lt;code&gt;appinfo.dll&lt;/code&gt;. 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 &lt;code&gt;System32&lt;/code&gt; and cross-check which ones actually auto-elevate.&lt;/p&gt;

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.&lt;p&gt;The list of &lt;em&gt;names&lt;/em&gt; in the manifest is not the same as the set Appinfo actually auto-elevates. UACMe&apos;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.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;{`
// Pseudocode of Appinfo&apos;s auto-elevation decision (Win7+).
// All four gates must pass for auto-elevation without a consent prompt.&lt;/p&gt;
&lt;p&gt;function shouldAutoElevate(binaryPath) {
  // Gate 1: the application manifest must assert autoElevate=true.
  const manifest = readEmbeddedManifest(binaryPath);
  if (manifest?.requestedPrivileges?.autoElevate !== true) return false;&lt;/p&gt;
&lt;p&gt;  // Gate 2: the binary must carry a valid Microsoft Authenticode signature.
  const sig = verifyAuthenticodeSignature(binaryPath);
  if (sig.status !== &apos;valid&apos; || sig.rootCA !== &apos;Microsoft&apos;) return false;&lt;/p&gt;
&lt;p&gt;  // Gate 3: the binary must reside under a trusted system directory.
  const trustedDirs = [&apos;C:\\Windows\\System32\\&apos;, &apos;C:\\Windows\\SysWOW64\\&apos;];
  if (!trustedDirs.some(d =&amp;gt; binaryPath.toLowerCase().startsWith(d.toLowerCase()))) return false;&lt;/p&gt;
&lt;p&gt;  // Gate 4: the binary name must appear on Appinfo&apos;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;&lt;/p&gt;
&lt;p&gt;  return true;
}
`}&lt;/p&gt;
&lt;p&gt;Four gating conditions. Three of them constrain &lt;em&gt;which binary&lt;/em&gt; gets elevated. None of them constrain &lt;em&gt;what the binary does after elevation&lt;/em&gt;. The fourth gap, the behavioural one, is the space the bypass-research industry has occupied for fifteen years. That is §7.&lt;/p&gt;
&lt;h2&gt;7. Twenty Years of Bypass Research as Empirical Test&lt;/h2&gt;
&lt;p&gt;In February 2007, eleven days after Vista&apos;s consumer launch, Mark Russinovich published a TechNet Blogs post titled &lt;em&gt;PsExec, User Account Control and Security Boundaries&lt;/em&gt;. The post walked through a quirk of how PsExec&apos;s &lt;code&gt;-l&lt;/code&gt; switch interacted with restricted tokens on Windows XP, used the walkthrough to introduce Vista&apos;s integrity-level model, and then dropped a single sentence the entire later debate would rest on [@russinovich-blog-2007].&lt;/p&gt;

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
&lt;p&gt;That sentence, in the public record from week one, is the architectural reason every &quot;UAC bypass&quot; 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.&lt;/p&gt;
&lt;h3&gt;7.1 The &lt;code&gt;ms-settings&lt;/code&gt; / &lt;code&gt;DelegateExecute&lt;/code&gt; registry-hijack class&lt;/h3&gt;
&lt;p&gt;The first durable class is the registry-hijack bypass of auto-elevated binaries. Mechanism: an auto-elevated binary (&lt;code&gt;eventvwr.exe&lt;/code&gt;, &lt;code&gt;fodhelper.exe&lt;/code&gt;, &lt;code&gt;ComputerDefaults.exe&lt;/code&gt;, certain &lt;code&gt;sdclt.exe&lt;/code&gt; variants) executes a handler for a custom file extension or URL protocol on launch. The relevant handler mapping is in &lt;code&gt;HKCR&lt;/code&gt;, but Windows resolves &lt;code&gt;HKCR&lt;/code&gt; by first consulting &lt;code&gt;HKCU\Software\Classes&lt;/code&gt; and only falling back to &lt;code&gt;HKLM\Software\Classes&lt;/code&gt; if no per-user mapping exists. A Medium-IL user can write to &lt;code&gt;HKCU&lt;/code&gt; without elevation. So the user writes a &lt;code&gt;HKCU\Software\Classes\&amp;lt;scheme&amp;gt;\shell\open\command&lt;/code&gt; key whose default value is an arbitrary command line and whose &lt;code&gt;DelegateExecute&lt;/code&gt; 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 &lt;code&gt;HKCU\Software\Classes&lt;/code&gt; first, finds the attacker-controlled command line, and executes it. The attacker&apos;s command runs at High IL [@enigma-eventvwr][@mitre-t1548002].&lt;/p&gt;
&lt;p&gt;The first public canonical demonstration was Matt Nelson&apos;s August 15, 2016 post &lt;em&gt;Fileless UAC Bypass Using eventvwr.exe and Registry Hijacking&lt;/em&gt;, published on his blog under the handle &lt;code&gt;enigma0x3&lt;/code&gt;. Nelson hijacked the &lt;code&gt;mscfile&lt;/code&gt; association by writing &lt;code&gt;HKCU\Software\Classes\mscfile\shell\open\command&lt;/code&gt; with &lt;code&gt;cmd.exe&lt;/code&gt; as the default value, then launched &lt;code&gt;eventvwr.exe&lt;/code&gt;. The Event Viewer auto-elevates because of its manifest, resolves the &lt;code&gt;mscfile&lt;/code&gt; association to load &lt;code&gt;eventvwr.msc&lt;/code&gt;, walks the HKCU mapping first, finds &lt;code&gt;cmd.exe&lt;/code&gt; instead of &lt;code&gt;mmc.exe&lt;/code&gt;, and launches an attacker-controlled &lt;code&gt;cmd.exe&lt;/code&gt; at High IL [@enigma-eventvwr]. The technique required no file on disk except the registry value itself; this is what &lt;em&gt;fileless&lt;/em&gt; means in this context.&lt;/p&gt;
&lt;p&gt;Nelson productised the class through 2017. The March 14, 2017 &lt;em&gt;Bypassing UAC Using App Paths&lt;/em&gt; post generalised to &lt;code&gt;HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\control.exe&lt;/code&gt;, exploited by &lt;code&gt;sdclt.exe&lt;/code&gt; [@enigma-apppaths]. The March 17, 2017 &lt;em&gt;&apos;Fileless&apos; UAC Bypass Using sdclt.exe&lt;/em&gt; post showed a fileless variant of the same attack using the &lt;code&gt;IsolatedCommand&lt;/code&gt; REG_SZ value on &lt;code&gt;HKCU:\Software\Classes\Folder\shell\open\command&lt;/code&gt;, with &lt;code&gt;sdclt.exe /KickOffElev&lt;/code&gt; as the trigger [@enigma-sdclt]. The same post referenced WikiLeaks&apos;s March 2017 Vault7 disclosures, in which the CIA&apos;s &quot;Vault7&quot; cache contained operationalised versions of the technique, confirming nation-state adoption of the bypass class [@enigma-sdclt].&lt;/p&gt;
&lt;p&gt;The fodhelper variant was published on May 12, 2017 under the title &lt;em&gt;Bypassing UAC Using fodhelper.exe&lt;/em&gt;. The canonical URL &lt;code&gt;enigma0x3.net/2017/05/12/bypassing-uac-using-fodhelper-exe/&lt;/code&gt; currently returns HTTP 404; the technique is anchored by UACMe Method 33 and MITRE ATT&amp;amp;CK T1548.002, both of which preserve the date, author, and registry path verbatim [@uacme][@mitre-t1548002].&lt;/p&gt;

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-&amp;gt;&amp;gt;R: Write HKCU Software Classes ms-settings shell open command with attacker cmd
    U-&amp;gt;&amp;gt;F: ShellExecute(&quot;fodhelper.exe&quot;)
    F-&amp;gt;&amp;gt;A: Request elevation (autoElevate gate passes)
    A-&amp;gt;&amp;gt;F: New process at High IL, no consent prompt
    F-&amp;gt;&amp;gt;R: Resolve ms-settings handler via HKCU first
    R--&amp;gt;&amp;gt;F: Returns attacker command
    F-&amp;gt;&amp;gt;C: Spawn attacker payload at High IL
&lt;p&gt;Microsoft&apos;s response to the eventvwr bypass was to ship a fix in the Windows 10 Creators Update (1703) that made &lt;code&gt;eventvwr.exe&lt;/code&gt; not consult the registered association the technique exploited. The fix was &lt;em&gt;technique-specific&lt;/em&gt;, not class-specific: the &lt;code&gt;ms-settings&lt;/code&gt; (fodhelper), App Paths (sdclt), and &lt;code&gt;IsolatedCommand&lt;/code&gt; (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].&lt;/p&gt;
&lt;h3&gt;7.2 The DLL-search-order class&lt;/h3&gt;
&lt;p&gt;The second durable class is the DLL-search-order attack against auto-elevated binaries. Mechanism: an auto-elevated binary calls &lt;code&gt;LoadLibrary&lt;/code&gt; on a DLL name resolved via the standard Windows search order: the application directory, the system directory, the current directory, the &lt;code&gt;PATH&lt;/code&gt; 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&apos;s DLL first, and the DLL is loaded at the binary&apos;s elevated IL [@davidson-2009].&lt;/p&gt;
&lt;p&gt;The foundational canonical example is the December 2009 Leo Davidson essay &lt;em&gt;Windows 7 UAC whitelist: Code injection issue (and more)&lt;/em&gt;. Davidson demonstrated that &lt;code&gt;sysprep.exe&lt;/code&gt; (Microsoft-signed, in &lt;code&gt;System32&lt;/code&gt;, auto-elevation-allowlisted) loads &lt;code&gt;cryptbase.dll&lt;/code&gt; from its working directory before the system directory. By copying &lt;code&gt;sysprep.exe&lt;/code&gt; and a malicious &lt;code&gt;cryptbase.dll&lt;/code&gt; into a writable directory and launching &lt;code&gt;sysprep.exe&lt;/code&gt; from there, an attacker could load the malicious DLL into a High-IL process [@davidson-2009]. The same essay introduced the &lt;code&gt;IFileOperation&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;Coverage in the trade press confirmed the class&apos;s significance immediately. In February 2009, &lt;em&gt;The Register&lt;/em&gt; reported on a related Long Zheng / Rafael Rivera disclosure that demonstrated piggybacking on auto-elevation via &lt;code&gt;rundll32.exe&lt;/code&gt; [@register-2009], establishing that the auto-elevation surface had been understood as exploitable from the moment Windows 7 shipped.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s mitigations against the DLL-search-order class have been incremental. &lt;code&gt;SafeDllSearchMode&lt;/code&gt; was made the default in Windows XP SP2 and reshuffled the search order so the application directory came before the current directory. The &lt;code&gt;LOAD_LIBRARY_SEARCH_*&lt;/code&gt; flags introduced in Windows Vista let applications opt into stricter search behaviour. Side-by-side manifest pinning and the &lt;code&gt;KnownDLLs&lt;/code&gt; 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&apos;s catalogue of 81 methods includes numerous DLL-search-order entries across Windows versions [@uacme].&lt;/p&gt;
&lt;h3&gt;7.3 The auto-elevated COM-object behaviour-abuse class&lt;/h3&gt;
&lt;p&gt;The third durable class abuses the &lt;em&gt;behaviour&lt;/em&gt; of auto-elevation-eligible COM classes. Mechanism: a COM class registered as auto-elevation-eligible (the &lt;code&gt;IFileOperation&lt;/code&gt; / &lt;code&gt;ICMLuaUtil&lt;/code&gt; / &lt;code&gt;IColorDataProxy&lt;/code&gt; family historically, then the explicit &lt;code&gt;COMAutoApprovalList&lt;/code&gt; 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].&lt;/p&gt;
&lt;p&gt;Davidson&apos;s &lt;code&gt;IFileOperation&lt;/code&gt; proof of concept from December 2009 is the canonical example. A Medium-IL caller instantiates &lt;code&gt;IFileOperation&lt;/code&gt; via the COM Elevation Moniker. The resulting &lt;code&gt;dllhost.exe&lt;/code&gt; runs at High IL and exposes &lt;code&gt;IFileOperation::CopyItem&lt;/code&gt; and related methods. The caller invokes &lt;code&gt;CopyItem(&quot;evil.dll&quot;, &quot;C:\\Windows\\System32\\&quot;)&lt;/code&gt;. The High-IL &lt;code&gt;dllhost.exe&lt;/code&gt; performs the copy, because the High-IL token has write access to &lt;code&gt;%SystemRoot%\System32&lt;/code&gt;. The caller has now planted a DLL in &lt;code&gt;System32&lt;/code&gt; without ever holding a High-IL token itself [@davidson-2009][@ifileoperation].&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;COMAutoApprovalList&lt;/code&gt; era began in August 2016 with the Windows 10 Anniversary Update (RS1, build 14393). Microsoft added a dedicated registry surface at &lt;code&gt;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList&lt;/code&gt; enumerating which CLSIDs &lt;code&gt;consent.exe&lt;/code&gt; 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 &quot;Side effect of consent.exe COMAutoApprovalList introduction&quot; against the &lt;code&gt;TpmInit.exe&lt;/code&gt; &lt;code&gt;ICreateNewLink&lt;/code&gt; technique, dated to RS1 / build 14393 [@uacme]. Method 27 captures the subsequent narrowing in RS3 / 16299, when Microsoft removed the &lt;code&gt;UninstallStringLauncher&lt;/code&gt; interface from the list.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Canonical research&lt;/th&gt;
&lt;th&gt;Microsoft response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Registry-hijack (DelegateExecute)&lt;/td&gt;
&lt;td&gt;Auto-elevated binary resolves user-writable HKCU handler&lt;/td&gt;
&lt;td&gt;Nelson, eventvwr Aug 2016; sdclt and fodhelper 2017&lt;/td&gt;
&lt;td&gt;Patched individual binaries; class never classified as security vulnerability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DLL-search-order&lt;/td&gt;
&lt;td&gt;Auto-elevated binary loads attacker DLL via standard search path&lt;/td&gt;
&lt;td&gt;Davidson, December 2009 (sysprep + cryptbase)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SafeDllSearchMode&lt;/code&gt;, &lt;code&gt;LOAD_LIBRARY_SEARCH_*&lt;/code&gt;, KnownDLLs; shrunk but not eliminated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-elevated COM behaviour&lt;/td&gt;
&lt;td&gt;Medium-IL caller invokes High-IL methods via moniker&lt;/td&gt;
&lt;td&gt;Davidson, December 2009 (IFileOperation); COMAutoApprovalList RS1 Aug 2016&lt;/td&gt;
&lt;td&gt;Curated allowlist; entries added or removed in feature updates without CVEs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;7.4 The doctrine and the aha&lt;/h3&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The verbatim &quot;Neither UAC elevations nor Protected Mode IE define new Windows security boundaries&quot; sentence lives in the &lt;em&gt;PsExec, User Account Control and Security Boundaries&lt;/em&gt; TechNet Blogs post by Mark Russinovich, dated February 12, 2007 [@russinovich-blog-2007]. The architectural reference that most practitioners cite, &lt;em&gt;Security: Inside Windows Vista User Account Control&lt;/em&gt;, was published in the June 2007 issue of &lt;em&gt;TechNet Magazine&lt;/em&gt; 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 &quot;not a security boundary&quot; 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.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Microsoft Security Response Center&apos;s &lt;a href=&quot;https://paragmali.com/blog/windows-security-boundaries-the-document-that-decides-what-g/&quot; rel=&quot;noopener&quot;&gt;published servicing criteria&lt;/a&gt; define a security boundary as one that &quot;provides a logical separation between the code and data of security domains with different levels of trust&quot; [@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 &lt;em&gt;feature&lt;/em&gt;, 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].&lt;/p&gt;
&lt;p&gt;Forshaw&apos;s January 2026 Project Zero post on Administrator Protection reads the doctrine clearly in retrospect: &quot;due to the way it was designed, it was quickly apparent it didn&apos;t represent a hard security boundary, and Microsoft downgraded it to a security feature&quot; [@forshaw-adminprot-jan26]. The &quot;downgraded&quot; framing is exact. UAC was &lt;em&gt;promoted&lt;/em&gt; to a security feature, &lt;em&gt;not&lt;/em&gt; a security boundary, from the moment it shipped. The reclassification in November 2024 was a &lt;em&gt;re-promotion&lt;/em&gt; with new architecture, not a fix to the old architecture.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The twenty-year UAC bypass-research record is empirical confirmation, not counterargument, of the architect&apos;s 2007 disclaimer. Microsoft did not fix the bypasses as security vulnerabilities because Russinovich had already said in writing that there was nothing to &quot;fix&quot;: the consent prompt was a convenience, not a boundary. The bypass record is the proof that the disclaimer was honest from week one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;If MIC, UIPI, and the split-token model are sound primitives, and the bypasses do not violate Microsoft&apos;s own classification of them, what are the actual theoretical limits of integrity-level systems? What can MIC and UIPI never do, by design?&lt;/p&gt;
&lt;h2&gt;8. Theoretical Limits: What MIC and UIPI Cannot Do, by Design&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;Biba 1977 and the three rules&lt;/h3&gt;
&lt;p&gt;The integrity model MIC implements comes from Kenneth J. Biba&apos;s 1977 MITRE technical report MTR-3153 [@biba-wiki]. Biba&apos;s model is the integrity-side mirror of the better-known Bell-LaPadula confidentiality model [@blp-wiki]: where Bell-LaPadula&apos;s &quot;no read up&quot; prevents confidentiality leaks, Biba&apos;s &quot;no write up&quot; prevents integrity contamination. The Biba model defines three rules.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simple Integrity Property&lt;/strong&gt; (&lt;em&gt;no read down&lt;/em&gt;): a subject at integrity level $I_s$ cannot read an object at integrity level $I_o &amp;lt; 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&apos;s state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Star Integrity Property&lt;/strong&gt; (&lt;em&gt;no write up&lt;/em&gt;): a subject at integrity level $I_s$ cannot write an object at integrity level $I_o &amp;gt; I_s$. A Low-IL subject cannot write to a High-IL object, because the Low-IL subject&apos;s writes would degrade the High-IL object&apos;s integrity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invocation Property&lt;/strong&gt;: a subject at integrity level $I_s$ cannot invoke (call, request services from) a subject at integrity level $I_o &amp;gt; I_s$. A Low-IL caller cannot ask a High-IL server to perform an action on the caller&apos;s behalf, because the High-IL server would then act on Low-IL inputs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MIC implements the Star Integrity Property as the &lt;em&gt;default&lt;/em&gt; &lt;code&gt;NO_WRITE_UP&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;MIC does &lt;em&gt;not&lt;/em&gt; implement Biba&apos;s Simple Integrity Property at all. There is no &lt;code&gt;NO_READ_DOWN&lt;/code&gt; policy in the &lt;code&gt;winnt.h&lt;/code&gt; mandatory-label-policy enumeration. The opt-in &lt;code&gt;NO_READ_UP&lt;/code&gt; bit MIC exposes points the other way: it stops a &lt;em&gt;lower&lt;/em&gt;-IL subject from reading a &lt;em&gt;higher&lt;/em&gt;-IL object, which is structurally Bell-LaPadula&apos;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&apos;s &lt;em&gt;Reading Your Way Around UAC&lt;/em&gt; series turned into a research program in 2017 [@forshaw-reading-uac].&lt;/p&gt;
&lt;p&gt;MIC does &lt;em&gt;not&lt;/em&gt; implement the Invocation Property either. A Medium-IL process can invoke a High-IL service via the COM Elevation Moniker, via &lt;code&gt;ShellExecuteEx &quot;runas&quot;&lt;/code&gt;, 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 &quot;bypass&quot; of UAC is technically a &lt;em&gt;use&lt;/em&gt; of an architectural surface, not a violation of it.&lt;/p&gt;

flowchart LR
    subgraph BIBA [&quot;Biba 1977 -- integrity model&quot;]
        A[&quot;Biba 1977&lt;br /&gt;integrity model&quot;] --&amp;gt; B[&quot;Simple Integrity&lt;br /&gt;(no read down)&quot;]
        A --&amp;gt; C[&quot;Star Integrity&lt;br /&gt;(no write up)&quot;]
        A --&amp;gt; D[&quot;Invocation Property&lt;br /&gt;(no invoke up)&quot;]
        B --&amp;gt; E[&quot;MIC: not implemented&lt;br /&gt;(no NO_READ_DOWN policy in winnt.h)&quot;]
        C --&amp;gt; F[&quot;MIC: default NO_WRITE_UP&lt;br /&gt;(on by default)&quot;]
        D --&amp;gt; G[&quot;MIC: not implemented&lt;br /&gt;(COM moniker, runas verb, Appinfo)&quot;]
    end
    subgraph BLP [&quot;Bell-LaPadula 1973 -- confidentiality model&quot;]
        H[&quot;Bell-LaPadula 1973&lt;br /&gt;confidentiality model&quot;] --&amp;gt; I[&quot;Simple Security&lt;br /&gt;(no read up)&quot;]
        I --&amp;gt; J[&quot;MIC: opt-in NO_READ_UP&lt;br /&gt;(off by default, repurposed onto IL)&quot;]
    end

Strict Biba would forbid every brokered-elevation primitive in Vista. The COM Elevation Moniker, `ShellExecuteEx &quot;runas&quot;`, 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].&lt;p&gt;Microsoft&apos;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.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The access-control versus information-flow gap&lt;/h3&gt;
&lt;p&gt;The deeper bound is information-flow. Dorothy Denning&apos;s May 1976 &lt;em&gt;Communications of the ACM&lt;/em&gt; paper &lt;em&gt;A lattice model of secure information flow&lt;/em&gt; established the formal framework [@denning-1976]. Denning&apos;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.&lt;/p&gt;
&lt;p&gt;MIC enforces access control, not information flow. The distinction is essential. Access control answers &lt;em&gt;&quot;can this subject perform this operation on this object?&quot;&lt;/em&gt; in O(1) at operation time. Information flow asks &lt;em&gt;&quot;does the final state of this system contain any information derived from data the subject was not authorised to read?&quot;&lt;/em&gt; That is undecidable.&lt;/p&gt;
&lt;p&gt;What this means for UAC: even when MIC perfectly enforces &lt;code&gt;NO_WRITE_UP&lt;/code&gt;, a Low-IL process can still &lt;em&gt;influence&lt;/em&gt; a High-IL process via shared state the High-IL process reads. Forshaw&apos;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.&lt;/p&gt;
&lt;h3&gt;The five concrete limits&lt;/h3&gt;
&lt;p&gt;The theoretical bounds map onto five concrete limits any practitioner can observe on a default Windows 11 install.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The second limit is that &lt;code&gt;NO_READ_UP&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;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 &lt;code&gt;win32k.sys&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;The fourth limit is the same-IL same-desktop attack surface. Two Medium-IL processes on the user&apos;s &lt;code&gt;Default&lt;/code&gt; 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 (&lt;code&gt;CreateRestrictedToken&lt;/code&gt;-style SID denial) [@chromium-sandbox][@appcontainer-isolation]. Where MIC alone is insufficient, the stack layers additional primitives, but those primitives are &lt;em&gt;additions&lt;/em&gt; to MIC, not replacements for it.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;Why MIC has to be a separate evaluator&lt;/h3&gt;
&lt;p&gt;The Harrison-Ruzzo-Ullman 1976 result is the theoretical reason MIC could not be implemented as discretionary ACEs [@hru-1976]. HRU prove that the &lt;em&gt;safety question&lt;/em&gt; (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 &lt;em&gt;separate&lt;/em&gt; evaluator: if integrity were encoded as discretionary ACEs, the safety of an object&apos;s integrity label would inherit HRU undecidability through every principal with rights over the ACE.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;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&apos;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.&lt;/p&gt;
&lt;p&gt;Did Microsoft ever try to actually move the boundary? What does it look like when a security feature finally becomes a security boundary?&lt;/p&gt;
&lt;h2&gt;9. The Adminless Successor and the Open Problems&lt;/h2&gt;
&lt;p&gt;In November 2024, Microsoft did something it had not done in seventeen years. It moved the security-boundary line. &lt;a href=&quot;https://paragmali.com/blog/adminless-how-windows-finally-made-elevation-a-security-boun/&quot; rel=&quot;noopener&quot;&gt;Administrator Protection&lt;/a&gt;, 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.&lt;/p&gt;
&lt;h3&gt;What the split-token model shared, and what Administrator Protection separates&lt;/h3&gt;
&lt;p&gt;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&apos;s May 2017 &lt;em&gt;Reading Your Way Around UAC&lt;/em&gt; framing [@forshaw-reading-uac]: same user SID, same &lt;code&gt;%USERPROFILE%&lt;/code&gt;, same &lt;code&gt;HKCU&lt;/code&gt; hive, same logon-session LUID. Administrator Protection attacks all four.&lt;/p&gt;

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&apos;s state.
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;2007 Split-Token (UAC)&lt;/th&gt;
&lt;th&gt;2024 Administrator Protection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;User SID&lt;/td&gt;
&lt;td&gt;Same as caller&lt;/td&gt;
&lt;td&gt;Different (per-user System Managed Administrator Account, SMAA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%USERPROFILE%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same as caller&lt;/td&gt;
&lt;td&gt;Different: &lt;code&gt;C:\Users\ADMIN_&amp;lt;random&amp;gt;\&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HKCU&lt;/code&gt; registry hive&lt;/td&gt;
&lt;td&gt;Same hive as caller&lt;/td&gt;
&lt;td&gt;Different hive (per-SMAA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logon session LUID&lt;/td&gt;
&lt;td&gt;Same session as caller&lt;/td&gt;
&lt;td&gt;Fresh logon session per elevation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;Consent click only&lt;/td&gt;
&lt;td&gt;Windows Hello integrated authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classification&lt;/td&gt;
&lt;td&gt;Security feature, not boundary&lt;/td&gt;
&lt;td&gt;Security boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;SMAA expands as &quot;System Managed Administrator Account&quot; per the May 19, 2025 Microsoft Windows Developer Blog explainer &lt;em&gt;Enhance your application security with Administrator protection&lt;/em&gt; [@msft-devblog-adminprot]. Earlier Microsoft Learn documentation from 2024 used the working name &quot;Adminless&quot; without the SMAA acronym. The corpus&apos;s Adminless / Administrator Protection article (#52) covers the SMAA lifecycle and the Insider-Preview timeline in more depth than this article does.&lt;/p&gt;
&lt;p&gt;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 &lt;code&gt;HKCU\Software\Classes\ms-settings\shell\open\command&lt;/code&gt;, the attacker writes to the &lt;em&gt;caller&apos;s&lt;/em&gt; HKCU hive. When &lt;code&gt;fodhelper.exe&lt;/code&gt; is then elevated under Administrator Protection, the elevated process runs under the SMAA identity, with the SMAA&apos;s own HKCU hive, which does not contain the attacker&apos;s key. The auto-elevated binary resolves the &lt;code&gt;ms-settings&lt;/code&gt; association via the SMAA&apos;s HKCU, falls through to HKLM, and gets the legitimate handler. The attacker&apos;s bypass is structurally defeated by the identity change, not by a per-binary fix [@admin-protection][@forshaw-adminprot-jan26].&lt;/p&gt;
&lt;h3&gt;The 2025 timeline&lt;/h3&gt;
&lt;p&gt;Administrator Protection&apos;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 &lt;em&gt;Administrator protection&lt;/em&gt; page acknowledges a temporary revert on December 1, 2025 &quot;while an application compatibility issue is dealt with&quot; [@admin-protection][@forshaw-adminprot-jan26]. The expected re-enablement is in 2026.&lt;/p&gt;
&lt;p&gt;Forshaw&apos;s January 26, 2026 Project Zero post &lt;em&gt;Bypassing Windows Administrator Protection&lt;/em&gt; documents the application-compatibility revert with verbatim precision. He notes that &quot;the issue is unlikely to be related to anything described in this blog post,&quot; 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.&lt;/p&gt;
&lt;h3&gt;The 2026 retrospective: nine bypasses, five via UI Access&lt;/h3&gt;
&lt;p&gt;Forshaw&apos;s January and February 2026 Project Zero pair is the canonical modern retrospective on Administrator Protection&apos;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.&lt;/p&gt;

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
&lt;p&gt;The February 2026 follow-on post, &lt;em&gt;Bypassing Administrator Protection by Abusing UI Access&lt;/em&gt;, is the more architecturally significant of the pair. It documents that &lt;strong&gt;five of the nine&lt;/strong&gt; pre-GA Administrator Protection bypasses operated entirely through the &lt;code&gt;uiAccess=true&lt;/code&gt; exemption, the long-standing UIPI carve-out for accessibility software inherited unchanged from Vista 2007 [@forshaw-adminprot-feb26].&lt;/p&gt;
&lt;p&gt;The reading is structural. Administrator Protection successfully closes the bypass surface that the split-token model&apos;s shared identity created (limit #1 through limit #4 in §8). It does &lt;em&gt;not&lt;/em&gt; close the bypass surface created by the UI Access carve-out, because UI Access is a &lt;em&gt;deliberate&lt;/em&gt; 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.&lt;/p&gt;
&lt;p&gt;The three gating conditions for &lt;code&gt;uiAccess=true&lt;/code&gt; (manifest assertion, valid Authenticode signature, admin-only install location) are documented in the &lt;em&gt;Security Considerations for Assistive Technologies&lt;/em&gt; Microsoft Learn page [@uia-security]. Forshaw&apos;s February 2026 post enumerates them verbatim and describes the &lt;code&gt;RAiLaunchAdminProcess&lt;/code&gt; Appinfo RPC entry point the UI-Access bypasses operate through [@forshaw-adminprot-feb26]. The trade press picked up the story immediately: &lt;em&gt;The Register&lt;/em&gt; covered Forshaw&apos;s January 2026 post under the headline &quot;Google researcher sits on UAC bypass for ages, only for it to become valid with new security feature&quot; on January 28, 2026 [@register-2026].&lt;/p&gt;
&lt;h3&gt;The downstream legacy&lt;/h3&gt;
&lt;p&gt;MIC and UIPI outlived UAC. The integrity-SID primitive is the connective tissue of every later sandbox model on Windows.&lt;/p&gt;

flowchart TD
    A[&quot;Integrity-SID primitive&lt;br /&gt;(MIC + UIPI, Vista 2006/2007)&quot;] --&amp;gt; B[&quot;AppContainer&lt;br /&gt;(Windows 8, 2012)&quot;]
    A --&amp;gt; C[&quot;IE Protected Mode&lt;br /&gt;(IE7, Vista 2006)&quot;]
    A --&amp;gt; D[&quot;Edge / Chrome / Firefox sandbox tiers&lt;br /&gt;(2008-present)&quot;]
    A --&amp;gt; E[&quot;Protected Process Light&lt;br /&gt;(Windows 8.1, 2013)&quot;]
    A --&amp;gt; F[&quot;Administrator Protection / SMAA&lt;br /&gt;(Windows 11, 2024)&quot;]
    A --&amp;gt; G[&quot;RunAsPPL for LSASS&lt;br /&gt;(Windows 8.1, 2013)&quot;]
    A --&amp;gt; H[&quot;Office Protected View&lt;br /&gt;(Office 2010+)&quot;]
&lt;p&gt;AppContainer (Windows 8, 2012) layers package SIDs above the integrity SID and rides the same &lt;code&gt;SeAccessCheck&lt;/code&gt; 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&apos;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]. &lt;a href=&quot;https://paragmali.com/blog/protected-process-light-when-the-administrator-isnt-enough/&quot; rel=&quot;noopener&quot;&gt;Protected Process Light&lt;/a&gt; (Windows 8.1, 2013) is a signature-based generalisation of the integrity-SID concept that PPL-protects LSASS against &lt;code&gt;OpenProcess&lt;/code&gt; 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].&lt;/p&gt;
&lt;p&gt;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&apos;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.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;

On December 1, 2025, Microsoft temporarily reverted Administrator Protection in KB5067036 pending an application-compatibility fix [@admin-protection][@forshaw-adminprot-jan26]. Forshaw&apos;s exact framing matters: &quot;the issue is unlikely to be related to anything described in this blog post.&quot; 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].
&lt;h2&gt;10. Inspecting the Stack on a Real Box&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;Inspecting integrity levels&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;whoami /groups | findstr Mandatory&lt;/code&gt; prints the mandatory label of the current process token. From an unelevated PowerShell on an administrator account, it will read &lt;code&gt;Mandatory Label\Medium Mandatory Level&lt;/code&gt;. From an elevated PowerShell, it will read &lt;code&gt;Mandatory Label\High Mandatory Level&lt;/code&gt;. From a renderer-process command inside a Chromium-based browser, it would read &lt;code&gt;Mandatory Label\Low Mandatory Level&lt;/code&gt; or &lt;code&gt;Untrusted Mandatory Level&lt;/code&gt;, depending on the sandbox tier.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;whoami /all&lt;/code&gt; is the longer view. It prints every group SID, every privilege, and the full mandatory label.Process Explorer (and System Informer) will show you the same data graphically, but &lt;code&gt;whoami&lt;/code&gt; is the canonical first-party command for getting at the same kernel information from the shell. Run it twice -- once from an unelevated PowerShell, once from an elevated PowerShell on the same admin account -- and diff the outputs to see what the elevation actually changed. That is the empirical re-creation of §1&apos;s hook.&lt;/p&gt;
&lt;p&gt;Sysinternals&apos; 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 &lt;code&gt;accesschk -e -l &amp;lt;object&amp;gt;&lt;/code&gt; Sysinternals command prints the mandatory label of a file, registry key, or other securable object: &lt;code&gt;accesschk -e -l C:\Windows\System32\drivers\&lt;/code&gt; reveals the System-IL label that protects the driver directory.&lt;/p&gt;

The PowerShell-native equivalent of `whoami /all` that programs can consume is:&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[System.Security.Principal.WindowsIdentity]::GetCurrent() |
  Select-Object -ExpandProperty Groups |
  ForEach-Object { $_.Translate([System.Security.Principal.NTAccount]) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces the same SID-to-account-name resolution &lt;code&gt;whoami /groups&lt;/code&gt; does, and is useful inside automation that needs to test deny-only group membership programmatically.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Inspecting UIPI&lt;/h3&gt;
&lt;p&gt;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. &lt;code&gt;SendMessage&lt;/code&gt; returns 0 with &lt;code&gt;GetLastError&lt;/code&gt; reading &lt;code&gt;ERROR_ACCESS_DENIED&lt;/code&gt;. The &lt;code&gt;ChangeWindowMessageFilterEx&lt;/code&gt; documentation page is the Microsoft Learn entry point for understanding the per-window, per-message exemption surface [@changewindowfilter].&lt;/p&gt;
&lt;h3&gt;Enumerating the auto-elevation list&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sigcheck -m C:\Windows\System32\*.exe | findstr /i autoelevate&lt;/code&gt; walks every executable in &lt;code&gt;System32&lt;/code&gt; and prints the manifest of each. The &lt;code&gt;findstr&lt;/code&gt; filter narrows to lines containing &lt;code&gt;autoElevate&lt;/code&gt;, 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 &lt;em&gt;not&lt;/em&gt; the same as Appinfo&apos;s internal operational allowlist; the operational subset is what UACMe enumerates [@uacme].&lt;/p&gt;
&lt;h3&gt;Watching Appinfo in action&lt;/h3&gt;
&lt;p&gt;Procmon (Sysinternals Process Monitor) filtered on &lt;code&gt;consent.exe&lt;/code&gt; shows every elevation event: the registry reads against the manifest, the SACL reads on the binary, the token-information queries against the caller&apos;s filtered token. The Windows Event Viewer&apos;s &lt;em&gt;Applications and Services Logs / Microsoft / Windows / User Account Control&lt;/em&gt; 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.&lt;/p&gt;
&lt;h3&gt;A safe lab for the bypass classes&lt;/h3&gt;
&lt;p&gt;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&apos;s workstation, James Forshaw&apos;s &lt;em&gt;sandbox-attacksurface-analysis-tools&lt;/em&gt; repository (the NtObjectManager, TokenViewer, and NtCoreLib PowerShell modules) is the standard research toolchain [@forshaw-tools]. The UACMe reference implementations (&lt;code&gt;akagi32.exe&lt;/code&gt;, &lt;code&gt;akagi64.exe&lt;/code&gt;) are flagged by Microsoft Defender as &lt;code&gt;HackTool:Win32/Welevate&lt;/code&gt;, 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.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The minimum five commands a reader can run on their own Windows box to verify everything in this article: 1. &lt;code&gt;whoami /all&lt;/code&gt; (run twice: once unelevated, once elevated; diff the outputs) 2. &lt;code&gt;whoami /groups | findstr Mandatory&lt;/code&gt; (inspect the IL of the current token) 3. &lt;code&gt;sigcheck -m C:\Windows\System32\eventvwr.exe&lt;/code&gt; (read the autoElevate manifest) 4. &lt;code&gt;tasklist /v /fi &quot;imagename eq svchost.exe&quot; | findstr Appinfo&lt;/code&gt; (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.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;11. Five Misconceptions That Will Not Die&lt;/h2&gt;
&lt;p&gt;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.&lt;/p&gt;

No, and Microsoft&apos;s own documentation has said so since February 2007. The canonical &quot;Neither UAC elevations nor Protected Mode IE define new Windows security boundaries&quot; 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.

No. The Secure Desktop is on the `Winlogon` desktop within `WinSta0`, *within* the user&apos;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.

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&apos;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&apos;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.

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 &quot;blocks all `WM_*`&quot; misconception is one of the most common errors in Windows-security literature [@uipi-wiki][@russinovich-blog-2007].

No. `ShellExecuteEx` with the `&quot;runas&quot;` verb is whole-process elevation: Appinfo creates a new process under the caller&apos;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&apos;s methods at High IL [@com-elevation-moniker]. The caller stays Medium; only the COM object&apos;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.

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&apos;s HKCU hive, but the elevated process runs under the SMAA&apos;s different HKCU hive and never consults the attacker&apos;s key [@admin-protection]. The DLL-search-order class is partially mitigated by the SMAA&apos;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&apos;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.

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&apos;s daily shell does not benefit, and a malware drop in any process under the admin&apos;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.
&lt;h2&gt;12. The Plumbing Outlived the Yellow Dialog&lt;/h2&gt;
&lt;p&gt;Return to the two &lt;code&gt;whoami&lt;/code&gt; 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.&lt;/p&gt;
&lt;p&gt;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 (&lt;code&gt;S-1-16-8192&lt;/code&gt;) to High (&lt;code&gt;S-1-16-12288&lt;/code&gt;). 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 &lt;code&gt;CreateProcessAsUser&lt;/code&gt;, 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).&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;The bypass-research industry of 2009 to 2024 was the empirical confirmation of the architect&apos;s 2007 disclaimer. Davidson&apos;s December 2009 essay opened the auto-elevation surface; Nelson&apos;s 2016-2017 series productised the registry-hijack class; hfiref0x&apos;s UACMe catalogued 81 methods and counting; Forshaw&apos;s 2017 &lt;em&gt;Reading Your Way Around UAC&lt;/em&gt; 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].&lt;/p&gt;
&lt;p&gt;The November 2024 Administrator Protection reclassification is the line finally moving. The split-token model&apos;s four shared properties between filtered and linked tokens (same SID, same &lt;code&gt;%USERPROFILE%&lt;/code&gt;, same &lt;code&gt;HKCU&lt;/code&gt;, 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&apos;s February 2026 Project Zero post documents as the source of five of nine pre-GA bypasses [@forshaw-adminprot-feb26].&lt;/p&gt;
&lt;p&gt;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 &lt;em&gt;is&lt;/em&gt;. 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 &lt;code&gt;WinSta0&lt;/code&gt; primitives that shipped on January 30, 2007 [@vista-press-release][@appcontainer-isolation][@chromium-sandbox]. The quiet plumbing did the work.&lt;/p&gt;
&lt;p&gt;Next time you click &quot;Yes&quot; 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].&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;integrity-level-stack-mic-uipi-uac&quot; keyTerms={[
  { term: &quot;Mandatory Integrity Control (MIC)&quot;, definition: &quot;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&apos;s DACL. Denials short-circuit the access check; integrity beats identity.&quot; },
  { term: &quot;Integrity Level (IL)&quot;, definition: &quot;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.&quot; },
  { term: &quot;User Interface Privilege Isolation (UIPI)&quot;, definition: &quot;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.&quot; },
  { term: &quot;Split-token model&quot;, definition: &quot;Admin Approval Mode: an administrator&apos;s logon produces a Medium-IL filtered token plus a dormant High-IL linked token. The filtered token runs the interactive shell.&quot; },
  { term: &quot;Secure Desktop&quot;, definition: &quot;A separate desktop object (Winlogon) within WinSta0 in the user&apos;s interactive session, on which consent.exe renders the UAC prompt. Not Session 0.&quot; },
  { term: &quot;Application Information service (Appinfo)&quot;, definition: &quot;The SYSTEM-trusted Windows service that mediates the filtered-to-linked token swap at elevation time. Exposes RAiLaunchAdminProcess.&quot; },
  { term: &quot;Auto-elevation allowlist&quot;, definition: &quot;The internal Appinfo allowlist of Microsoft-signed binaries in trusted directories whose manifests assert autoElevate=true. Four gates: manifest, signature, path, allowlist entry.&quot; },
  { term: &quot;COM Elevation Moniker&quot;, definition: &quot;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.&quot; },
  { term: &quot;System Managed Administrator Account (SMAA)&quot;, definition: &quot;The per-user separate identity Administrator Protection provisions at first elevation. Different SID, profile, HKCU, LUID from the calling user.&quot; },
  { term: &quot;Biba Star Integrity Property&quot;, definition: &quot;No write up: a subject at integrity level Is cannot write an object at integrity level Io &amp;gt; Is. MIC implements this as the default NO_WRITE_UP policy.&quot; }
]} questions={[
  { q: &quot;Why is MIC a separate evaluator rather than another ACE in the DACL?&quot;, a: &quot;Because DACLs are discretionary by definition; an object owner can rewrite the DACL. MIC&apos;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.&quot; },
  { q: &quot;Why doesn&apos;t the consent prompt elevate?&quot;, a: &quot;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.&quot; },
  { q: &quot;Why does UIPI block WM_SETTEXT but not WM_PAINT?&quot;, a: &quot;Because WM_SETTEXT mutates the receiving window&apos;s state (the message replaces the window&apos;s text), and an attacker who can mutate a higher-IL window&apos;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.&quot; },
  { q: &quot;Which Biba rules does MIC actually implement?&quot;, a: &quot;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&apos;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 &apos;runas&apos;, Appinfo) is the operationally usable workaround.&quot; },
  { q: &quot;What changed in November 2024 that turned UAC&apos;s elevation transition into a security boundary?&quot;, a: &quot;Administrator Protection replaced the split-token model&apos;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.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>uac</category><category>mandatory-integrity-control</category><category>uipi</category><category>integrity-levels</category><category>access-control</category><category>split-token</category><category>administrator-protection</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>&quot;Can This Code Do This?&quot; -- Twenty-Five Years of Attacks on the Windows Access-Control Model</title><link>https://paragmali.com/blog/windows-access-control-25-years-of-attacks/</link><guid isPermaLink="true">https://paragmali.com/blog/windows-access-control-25-years-of-attacks/</guid><description>How a single kernel function, SeAccessCheck, decides every Windows operation -- and how Mimikatz, the Potato lineage, and seventy UAC bypasses each attack one of its inputs.</description><pubDate>Sun, 10 May 2026 00:00:00 GMT</pubDate><content:encoded>
Windows answers the question *can this code do this?* with one kernel function, `SeAccessCheck`, evaluated against five inputs: a security descriptor, an access token, a desired-access mask, a generic-mapping table, and any previously-granted access. The function and its inputs have not structurally changed since July 27, 1993. Every famous Windows local-privilege-escalation tool of the last twenty-five years -- Mimikatz, JuicyPotato and seven other Potatoes, the seventy AutoElevate-redirect methods catalogued in UACMe -- attacks one of those inputs. This article tells that story as one system, names the five structural limits Microsoft has publicly conceded, and explains why Adminless, NTLMless, VBS Trustlets, and Credential Guard are the four non-overlapping ways to close them.
&lt;h2&gt;1. One Question, Billions of Times a Second&lt;/h2&gt;
&lt;p&gt;Open a Windows PowerShell window and run &lt;code&gt;whoami /priv&lt;/code&gt;. Read the column on the right. &lt;code&gt;SeShutdownPrivilege&lt;/code&gt;. &lt;code&gt;SeUndockPrivilege&lt;/code&gt;. &lt;code&gt;SeIncreaseWorkingSetPrivilege&lt;/code&gt;. &lt;code&gt;SeTimeZonePrivilege&lt;/code&gt;. About twenty rows of capabilities, almost all marked &lt;em&gt;Disabled&lt;/em&gt;, on a token that lives inside &lt;code&gt;explorer.exe&lt;/code&gt;&apos;s memory and that the kernel consults billions of times a second.&lt;/p&gt;
&lt;p&gt;Now run &lt;code&gt;icacls C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;. The output reads &lt;code&gt;BUILTIN\Administrators:(F)&lt;/code&gt;, &lt;code&gt;NT AUTHORITY\SYSTEM:(F)&lt;/code&gt;, &lt;code&gt;BUILTIN\Users:(R)&lt;/code&gt;. Six characters per principal, decoded by something inside the kernel called &lt;code&gt;SeAccessCheck&lt;/code&gt;, applied to a data structure called a security descriptor, against a credential called an access token, every time any process anywhere on the machine asks for read access to that single file [@ms-learn-access-control].&lt;/p&gt;
&lt;p&gt;This article is about the model behind those two outputs. A model that has not structurally changed since July 27, 1993, when Windows NT 3.1 shipped from Redmond [@en-wiki-windows-nt-3-1]. A model that every famous Windows local-privilege-escalation tool of the last twenty-five years -- Mimikatz, JuicyPotato, fodhelper.exe, the seventy methods in the open-source UACMe catalogue -- exists to attack [@github-gentilkiwi-mimikatz, @github-ohpe-juicy-potato, @github-hfiref0x-uacme].&lt;/p&gt;
&lt;p&gt;The thesis comes in three convictions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SeAccessCheck&lt;/code&gt; is the answer.&lt;/strong&gt; Every securable Windows operation that touches a securable object resolves through one decision function with one set of inputs [@ms-learn-how-dacls-control-access].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every famous Windows escalation tool attacks one of those inputs.&lt;/strong&gt; JuicyPotato attacks the token. Mimikatz attacks the privilege list. Fodhelper attacks the elevation flow that produces the token. HiveNightmare attacks the DACL on a single file [@nvd-cve-2021-36934]. The vocabulary scales.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The model has five structural limits its keepers have publicly conceded&lt;/strong&gt; [@msrc-servicing-criteria], and Adminless, NTLMless, VBS Trustlets, and Credential Guard are the four non-overlapping ways to close them.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The forecast for the next 14,000 words: ten primitives (the Security Reference Monitor, security identifiers, tokens, security descriptors, DACLs, SACLs, ACEs, privileges, Mandatory Integrity Control, User Account Control), one canonical oracle (&lt;code&gt;SeAccessCheck&lt;/code&gt;), two attack families (the Potato lineage and the UACMe bypass tradition), and four successor architectures (Adminless, NTLMless, VBS Trustlets, Credential Guard).&lt;/p&gt;
&lt;p&gt;If the model has not structurally changed since 1993, why has it taken thirty-three years and seventy bypasses to map its failure modes -- and what does each generation tell us about the next?&lt;/p&gt;
&lt;h2&gt;2. Origins: From Lampson&apos;s Matrix to Cutler&apos;s Kernel (1971-1993)&lt;/h2&gt;
&lt;p&gt;The vocabulary starts in a paper Butler Lampson presented at Princeton in 1971 and the ACM republished in &lt;em&gt;Operating Systems Review&lt;/em&gt; in January 1974. Lampson framed protection as a 2-D matrix: rows index the &lt;em&gt;subjects&lt;/em&gt; (users, processes), columns index the &lt;em&gt;objects&lt;/em&gt; (files, devices, memory pages), and the cell at the intersection holds the &lt;em&gt;operations&lt;/em&gt; the subject is permitted on the object. The matrix is a sparse, mostly-empty table the size of every-process times every-file. No real system has ever stored it that way.&lt;/p&gt;
&lt;p&gt;Two implementation strategies fall out of the formalism. Slice the matrix by row and you get &lt;em&gt;capability lists&lt;/em&gt;: each subject carries a token that names the objects it can touch. Slice by column and you get &lt;em&gt;access-control lists&lt;/em&gt;: each object carries a list of subjects allowed to touch it. Lampson worked through both in the paper. Operating systems built on the second slice came to dominate, partly because hardware in 1971 made unforgeable capabilities expensive, partly because file systems could carry an ACL at the inode without changing every program. Decades later, the gap between the two implementations would still matter; we will reach Norm Hardy&apos;s &quot;Confused Deputy&quot; in a moment.&lt;/p&gt;
&lt;p&gt;The lever that turned theory into Windows came from procurement, not academia. On December 26, 1985, the U.S. Department of Defense published &lt;code&gt;DoD 5200.28-STD&lt;/code&gt;, the &lt;em&gt;Trusted Computer System Evaluation Criteria&lt;/em&gt;, known by the colour of its cover as the Orange Book [@nist-csrc-rainbow]. The Orange Book defined four divisions of trusted-system assurance, and its C2 class -- &quot;Controlled Access Protection&quot; -- made discretionary access control plus auditing a federal procurement floor. The September 30, 1987 Neon Orange Book (NCSC-TG-003) and the July 28, 1987 Tan Book (NCSC-TG-001) elaborated DAC and audit respectively [@nist-csrc-rainbow]. After 1985, no operating system that wanted U.S. federal customers could ship without per-user ACLs and an audit log.&lt;/p&gt;
&lt;p&gt;A year before the Orange Book ossified DAC into procurement, Norm Hardy of the Tymshare / KeyKOS lineage published a three-page paper in &lt;em&gt;Operating Systems Review&lt;/em&gt; that named the structural limit of the entire ACL-shaped class: &quot;The Confused Deputy (or why capabilities might have been invented)&quot; [@wayback-cap-lore-hardy]. Hardy described a privileged compiler that wrote billing records to a system file. A user could trick the compiler into writing the user&apos;s &lt;em&gt;output&lt;/em&gt; file over the billing file, because the compiler used &lt;em&gt;its own&lt;/em&gt; authority on every write and could not distinguish &quot;authority I have&quot; from &quot;authority the caller asked me to use.&quot;&lt;/p&gt;
&lt;p&gt;The Wikipedia summary of the field is exact: &quot;Capability systems protect against the confused deputy problem, whereas access-control list-based systems do not&quot; [@en-wiki-confused-deputy]. Hold this paper. It will come back in Section 10.&lt;/p&gt;

The team that built Windows NT was not assembled in Redmond. David Cutler arrived in October 1988 from Digital Equipment Corporation [@en-wiki-cutler], where he had led VMS and the cancelled Mica successor, and brought with him a fraction of his old DEC team.&lt;p&gt;The cultural import mattered: VAX/VMS, announced October 25, 1977 alongside the VAX-11/780 (V1.0 shipped August 1978) [@en-wiki-openvms], introduced UIC-based file protection and a kernel-mode security architecture, and by the mid-1980s the VAX/VMS line had been evaluated at TCSEC Class C2 [@en-wiki-openvms], by which time the system had been hardened with per-object ACLs, audit channels, and an explicit reference monitor. That C2-hardened VMS was the cultural reference Cutler brought with him to Microsoft. G. Pascal Zachary&apos;s &lt;em&gt;Showstopper!&lt;/em&gt; tells the story of the four-year build of NT 3.1 from that team [@showstopper-zachary].&lt;/p&gt;
&lt;p&gt;The point for this article is narrower. NT 3.1&apos;s nine access-control primitives -- Security Reference Monitor, security identifier, access token, security descriptor, DACL, SACL, ACE, privileges, audit channel -- did not arrive piecemeal. They were specified together, before the first line of &lt;code&gt;SeAccessCheck&lt;/code&gt; was written, against a procurement standard the team intended to clear.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;NT 3.1 released to manufacturing on July 27, 1993 [@en-wiki-windows-nt-3-1]. NT 3.5, released to manufacturing on September 21, 1994 [@en-wiki-nt-3-5], was hardened through Service Pack 3 (June 21, 1995) and was rated by the National Security Agency in July 1995 as complying with TCSEC C2 criteria against the standalone single-user configuration [@en-wiki-nt-3-5]; NT 4.0 Service Pack 6a was evaluated at TCSEC C2 in a standalone (non-networked) configuration, consistent with NT 3.5 SP3. The combination froze the model. Once C2 evaluation was on the books, structural changes to the access-control surface would have required re-evaluation. Federal procurement obligations have kept the structural shape intact for thirty-one years, even after the Department of Defense formally retired TCSEC in favour of the Common Criteria.&lt;/p&gt;
&lt;p&gt;Cutler shipped a model that has answered &quot;can this code do this?&quot; the same way for thirty-three years. What is the actual function -- and what are its inputs?&lt;/p&gt;
&lt;h2&gt;3. The Kernel Oracle: &lt;code&gt;SeAccessCheck&lt;/code&gt; and Its Inputs&lt;/h2&gt;
&lt;p&gt;The function has one signature, one return value, and one job. Microsoft&apos;s Win32 documentation exposes a user-mode mirror called &lt;code&gt;AccessCheck&lt;/code&gt; that lets userland code ask the question without holding a handle, and a kernel routine called &lt;code&gt;SeAccessCheck&lt;/code&gt; that the kernel invokes at every securable operation [@ms-learn-access-control, @ms-learn-access-tokens]. The shape is the same in both directions:&lt;/p&gt;
&lt;p&gt;$$
\textsf{SeAccessCheck}(\textit{SD}, \textit{Token}, \textit{DesiredAccess}) \to (\textit{GrantedAccess}, \textit{Status})
$$&lt;/p&gt;
&lt;p&gt;Three inputs in (a security descriptor, an access token, a requested-access mask), two outputs out (the access mask actually granted, and a &lt;code&gt;STATUS_ACCESS_DENIED&lt;/code&gt; flavour if any). Two more hidden inputs make the kernel signature precise: a &lt;em&gt;generic mapping&lt;/em&gt; table that translates the four generic rights (&lt;code&gt;GENERIC_READ&lt;/code&gt;, &lt;code&gt;GENERIC_WRITE&lt;/code&gt;, &lt;code&gt;GENERIC_EXECUTE&lt;/code&gt;, &lt;code&gt;GENERIC_ALL&lt;/code&gt;) into object-type-specific bits, and a &lt;em&gt;previously-granted-access&lt;/em&gt; mask that the kernel carries forward when an access check happens in two phases. Together: five inputs, one decision, one log entry (the documented kernel routine carries a few additional parameters of kernel-internal bookkeeping -- a synchronization flag, an &lt;code&gt;AccessMode&lt;/code&gt; discriminator that elides the check for kernel-mode callers, and a privileges out-parameter -- which the explanatory five-input model collapses; the user-mode &lt;code&gt;AccessCheck&lt;/code&gt; mirror is closer to the model the article uses).&lt;/p&gt;

The kernel-mode component of the Windows NT executive that performs all access checks against securable objects. It owns `SeAccessCheck` and the audit log generation routines. The SRM is a *subsystem*, not a feature: every other kernel component that needs to grant or deny access calls into it.

The Windows kernel routine that decides whether a thread may perform a requested set of operations on an object. It takes a security descriptor, an access token, a desired-access mask, a per-object-type generic-mapping table, and any previously-granted access. (The documented kernel signature also carries a synchronization flag, an `AccessMode` discriminator that elides the check for kernel-mode callers, and a privileges out-parameter; the five-input model used here is an explanatory simplification that the user-mode `AccessCheck` mirror tracks more closely.) It returns the subset of the desired-access mask that the kernel grants and a status code. Every call site that opens or modifies a securable object eventually reaches this function [@ms-learn-how-dacls-control-access].
&lt;p&gt;The five inputs are not equally exotic. The desired-access mask and the generic-mapping table are bookkeeping that an object type defines once at registration time. The previously-granted-access input is the kernel handing itself a pencil for two-phase access checks, mostly invisible to userland. The two inputs the rest of the article will keep returning to are the security descriptor and the access token.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;security descriptor&lt;/em&gt; is the data structure attached to the object. It carries an owner SID, a primary-group SID, a discretionary access-control list (DACL) of allow / deny / audit entries, and a system access-control list (SACL) holding audit and integrity-label entries [@ms-learn-mic]. The DACL is what &lt;code&gt;icacls&lt;/code&gt; prints. The SACL is what writes Event Log entries when something the descriptor&apos;s writer wanted to watch happens.&lt;/p&gt;
&lt;p&gt;An &lt;em&gt;access token&lt;/em&gt; is the data structure attached to the caller. It names the user (one SID), the user&apos;s groups (a list of SIDs), the privileges the user holds (a list of named superpowers), the integrity level the kernel will compare against the object&apos;s label, and a flag that says whether the token is a &lt;em&gt;primary&lt;/em&gt; token (one per process) or an &lt;em&gt;impersonation&lt;/em&gt; token (one per thread, used to act on a client&apos;s behalf) [@ms-learn-access-tokens].&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s documentation lists the token&apos;s contents almost as bullet points: &quot;The security identifier (SID) for the user&apos;s account; SIDs for the groups of which the user is a member; A logon SID that identifies the current logon session; A list of the privileges held by either the user or the user&apos;s groups; An owner SID; The SID for the primary group; The default DACL; ... Whether the token is a primary or impersonation token; An optional list of restricting SIDs; Current impersonation levels...&quot; [@ms-learn-access-tokens].&lt;/p&gt;
&lt;p&gt;The flow inside &lt;code&gt;SeAccessCheck&lt;/code&gt; is mechanical. The kernel maps &lt;code&gt;DesiredAccess&lt;/code&gt; from generic to specific bits using the type&apos;s mapping table. It checks the integrity label of the object against the integrity level on the token (Mandatory Integrity Control runs &lt;em&gt;before&lt;/em&gt; the DACL walk, a point Section 7 expands). It walks the DACL in order, deny-first, accumulating bits granted by allow ACEs whose SID is in the token&apos;s list. It applies a &lt;em&gt;privilege bypass&lt;/em&gt; short-circuit if the caller holds &lt;code&gt;SeBackupPrivilege&lt;/code&gt;, &lt;code&gt;SeRestorePrivilege&lt;/code&gt;, &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, or one of the other privileges that grants access to whole classes of objects without consulting the DACL. It returns the accumulated &lt;code&gt;GrantedAccess&lt;/code&gt;, or &lt;code&gt;STATUS_ACCESS_DENIED&lt;/code&gt; if the requested bits were not all granted.&lt;/p&gt;

sequenceDiagram
    participant App as User-mode caller
    participant ObjMgr as Object Manager
    participant SRM as Security Reference Monitor
    participant Audit as Audit channel
    App-&amp;gt;&amp;gt;ObjMgr: OpenObject(name, DesiredAccess, ImpersonationToken)
    ObjMgr-&amp;gt;&amp;gt;ObjMgr: Resolve name (\BaseNamedObjects, \Device, \??)
    ObjMgr-&amp;gt;&amp;gt;ObjMgr: Fetch security descriptor from object header
    ObjMgr-&amp;gt;&amp;gt;SRM: SeAccessCheck(SD, Token, DesiredAccess)
    SRM-&amp;gt;&amp;gt;SRM: Map generic rights via type mapping
    SRM-&amp;gt;&amp;gt;SRM: Check mandatory integrity label
    SRM-&amp;gt;&amp;gt;SRM: Privilege-bypass short-circuit (SeBackup, SeRestore, SeDebug)
    SRM-&amp;gt;&amp;gt;SRM: Walk DACL in canonical order, deny-first
    SRM--&amp;gt;&amp;gt;ObjMgr: GrantedAccess + STATUS code
    ObjMgr-&amp;gt;&amp;gt;Audit: Emit SACL audit ACE if matched
    ObjMgr--&amp;gt;&amp;gt;App: HANDLE or STATUS_ACCESS_DENIED
&lt;p&gt;Five inputs. One function. One log entry on the way out. The thesis statement of this article is the consequence: every later section is an attack on one of those inputs. JuicyPotato attacks the token. Mimikatz attacks the privilege list. Fodhelper attacks the elevation flow that produces the token. HiveNightmare attacks the security descriptor on a single file. Conditional ACEs and Dynamic Access Control extend the matrix&apos;s &lt;em&gt;subject&lt;/em&gt; dimension by adding claims to the token. UAC tries to keep the token small by default and only inflate it on demand. Every primitive in the article maps cleanly onto one of &lt;code&gt;SeAccessCheck&lt;/code&gt;&apos;s five inputs, and every famous attack tool in the article maps cleanly onto one primitive.&lt;/p&gt;
&lt;p&gt;The function is fixed. The inputs are five. So how does the kernel actually walk a DACL -- and where does the wrong answer come from?&lt;/p&gt;
&lt;h2&gt;4. The DACL Algorithm and the SID Namespace&lt;/h2&gt;
&lt;p&gt;&quot;Walk the DACL in order.&quot; Six words that have generated hundreds of thousands of misconfigured ACLs since 1993. The Microsoft Learn page that ships the algorithm is short and exact. The system &quot;examines each ACE in sequence until... an access-denied ACE explicitly denies any of the requested access rights to one of the trustees listed in the thread&apos;s access token... one or more access-allowed ACEs for trustees listed in the thread&apos;s access token explicitly grant all the requested access rights... All ACEs have been checked and there is still at least one requested access right that has not been explicitly allowed, in which case, access is implicitly denied&quot; [@ms-learn-how-dacls-control-access].&lt;/p&gt;
&lt;p&gt;Three terminations. Deny terminates with denial. Enough allows terminates with grant. End-of-list with anything left ungranted terminates with denial. Note the asymmetry the algorithm encodes: a single deny anywhere in the DACL kills the request, but an allow has to be paired with explicit coverage of every desired bit. The default is denial.&lt;/p&gt;

The ordered list of access-control entries (ACEs) attached to a securable object&apos;s security descriptor that specifies which trustees are granted or denied which access rights. *Discretionary* means the object&apos;s owner controls the list, in contrast to a mandatory list whose entries the system enforces independent of owner intent.

A single grant, deny, audit, or mandatory-label record inside a DACL or SACL. Each ACE carries an SID identifying the trustee, a 32-bit access mask, a flags byte controlling inheritance, and a type discriminator. Windows defines four primary ACE types (`ACCESS_ALLOWED_ACE`, `ACCESS_DENIED_ACE`, `SYSTEM_AUDIT_ACE`, `SYSTEM_MANDATORY_LABEL_ACE`) plus callback variants for conditional ACEs [@ms-learn-ace-strings].
&lt;p&gt;{`
// Faithful implementation of the algorithm at
// learn.microsoft.com/en-us/windows/win32/secauthz/how-dacls-control-access-to-an-object
// Inputs: token (set of SIDs), DACL (ordered ACEs), desiredAccess mask.
// Output: granted mask + a per-ACE trace of why.&lt;/p&gt;
&lt;p&gt;function seAccessCheck(token, dacl, desiredAccess) {
  let remaining = desiredAccess;       // bits still needed
  let granted = 0;                     // bits accumulated so far
  const trace = [];&lt;/p&gt;
&lt;p&gt;  // NULL DACL grants everything; empty DACL grants nothing.
  if (dacl === null) {
    return { status: &apos;GRANTED (NULL DACL)&apos;, granted: desiredAccess, trace: [&apos;NULL DACL: full access&apos;] };
  }
  if (dacl.length === 0) {
    return { status: &apos;DENIED (empty DACL)&apos;, granted: 0, trace: [&apos;Empty DACL: no access&apos;] };
  }&lt;/p&gt;
&lt;p&gt;  for (let i = 0; i &amp;lt; dacl.length; i++) {
    const ace = dacl[i];
    const sidMatches = token.sids.includes(ace.sid);
    if (!sidMatches) {
      trace.push(`ACE ${i} (${ace.type} ${ace.sid}): SID not in token; skip`);
      continue;
    }
    if (ace.type === &apos;DENY&apos;) {
      const deniedBits = ace.mask &amp;amp; remaining;
      if (deniedBits !== 0) {
        trace.push(`ACE ${i}: DENY hits 0x${deniedBits.toString(16)} -&amp;gt; ACCESS_DENIED`);
        return { status: &apos;DENIED&apos;, granted: 0, trace };
      }
      trace.push(`ACE ${i}: DENY ${ace.sid} 0x${ace.mask.toString(16)} -- no requested bit hit; skip`);
    } else if (ace.type === &apos;ALLOW&apos;) {
      const newBits = ace.mask &amp;amp; remaining;
      granted |= newBits;
      remaining &amp;amp;= ~newBits;
      trace.push(`ACE ${i}: ALLOW grants 0x${newBits.toString(16)}, remaining 0x${remaining.toString(16)}`);
      if (remaining === 0) {
        return { status: &apos;GRANTED&apos;, granted, trace };
      }
    }
  }
  return { status: &apos;DENIED (end of DACL with bits unsatisfied)&apos;, granted: 0, trace };
}&lt;/p&gt;
&lt;p&gt;// Demo: a token with the user&apos;s SID and BUILTIN\Users; DACL with explicit deny + Everyone allow.
const token = { sids: [&apos;S-1-5-21-A-B-C-1001&apos;, &apos;S-1-5-32-545&apos;, &apos;S-1-1-0&apos;] };
const dacl = [
  { type: &apos;DENY&apos;,  sid: &apos;S-1-5-21-A-B-C-1001&apos;, mask: 0x00040000 },  // deny WRITE_DAC
  { type: &apos;ALLOW&apos;, sid: &apos;S-1-1-0&apos;,              mask: 0x00120089 }, // FILE_GENERIC_READ
  { type: &apos;ALLOW&apos;, sid: &apos;S-1-5-32-545&apos;,         mask: 0x001200A9 }, // GENERIC_READ + EXECUTE
];
console.log(seAccessCheck(token, dacl, 0x00040089));
`}&lt;/p&gt;
&lt;p&gt;The runnable above implements three subtleties the prose can flatten. First: the &lt;em&gt;NULL DACL&lt;/em&gt; versus &lt;em&gt;empty DACL&lt;/em&gt; distinction. A descriptor with no DACL at all -- a literal &lt;code&gt;NULL&lt;/code&gt; pointer where the list would be -- grants full access, on the theory that the writer expressed no policy and the kernel will not invent one. A descriptor with a DACL that exists but contains zero ACEs denies everything, because the writer expressed a policy and that policy has no allows. The single most common high-impact misconfiguration in the Windows codebase is code that meant to write the second and wrote the first, or vice versa.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Newly-written code that &quot;creates a file with no protection&quot; almost always wants an empty DACL but ends up with a NULL DACL because of how the SECURITY_DESCRIPTOR initialisation defaults work. Verify with &lt;code&gt;Get-Acl&lt;/code&gt; or &lt;code&gt;icacls&lt;/code&gt; after creation; a NULL DACL surface in &lt;code&gt;icacls&lt;/code&gt; looks like &lt;code&gt;Everyone:(F)&lt;/code&gt; and is almost always a bug, not a feature [@ms-learn-how-dacls-control-access].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Second: ACE &lt;em&gt;order&lt;/em&gt; is the caller&apos;s responsibility. The kernel walks the list in the order it finds it. The &quot;canonical&quot; order Windows expects is four-step, quoted verbatim from the Microsoft Learn reference [@ms-learn-order-of-aces]: &quot;1. All explicit ACEs are placed in a group before any inherited ACEs. 2. Within the group of explicit ACEs, access-denied ACEs are placed before access-allowed ACEs. 3. Inherited ACEs are placed in the order in which they are inherited... 4. For each level of inherited ACEs, access-denied ACEs are placed before access-allowed ACEs.&quot;&lt;/p&gt;
&lt;p&gt;The same page underlines who has to enforce the order: &quot;Functions such as &lt;code&gt;AddAccessAllowedAceEx&lt;/code&gt; and &lt;code&gt;AddAccessAllowedObjectAce&lt;/code&gt; add an ACE to the end of an ACL. It is the caller&apos;s responsibility to ensure that the ACEs are added in the proper order.&quot; If the writer of the DACL hands the kernel an out-of-order list with a deny ACE buried after a wide allow ACE, the deny will be unreachable and the descriptor will silently grant more than the writer intended.&lt;/p&gt;
&lt;p&gt;Third: there is no special case for &quot;Everyone.&quot; The well-known SID &lt;code&gt;S-1-1-0&lt;/code&gt; exists in every token of every process on the machine; an ACE against it applies to every caller. There is no extra logic that says &quot;if this is Everyone, treat it differently from any other group.&quot; James Forshaw made the point with characteristic bluntness in 2020: &quot;don&apos;t forget S-1-1-0, this is NOT A SECURITY BOUNDARY. Lah lah, I can&apos;t hear you!&quot; [@tiraniddo-sharing-logon-session]. The DACL evaluation algorithm does not know &quot;Everyone&quot; is special. It is a SID like any other.&lt;/p&gt;
&lt;p&gt;That makes the SID namespace itself worth a tour. Microsoft documents the structure as a revision number, an &lt;em&gt;identifier authority&lt;/em&gt; (a six-byte field that says which authority issued the SID), a list of sub-authorities, and a final &lt;em&gt;relative identifier&lt;/em&gt; (RID) [@ms-learn-security-identifiers]. Microsoft&apos;s own page on SIDs is precise: &quot;A security identifier (SID) is a unique value of variable length used to identify a trustee... When a SID has been used as the unique identifier for a user or group, it cannot ever be used again to identify another user or group.&quot;&lt;/p&gt;
&lt;p&gt;The well-known SIDs the kernel recognises by name include &lt;code&gt;SYSTEM&lt;/code&gt; (S-1-5-18), &lt;code&gt;LocalService&lt;/code&gt; (S-1-5-19), &lt;code&gt;NetworkService&lt;/code&gt; (S-1-5-20), &lt;code&gt;Everyone&lt;/code&gt; (S-1-1-0), &lt;code&gt;Authenticated Users&lt;/code&gt; (S-1-5-11), and the four Mandatory Integrity Control levels (S-1-16-4096 / 8192 / 12288 / 16384) [@en-wiki-mic, @ms-learn-mic]. Machine-issued SIDs encode the machine&apos;s domain identity in the sub-authorities; domain-issued SIDs encode the domain identity. RID 500 is, by convention, the local Administrator account; RID 501 is the Guest account.&lt;/p&gt;

A variable-length, never-reused identifier for a trustee (user, group, machine, service, or capability) inside the Windows security model. SIDs are encoded in canonical form `S-R-I-S1-S2-...-RID`, where R is a revision number, I is the identifier authority, the Sn are sub-authorities issued by that authority, and RID is the relative identifier. Every ACE references an SID; every token contains a list of them [@ms-learn-security-identifiers].
&lt;p&gt;James Forshaw documented in 2017 that Windows generates the per-service SID for &lt;code&gt;NT SERVICE\&amp;lt;ServiceName&amp;gt;&lt;/code&gt; deterministically: it is the SHA-1 hash of the uppercased service name, formatted into the SID&apos;s sub-authority fields. This is why Windows can refer to running services as security principals without an explicit registration step -- the kernel derives the SID on demand [@tiraniddo-trustedinstaller-blog].&lt;/p&gt;
&lt;p&gt;Two SID families this article will not derive: AppContainer &lt;em&gt;Package SIDs&lt;/em&gt; (S-1-15-2-...) and &lt;em&gt;capability SIDs&lt;/em&gt; (S-1-15-3-...). Both arrived with Windows 8 in 2012 and extend the matrix&apos;s subjects with code identity and capability tokens. The sibling &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;App Identity article&lt;/a&gt; in this series carries the canonical derivation, including the Crockford-Base32 PublisherId derivation that produces a Package SID from an MSIX package signature [@app-identity-sibling]. Section 5 of &lt;em&gt;this&lt;/em&gt; article will mention those SIDs in tokens; we will not redefine them here.&lt;/p&gt;
&lt;p&gt;The DACL is half the story. What does the &lt;em&gt;thread&lt;/em&gt; bring to the access check -- and why is the answer &quot;whoever happens to hold the handle&quot;?&lt;/p&gt;
&lt;h2&gt;5. Tokens as Bearer Credentials&lt;/h2&gt;
&lt;p&gt;A token is not a credential the way a password is. A token is a credential the way &lt;em&gt;cash&lt;/em&gt; is: whoever holds it gets the rights. This is the single most important property in the article.&lt;/p&gt;
&lt;p&gt;Microsoft splits tokens into two flavours by purpose [@ms-learn-access-tokens]. A &lt;em&gt;primary token&lt;/em&gt; hangs off a process and represents the security identity that process runs as. Every process has exactly one. An &lt;em&gt;impersonation token&lt;/em&gt; hangs off a thread and lets that thread temporarily act as someone else -- typically a network client whose request the thread is servicing. Tokens are kernel objects with handles, and like every other kernel object the kernel does not care how a process obtained the handle. If the handle resolves to a token in the kernel&apos;s table, the kernel grants the rights the token names.&lt;/p&gt;

A kernel object that names the security identity of a thread or process. It carries the user&apos;s SID, the SIDs of the user&apos;s groups, the privileges the user holds, an integrity level, an integrity-level mandatory policy, an optional list of *restricting* SIDs, a flag distinguishing primary from impersonation tokens, and the impersonation level (Anonymous / Identification / Impersonation / Delegation) [@ms-learn-access-tokens]. The kernel consults a token on every access check for the thread that holds it.

A *primary token* is owned by a process and represents the identity the process runs as; every process has exactly one primary token. An *impersonation token* is owned by a thread and represents an identity the thread is temporarily acting on behalf of -- typically a network client. The primary / impersonation distinction is a discriminator inside the token itself, set when the token is created or duplicated [@ms-learn-access-tokens].
&lt;p&gt;The impersonation flavour acquired its modern shape in Windows 2000. A token&apos;s &lt;em&gt;impersonation level&lt;/em&gt; takes one of four values, ordered from least to most privileged for the impersonator. &lt;em&gt;Anonymous&lt;/em&gt; lets the server know nothing about the client. &lt;em&gt;Identification&lt;/em&gt; lets the server learn the client&apos;s SIDs but not act as the client. &lt;em&gt;Impersonation&lt;/em&gt; lets the server perform local access checks as the client; this is the level a typical RPC server requests. &lt;em&gt;Delegation&lt;/em&gt; lets the server forward the client&apos;s identity onto another machine, useful for multi-hop scenarios but a frequent source of relay-style bugs. Almost every Potato lineage attack consumes an &lt;em&gt;Impersonation&lt;/em&gt;-level token; that is enough to call &lt;code&gt;ImpersonateLoggedOnUser&lt;/code&gt; and run as the client [@itm4n-printspoofer-blog].&lt;/p&gt;
&lt;p&gt;Microsoft documents a third token shape, the &lt;em&gt;restricted token&lt;/em&gt;, that is rare in practice but worth understanding because it is the only place in the model where an explicit deny-list lives on the token itself rather than the descriptor. A restricted token combines three knobs: a list of SIDs converted to &lt;em&gt;deny-only&lt;/em&gt; (their grants count for no allow ACE but their presence still triggers deny ACEs), a list of &lt;em&gt;restricting SIDs&lt;/em&gt; that the access check must independently permit, and a list of privileges removed from the token&apos;s privilege set [@ms-learn-restricted-tokens].&lt;/p&gt;
&lt;p&gt;The kernel runs &lt;code&gt;SeAccessCheck&lt;/code&gt; twice and grants only the intersection: &quot;When a restricted process or thread tries to access a securable object, the system performs two access checks: one using the token&apos;s enabled SIDs, and another using the list of restricting SIDs. Access is granted only if both access checks allow the requested access rights&quot; [@ms-learn-restricted-tokens]. Restricted tokens are operationally niche because the same documentation requires applications using them to &quot;run the restricted application on desktops other than the default desktop. This is necessary to prevent an attack by a restricted application, using &lt;code&gt;SendMessage&lt;/code&gt; or &lt;code&gt;PostMessage&lt;/code&gt;, to unrestricted applications on the default desktop&quot; [@ms-learn-restricted-tokens]. Few applications can spare the desktop overhead.&lt;code&gt;whoami /priv&lt;/code&gt; shows &lt;em&gt;available&lt;/em&gt; privileges, not &lt;em&gt;enabled&lt;/em&gt; privileges. The &lt;code&gt;Enabled&lt;/code&gt; column is the load-bearing one: an available-but-disabled privilege does not affect any access check until the process explicitly enables it via &lt;code&gt;AdjustTokenPrivileges&lt;/code&gt;. The discipline of leaving privileges disabled by default is a defence in depth that depends on the application not having an exploitable bug between disable and use.&lt;/p&gt;
&lt;p&gt;A token also carries flags that drive specific runtime behaviours: a &lt;em&gt;split-token&lt;/em&gt; indicator points at a &lt;em&gt;linked&lt;/em&gt; full-administrator counterpart for the UAC scenario in Section 8; an &lt;em&gt;AppContainer&lt;/em&gt; flag plus a Package SID and capability SIDs name an AppContainer-bound process. In every case, the kernel consults the token by handle and trusts the contents. The kernel does not ask how a process obtained the handle. It asks only what the token says.&lt;/p&gt;
&lt;p&gt;This is the property that organises the rest of the article.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &lt;strong&gt;The Windows access token is a bearer credential.&lt;/strong&gt; Whichever process holds the handle gets the rights. The kernel does not ask how the handle was obtained; it asks only what the token says. This single property explains the entire Potato lineage, Mimikatz &lt;code&gt;token::elevate&lt;/code&gt;, and most of the privilege-abuse canon. Once you see it, every later attack section in the article becomes the same bug repeated against a different token-acquisition primitive.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the token is a bearer credential, anyone with a way to obtain a SYSTEM token&apos;s handle is SYSTEM. Every Potato in Section 10 will be a different way to provoke a SYSTEM-token handle into the attacker&apos;s process. But the access check has another input that bypasses the DACL entirely. What is it, and which attackers know about it?&lt;/p&gt;
&lt;h2&gt;6. Privileges as a Different Dimension&lt;/h2&gt;
&lt;p&gt;Privileges are not access rights. They are pre-checked superpowers. They live on the token, they bypass the DACL for specific operations, and they are baked into the kernel for those operations alone.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s framing on Learn is exact: &quot;Privileges differ from access rights in two ways: Privileges control access to system resources and system-related tasks, whereas access rights control access to securable objects&quot; [@ms-learn-privileges]. The same page makes the operational consequence clear: &quot;Most privileges are disabled by default&quot; [@ms-learn-privileges]. A process that holds a privilege but has not enabled it (via &lt;code&gt;AdjustTokenPrivileges&lt;/code&gt;) cannot use it. The discipline is &quot;principle of least privilege at the millisecond level&quot; -- the privilege is on the token, but it does nothing until the program explicitly turns it on for the next system call.&lt;/p&gt;

A named, kernel-recognised authority on the access token that lets the holder perform a specific class of operations the DACL evaluation alone would not permit. Privileges include `SeDebugPrivilege` (read/write any process), `SeImpersonatePrivilege` (act on a client&apos;s token), `SeAssignPrimaryTokenPrivilege` (start a process under a token), `SeBackupPrivilege` (read any file regardless of DACL), `SeRestorePrivilege` (write any file regardless of DACL), `SeTcbPrivilege` (act as the operating system), and `SeLoadDriverPrivilege` (load a kernel driver). Most are disabled by default and must be enabled via `AdjustTokenPrivileges` before use [@ms-learn-privileges].
&lt;p&gt;The reason privileges deserve a section of their own is that &lt;em&gt;five of them are equivalent to &quot;I am SYSTEM&quot;&lt;/em&gt; and the other dozen are housekeeping. The five-versus-housekeeping split is the load-bearing audit decision in any Windows hardening review. Step through them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeDebugPrivilege&lt;/code&gt; lets the holder open any process for full read and write, including processes running as SYSTEM. The privilege exists so that &lt;code&gt;Visual Studio&lt;/code&gt; and &lt;code&gt;WinDbg&lt;/code&gt; can debug code other users have started. The first move in almost every Mimikatz session is &lt;code&gt;privilege::debug&lt;/code&gt;, which enables the privilege the local administrator already has on the token [@github-gentilkiwi-mimikatz, @en-wiki-mimikatz]. Once enabled, the next Mimikatz command opens &lt;code&gt;lsass.exe&lt;/code&gt; and reads the credentials.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; lets the holder accept any token offered by a client and act as that client. The privilege is held by every Windows service account by default -- IIS, SQL Server, the Print Spooler, scheduled tasks, Docker containers, Citrix, the entire population of background services that need to handle authenticated requests. &lt;em&gt;This is the load-bearing privilege for the Potato lineage&lt;/em&gt; [@itm4n-printspoofer-blog]. Section 10 will spend twenty paragraphs on what a service account holding &lt;code&gt;SeImpersonate&lt;/code&gt; can be tricked into doing; the privilege is the entry condition.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt; lets the holder launch a new process under any primary token. Combined with &lt;code&gt;SeImpersonate&lt;/code&gt;, the pair gives an attacker the entire token-replay attack: get an impersonation handle to a SYSTEM token, then call &lt;code&gt;CreateProcessAsUser&lt;/code&gt; to run a command as SYSTEM. itm4n quotes decoder_it on the operational consequence.&lt;/p&gt;

if you have SeAssignPrimaryToken or SeImpersonate privilege, you are SYSTEM. -- decoder_it, quoted by itm4n in the PrintSpoofer disclosure [@itm4n-printspoofer-blog]
&lt;p&gt;&lt;code&gt;SeBackupPrivilege&lt;/code&gt; lets the holder read any file regardless of DACL, on the theory that backup software has to. &lt;code&gt;SeRestorePrivilege&lt;/code&gt; is the symmetric write privilege. The two together mean that a process holding both can rewrite any file on the machine, including service binaries.&lt;/p&gt;
&lt;p&gt;The 2021 &lt;em&gt;HiveNightmare&lt;/em&gt; / &lt;em&gt;SeriousSAM&lt;/em&gt; vulnerability (CVE-2021-36934) is the worked example of what happens when the model assumes nobody but the backup process has read access to a sensitive file and the assumption breaks. The NVD description is exact: &quot;An elevation of privilege vulnerability exists because of overly permissive Access Control Lists (ACLs) on multiple system files, including the Security Accounts Manager (SAM) database&quot; [@nvd-cve-2021-36934]. The DACL on the live &lt;code&gt;\Windows\System32\config\SAM&lt;/code&gt; was correct; the DACL on the &lt;em&gt;Volume Shadow Copy&lt;/em&gt; mirror was overly permissive enough that any local user could read the SAM hashes through the shadow-copy device path.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s fix required not just a patch but a manual operator step: &quot;After installing this security update, you must manually delete all shadow copies of system files, including the SAM database, to fully mitigate this vulnerability&quot; [@nvd-cve-2021-36934]. A patch alone could not erase the historical shadow copies that already had the wrong DACL.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeTcbPrivilege&lt;/code&gt; lets the holder &quot;act as the operating system&quot; -- it is the privilege that grants identity to the kernel itself. Held only by &lt;code&gt;LocalSystem&lt;/code&gt; services in well-administered environments. A non-system process that somehow acquired &lt;code&gt;SeTcb&lt;/code&gt; is, in effect, indistinguishable from the kernel.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeLoadDriverPrivilege&lt;/code&gt; lets the holder load a kernel driver. By itself the privilege is harmless, because the loaded driver still has to be properly signed for HVCI / Driver Signature Enforcement to accept it. Combined with a known-vulnerable signed driver, however, the privilege becomes the entry point for &lt;em&gt;bring-your-own-vulnerable-driver&lt;/em&gt; (BYOVD) attacks: load a benign-looking but exploitable signed driver, then use its bug to execute arbitrary kernel code. Two worked examples bracket the class.&lt;/p&gt;
&lt;p&gt;The kernel-read/write half of the class is best illustrated by Micro-Star&apos;s &lt;code&gt;RTCore64.sys&lt;/code&gt; (CVE-2019-16098 [@nvd-cve-2019-16098]), the MSI Afterburner driver that &quot;allows any authenticated user to read and write to arbitrary memory, I/O ports, and MSRs&quot; [@nvd-cve-2019-16098]. In October 2022, the threat actors behind the BlackByte ransomware weaponised the primitive at scale [@sophos-blackbyte]: the dropper loaded &lt;code&gt;RTCore64.sys&lt;/code&gt;, then walked the kernel&apos;s &lt;code&gt;PspCreateProcessNotifyRoutine&lt;/code&gt; callback array and zeroed every entry, blinding every registered process-creation callback before the encryption stage ran.&lt;/p&gt;
&lt;p&gt;Sophos&apos;s October 4, 2022 disclosure named the technique exactly: &quot;We found a sophisticated technique to bypass security products by abusing a known vulnerability in the legitimate vulnerable driver RTCore64.sys. The evasion technique supports disabling a whopping list of over 1,000 drivers on which security products rely to provide protection&quot; [@sophos-blackbyte].&lt;/p&gt;
&lt;p&gt;The kernel-code-execution half of the class is GIGABYTE&apos;s &lt;code&gt;gdrv.sys&lt;/code&gt; (CVE-2018-19320 [@nvd-cve-2018-19320]). The NVD description states the primitive directly: &quot;The GDrv low-level driver in GIGABYTE APP Center v1.05.21 and earlier... exposes ring0 memcpy-like functionality that could allow a local attacker to take complete control of the affected system&quot; [@nvd-cve-2018-19320]. A signed &lt;code&gt;IOCTL&lt;/code&gt; accepts an attacker-supplied source pointer, destination pointer, and length, and copies kernel memory at ring 0 -- a write-what-where primitive that the attacker can compose with the read-anywhere primitive of &lt;code&gt;RTCore64.sys&lt;/code&gt; to mint arbitrary kernel code execution.&lt;/p&gt;
&lt;p&gt;CISA added GIGABYTE Multiple Products to the Known Exploited Vulnerabilities Catalog on October 24, 2022 with a remediation due date of November 14, 2022, citing in-the-wild exploitation [@nvd-cve-2018-19320]. The U.S. federal-civilian executive branch had two weeks to remediate; the rest of the install base did not.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s structural answer is the Microsoft-recommended driver blocklist, enabled by default on every device since the Windows 11 2022 update [@ms-learn-driver-blocklist]. The Learn page is exact about coverage: the blocklist targets drivers with &quot;known security vulnerabilities that an attacker could exploit to elevate privileges in the Windows kernel&quot;, and explicitly catches drivers whose behaviours &quot;circumvent the Windows Security Model&quot; [@ms-learn-driver-blocklist].&lt;/p&gt;
&lt;p&gt;Both &lt;code&gt;RTCore64.sys&lt;/code&gt; and &lt;code&gt;gdrv.sys&lt;/code&gt; appear on the blocklist; the ride from disclosure to default-on enforcement was four years for &lt;code&gt;RTCore64&lt;/code&gt;, four years for &lt;code&gt;gdrv&lt;/code&gt;, and the same arc applies to every member of the class. Honourable mention: &lt;code&gt;aswArPot.sys&lt;/code&gt; (CVE-2022-26522 / CVE-2022-26523 [@sentinelone-avast-avg]) shows the same pattern from a security-product driver, with SentinelLabs reporting &quot;two high severity flaws in Avast and AVG... that went undiscovered for years affecting dozens of millions of users&quot; before the silent fix [@sentinelone-avast-avg].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On a Windows machine, holding any of &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt;, &lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt;, &lt;code&gt;SeBackupPrivilege&lt;/code&gt;, or &lt;code&gt;SeRestorePrivilege&lt;/code&gt; is operationally indistinguishable from being SYSTEM. The other privileges in the standard token (the long tail of &lt;code&gt;SeShutdown&lt;/code&gt;, &lt;code&gt;SeIncreaseWorkingSet&lt;/code&gt;, &lt;code&gt;SeTimeZone&lt;/code&gt;, &lt;code&gt;SeChangeNotify&lt;/code&gt;, &lt;code&gt;SeUndock&lt;/code&gt;, &lt;code&gt;SeIncreaseQuota&lt;/code&gt;...) are housekeeping. Audit the holders of the five accordingly: any non-LocalSystem-equivalent account that holds them is a target.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The DACL is the load-bearing thing for &lt;em&gt;file&lt;/em&gt; access, but exactly &lt;em&gt;one bad DACL&lt;/em&gt; on a sensitive file ends the model. HiveNightmare proved that the cost of getting a single security descriptor wrong on a single file is the entire credential database. The general rule the lesson encodes: every primitive in Section 3&apos;s input list has at least one production example where misuse of that primitive alone dropped the security model to zero.&lt;/p&gt;
&lt;p&gt;Discretionary access control assumes the principal -- the user -- is the right unit of authorisation. By 2006, exploitable user-mode code had proven the principal was wrong. What did Microsoft do?&lt;/p&gt;
&lt;h2&gt;7. Mandatory Integrity Control: Stapling No-Write-Up onto DAC&lt;/h2&gt;
&lt;p&gt;November 2006. Vista releases to manufacturing, and for the first time in Windows history, the kernel&apos;s access check fires &lt;em&gt;before&lt;/em&gt; the DACL walk -- on something other than the user&apos;s identity. The new layer is &lt;em&gt;Mandatory Integrity Control&lt;/em&gt; (MIC), and it adds a four-level lattice to every securable object in the system.&lt;/p&gt;
&lt;p&gt;Microsoft Learn frames MIC compactly. &quot;MIC uses integrity levels and mandatory policy to evaluate access. Security principals and securable objects are assigned integrity levels that determine their levels of protection or access. For example, a principal with a low integrity level cannot write to an object with a medium integrity level, even if that object&apos;s DACL allows write access to the principal&quot; [@ms-learn-mic]. The same page enumerates the levels: &quot;Windows defines four integrity levels: low, medium, high, and system&quot; [@ms-learn-mic]. The levels are encoded as four well-known SIDs: Low (S-1-16-4096), Medium (S-1-16-8192), High (S-1-16-12288), System (S-1-16-16384) [@en-wiki-mic].&lt;/p&gt;

A Windows kernel mechanism, introduced with Vista (released to manufacturing November 8, 2006; consumer general availability January 30, 2007 [@en-wiki-windows-vista]), that adds an integrity-level check to `SeAccessCheck`. Each securable object carries an integrity label inside its SACL; each access token carries an integrity level. An access whose direction violates the configured *mandatory policy* (typically *no-write-up*) is denied at the integrity check before the DACL walk runs. MIC is the mandatory layer the Windows DAC model historically lacked [@ms-learn-mic].

A linearly-ordered tag (Low / Medium / High / System) carried on every Windows process token and every securable object. The kernel uses the relative ordering to enforce mandatory policy independent of the object&apos;s DACL. The four well-known SIDs are S-1-16-4096 (Low), S-1-16-8192 (Medium), S-1-16-12288 (High), and S-1-16-16384 (System) [@en-wiki-mic].
&lt;p&gt;The default policy is &lt;em&gt;no-write-up&lt;/em&gt;. A process at integrity level $L$ cannot modify an object at integrity level greater than $L$, regardless of what the DACL says. Microsoft&apos;s example is the load-bearing one: a process running at Low IL cannot write to a Medium-IL object even if the DACL says Everyone has full control. The integrity check fires &lt;em&gt;before&lt;/em&gt; the DACL walk; if the integrity check denies, the DACL is not consulted [@ms-learn-mic].&lt;/p&gt;
&lt;p&gt;The integrity label is stored as a &lt;code&gt;SYSTEM_MANDATORY_LABEL_ACE&lt;/code&gt; inside the SACL, not the DACL [@ms-learn-system-mandatory-label-ace]. The mask field on the label ACE encodes which directions of access the policy forbids: &lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_WRITE_UP (0x1)&lt;/code&gt;, &lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_READ_UP (0x2)&lt;/code&gt;, and &lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP (0x4)&lt;/code&gt; [@ms-learn-system-mandatory-label-ace].&lt;/p&gt;
&lt;p&gt;Storing the label in the SACL is a deliberate choice with one operational consequence: tools that copy the DACL but not the SACL silently drop the integrity label. The most common consequence is a Low-IL file getting copied to a new location and emerging with no integrity label, which defaults to Medium and unintentionally raises the object&apos;s protection. The opposite mistake -- a Medium-IL file losing its label and dropping to Low -- is the more dangerous one.&lt;/p&gt;
&lt;p&gt;The no-write-up mask is the one to memorise, because it is the policy almost every label uses. When a Low-IL caller tries to act on a Medium-IL object, the kernel denies any access whose mapped result contains write-class bits, including the standard-rights &lt;code&gt;WRITE_DAC&lt;/code&gt; (bit 18 of the 32-bit ACCESS_MASK [@ms-learn-access-mask]) and &lt;code&gt;WRITE_OWNER&lt;/code&gt; (bit 19), the type-generic &lt;code&gt;DELETE&lt;/code&gt; (bit 16), and the file-specific &lt;code&gt;FILE_WRITE_DATA&lt;/code&gt; (0x2), &lt;code&gt;FILE_APPEND_DATA&lt;/code&gt; (0x4), &lt;code&gt;FILE_WRITE_EA&lt;/code&gt; (0x10), and &lt;code&gt;FILE_WRITE_ATTRIBUTES&lt;/code&gt; (0x100) [@ms-learn-file-access-rights].&lt;/p&gt;
&lt;p&gt;The presence of &lt;code&gt;FILE_APPEND_DATA&lt;/code&gt; in that list matters operationally: a careless reader of the spec might assume that &quot;append&quot; semantics escape the no-write-up rule because they do not modify existing bytes. They do not. MIC denies both write modes, so log-only append handlers cannot be used as a write-up channel into a higher-IL object.&lt;/p&gt;
&lt;p&gt;A second rule completes the model: &lt;em&gt;process integrity inheritance&lt;/em&gt;. When a process is created, the kernel assigns it the &lt;em&gt;minimum&lt;/em&gt; of the user&apos;s integrity level and the file&apos;s integrity level [@ms-learn-mic]. A medium-IL user running a low-IL executable gets a low-IL process. This is the rule that lets Internet Explorer 7 run at Low even when launched from a Medium user session.&lt;/p&gt;
&lt;p&gt;The first MIC consumer was IE7 &lt;em&gt;Protected Mode&lt;/em&gt;, which shipped with Windows Vista RTM (released to manufacturing November 8, 2006) [@wayback-skywing-uninformed-v8a6, @en-wiki-windows-vista]. (IE7 standalone for Windows XP, released October 18, 2006 [@en-wiki-ie7], runs without Protected Mode -- the feature depends on Vista&apos;s MIC kernel layer.) Skywing&apos;s &lt;em&gt;Uninformed&lt;/em&gt; Volume 8 Article 6, &quot;Getting Out of Jail: Escaping Internet Explorer Protected Mode,&quot; is the first public reverse-engineering of the implementation.&lt;/p&gt;
&lt;p&gt;Skywing&apos;s framing remains the most-cited primer on the subject: &quot;With the introduction of Windows Vista, Microsoft has added a new form of mandatory access control to the core operating system. Internally known as &apos;integrity levels&apos;, this new addition to the security manager allows security controls to be placed on a per-process basis. This is different from the traditional model of per-user security controls used in all prior versions of Windows NT&quot; [@wayback-skywing-uninformed-v8a6]. IE7&apos;s Protected Mode pattern -- a Low-IL worker that does the dangerous parsing, paired with a Medium-IL broker that performs the system-changing operations on the worker&apos;s behalf -- became the template Windows would later generalise into AppContainer.&lt;em&gt;User Interface Privilege Isolation&lt;/em&gt; (UIPI) is the window-message gate that uses MIC at the desktop. A Low-IL window cannot send &lt;code&gt;SendMessage&lt;/code&gt; or &lt;code&gt;PostMessage&lt;/code&gt; traffic to a Medium-IL or higher window. UIPI is the reason you cannot click-jack a UAC consent prompt from a normally-running browser process: the consent prompt runs at High IL, the browser runs at Medium [@en-wiki-mic].&lt;/p&gt;
&lt;p&gt;Windows 8 generalised the MIC pattern into &lt;em&gt;AppContainer&lt;/em&gt;. An AppContainer process gets a fresh Low-IL token plus an &lt;em&gt;AppContainer&lt;/em&gt; flag, a Package SID that identifies the app, and a list of capability SIDs the app declared in its manifest. Microsoft Learn states the resulting isolation directly: &quot;Windows ensures that processes running with a low integrity level cannot obtain access to a process which is associated with an app container&quot; [@ms-learn-mic]. The Package SID and capability SID derivations are the subject of the App Identity sibling article in this series; we will not redefine them here [@app-identity-sibling].&lt;/p&gt;
&lt;p&gt;MIC fixed the integrity boundary inside the kernel. But the same year shipped a separate retrofit for a different problem: why was the consumer admin running every clicked-on &lt;code&gt;.exe&lt;/code&gt; with full administrative authority? And why did Microsoft refuse to call its answer a security boundary?&lt;/p&gt;
&lt;h2&gt;8. UAC, the Split-Token, and the Bypass Tradition&lt;/h2&gt;
&lt;p&gt;Vista&apos;s &lt;em&gt;User Account Control&lt;/em&gt; is the most famous Windows security retrofit, and the only one whose own keepers explicitly published a document declaring it not a security boundary [@msrc-servicing-criteria]. The mechanism is precise. The bypass tradition is enormous. The classification is honest.&lt;/p&gt;
&lt;p&gt;The mechanism first. Microsoft&apos;s documentation on how UAC works gives the verbatim recipe [@ms-learn-uac]: &quot;When an administrator logs on, two separate access tokens are created for the user: a standard user access token and an administrator access token. The standard user access token... contains the same user-specific information as the administrator access token, but the administrative Windows privileges and SIDs are removed... is used to display the desktop by executing the process &lt;code&gt;explorer.exe&lt;/code&gt;. &lt;code&gt;Explorer.exe&lt;/code&gt; is the parent process from which all other user-initiated processes inherit their access token. As a result, all apps run as a standard user unless a user provides consent or credentials to approve an app to use a full administrative access token.&quot;&lt;/p&gt;

A Windows mechanism, introduced with Vista (released to manufacturing November 8, 2006 [@en-wiki-windows-vista]), in which an administrative user receives two linked tokens at logon: a *filtered* Medium-IL token without administrative privileges or SIDs, used by `explorer.exe` and every process descended from it, and a *full* High-IL administrative counterpart that the kernel hands out only after the user clicks through a consent prompt or supplies credentials. The two tokens reference each other through the `LinkedToken` field. UAC is a UX-and-default-behaviour mechanism, not an enforced security boundary [@ms-learn-uac, @msrc-servicing-criteria].
&lt;p&gt;The split-token mechanism produces three elevation triggers. First, an executable can declare its required level in its manifest via the &lt;code&gt;requestedExecutionLevel&lt;/code&gt; element (&lt;code&gt;asInvoker&lt;/code&gt;, &lt;code&gt;highestAvailable&lt;/code&gt;, or &lt;code&gt;requireAdministrator&lt;/code&gt;). Second, certain Microsoft-signed binaries appear on an &lt;em&gt;AutoElevate&lt;/em&gt; whitelist that the kernel consults; processes on the whitelist transparently get the full token without prompting. Third, COM components can be marked elevatable via the &lt;em&gt;COM Elevation Moniker&lt;/em&gt;, which lets code instantiate &lt;code&gt;Elevation:Administrator!new:{guid}&lt;/code&gt; (or &lt;code&gt;Elevation:Highest!new:{guid}&lt;/code&gt;) to obtain a High-IL administrator COM caller -- not a SYSTEM caller; the moniker&apos;s supported run levels are &lt;code&gt;Administrator&lt;/code&gt; and &lt;code&gt;Highest&lt;/code&gt; [@ms-learn-com-elevation-moniker]. Method 41 (Oddvar Moe&apos;s &lt;code&gt;ICMLuaUtil&lt;/code&gt; construction) is the canonical worked example.&lt;/p&gt;
&lt;p&gt;The classification next. Microsoft&apos;s &lt;em&gt;Security Servicing Criteria for Windows&lt;/em&gt; defines a security boundary as the logical separation between security domains with different trust levels, and gives the kernel-mode / user-mode separation as the canonical example [@msrc-servicing-criteria]. The criteria document then enumerates which boundaries Microsoft commits to servicing. UAC and admin-to-kernel are not on the enumerated list.&lt;/p&gt;

A security boundary provides a logical separation between the code and data of security domains with different levels of trust... the separation between kernel mode and user mode is a classic [...] security boundary. Microsoft software depends on multiple security boundaries to isolate devices on the network, virtual machines, and applications on a device. -- Microsoft Security Servicing Criteria for Windows [@msrc-servicing-criteria]
&lt;p&gt;The &quot;outside the enumerated list&quot; classification has a concrete consequence: bypasses of UAC are not eligible for the same security-update treatment a kernel-mode-to-user-mode bypass would get. Mitigations are issued per-redirect, when an attacker&apos;s specific path becomes operationally noisy enough to warrant attention. The seventy-plus methods catalogued in UACMe are the empirical consequence.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &lt;strong&gt;UAC was never a security boundary.&lt;/strong&gt; The seventy-plus methods catalogued in UACMe are not bugs in UAC. They are the formal consequence of UAC&apos;s classification. Once you recognise that UAC is a UX-and-default-behaviour mechanism rather than an enforced boundary, the bypass tradition is legible as a feature being used as designed and the structural arc to Adminless makes sense.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The bypass canon. Walk it generation by generation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Method 1 -- Leo Davidson, 2009.&lt;/strong&gt; Davidson&apos;s &lt;em&gt;Windows 7 UAC Whitelist&lt;/em&gt; writeup is the genealogical root of the UAC bypass tradition [@pretentiousname-davidson, @github-hfiref0x-uacme]. He noticed that &lt;code&gt;sysprep.exe&lt;/code&gt; is on the AutoElevate whitelist and that, when launched from &lt;code&gt;C:\Windows\System32\sysprep\&lt;/code&gt;, it loads several DLLs from the working directory. Use the &lt;code&gt;IFileOperation&lt;/code&gt; COM interface (which the elevator treats as AutoApprove, enabling the file copy without prompting) to drop a malicious &lt;code&gt;cryptbase.dll&lt;/code&gt; into &lt;code&gt;%SystemRoot%\System32\sysprep\&lt;/code&gt;. Then trigger &lt;code&gt;sysprep.exe&lt;/code&gt; -- which is on the AutoElevate whitelist -- and the auto-elevated process loads the attacker&apos;s DLL, and the attacker has a High-IL full administrative token.&lt;/p&gt;
&lt;p&gt;Davidson&apos;s writeup quotes himself bluntly: &quot;This works against the RTM (retail) and RC1 versions of Windows 7&quot; [@pretentiousname-davidson]. UACMe Method 1 records the technique with structured metadata: &quot;Author: Leo Davidson / Type: Dll Hijack / Method: IFileOperation / Target(s): \system32\sysprep\sysprep.exe / Component(s): cryptbase.dll / Implementation: ucmStandardAutoElevation / Works from: Windows 7 (7600) / Fixed in: Windows 8.1 (9600)&quot; [@github-hfiref0x-uacme].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Method 25 -- Matt Nelson (enigma0x3), August 15, 2016.&lt;/strong&gt; Seven years after Davidson, Nelson published &quot;Fileless UAC Bypass Using &lt;code&gt;eventvwr.exe&lt;/code&gt; and Registry Hijacking&quot; [@enigma0x3-eventvwr-blog]. The bypass replaces the file-system DLL hijack with a registry redirect. Nelson noticed that &lt;code&gt;eventvwr.exe&lt;/code&gt;, an AutoElevated binary, queries &lt;code&gt;HKCU\Software\Classes\mscfile\shell\open\command&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; &lt;code&gt;HKCR\mscfile\shell\open\command&lt;/code&gt; to find the command to run for the &lt;code&gt;mscfile&lt;/code&gt; ProgID. His verbatim observation: &quot;From the output, it appears that &apos;eventvwr.exe&apos;, as a high integrity process, queries both HKCU and HKCR registry hives to start mmc.exe&quot; [@enigma0x3-eventvwr-blog]. HKCU is writable by the standard user; the user writes a malicious command line under that key, runs &lt;code&gt;eventvwr.exe&lt;/code&gt;, and the auto-elevated process happily executes the user-supplied command line. The first &lt;em&gt;fileless&lt;/em&gt; UAC bypass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Method 33 -- winscripting.blog, 2017.&lt;/strong&gt; Same primitive, different target. The &lt;code&gt;fodhelper.exe&lt;/code&gt; binary is on the AutoElevate whitelist and queries &lt;code&gt;HKCU\Software\Classes\ms-settings\shell\open\command&lt;/code&gt; to launch the Settings app. UACMe records the credit precisely: &quot;Author: winscripting.blog / Type: Shell API / Method: Registry key manipulation / Target(s): \system32\fodhelper.exe / Component(s): Attacker defined / Implementation: ucmShellRegModMethod / Works from: Windows 10 TH1 (10240) / Fixed in: unfixed&quot; [@github-hfiref0x-uacme]. &lt;em&gt;Fixed in: unfixed.&lt;/em&gt; This is what &quot;outside the enumerated list&quot; looks like in practice: nine years after Method 1 and a year after Method 25 demonstrated the underlying class, the registry-redirect template was still being applied to fresh AutoElevate targets and shipping unmitigated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Method 41 -- Oddvar Moe.&lt;/strong&gt; The COM elevation moniker route. UACMe records: &quot;Author: Oddvar Moe / Type: Elevated COM interface / Method: ICMLuaUtil&quot; [@github-hfiref0x-uacme]. Instantiate the &lt;code&gt;CMLuaUtil&lt;/code&gt; COM object via the elevation moniker from a Medium-IL process, get back a High-IL COM caller, and call its &lt;code&gt;ShellExec&lt;/code&gt; method to run an attacker command line at High IL. The seam is the COM moniker registry&apos;s &lt;code&gt;Elevation\Enabled&lt;/code&gt; key, which marks specific CLSIDs as elevation-capable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Method 31 (sdclt), 29, 34, ...&lt;/strong&gt; The pattern repeats. Matt Nelson&apos;s &lt;code&gt;sdclt.exe&lt;/code&gt; variants exploit the backup-restore UI&apos;s registry lookups. Forshaw&apos;s &lt;code&gt;schtasks&lt;/code&gt; variant exploits the scheduled-task COM interface. The UACMe README enumerates the lot with the laconic one-liner &quot;Defeating Windows User Account Control by abusing built-in Windows AutoElevate backdoor&quot; [@github-hfiref0x-uacme]. Most of the methods reduce to the same primitive: an AutoElevated Microsoft-signed binary performs a lookup that the standard user can redirect, the standard user supplies an attacker-controlled answer, and the auto-elevated binary executes attacker-controlled work.&lt;/p&gt;

flowchart TD
    A[User Medium-IL process] --&amp;gt; B[Write attacker command line into HKCU writable seam: registry key, file system path, scheduled-task XML]
    B --&amp;gt; C[Trigger AutoElevated Microsoft-signed binary: sysprep.exe / eventvwr.exe / fodhelper.exe / sdclt.exe / etc.]
    C --&amp;gt; D[AutoElevate flag honoured by kernel]
    D --&amp;gt; E[Binary launched at High IL with full administrative token]
    E --&amp;gt; F[Binary performs lookup: HKCU registry / DLL search path / scheduled-task definition]
    F --&amp;gt; G[Lookup returns attacker-supplied content]
    G --&amp;gt; H[High-IL process executes attacker work]

Mark Russinovich&apos;s June 2007 *TechNet Magazine* cover story, &quot;Inside Windows Vista User Account Control,&quot; is the canonical practitioner walkthrough of the split-token model and is preserved on the Wayback Machine [@wayback-russinovich-uac-technet]. Russinovich opens by naming the misunderstanding: &quot;User Account Control (UAC) is an often misunderstood feature in Windows Vista... In this article I discuss the problems UAC solves and describe the architecture and implementation of its component technologies.&quot; The framing throughout the article is that UAC&apos;s purpose is to create the *expectation* that consumer software would run as a standard user, and to push the developer community to refactor away from gratuitous administrator requirements. That framing -- UAC as a UX and migration mechanism -- is consistent with the eventual MSRC servicing-criteria position: not a defended boundary, but a behaviour gate.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft&apos;s servicing-criteria position means a UAC bypass that does not also cross a serviced security boundary is not, by policy, eligible for a security update. Mitigations land when the operational footprint of a particular bypass becomes large enough to justify one. Track UAC mitigations by KB number, not by feature description; consult the UACMe README&apos;s &lt;code&gt;Fixed in:&lt;/code&gt; field as the institutional memory [@github-hfiref0x-uacme, @msrc-servicing-criteria].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;UAC bypasses redirect the elevation flow that produces a token. A different attack family takes the result and steals tokens from already-elevated SYSTEM services. Where do those attacks come from, and why are they all the same bug?&lt;/p&gt;
&lt;h2&gt;9. The Object Manager and the Lookup Surface&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SeAccessCheck&lt;/code&gt; evaluates a security descriptor it has been handed. Who hands it the descriptor?&lt;/p&gt;
&lt;p&gt;The kernel&apos;s &lt;em&gt;Object Manager&lt;/em&gt; does, after walking a name. Every named kernel object lives somewhere in a hierarchical namespace rooted at &lt;code&gt;\&lt;/code&gt;. Devices live under &lt;code&gt;\Device&lt;/code&gt;. Synchronisation primitives live under &lt;code&gt;\BaseNamedObjects&lt;/code&gt;. Pre-resolved DLL names live under &lt;code&gt;\KnownDlls&lt;/code&gt;. Per-session subtrees live under &lt;code&gt;\Sessions&lt;/code&gt;. The DOS device prefix &lt;code&gt;\GLOBAL??&lt;/code&gt; (and its session-local sibling &lt;code&gt;\??&lt;/code&gt;) holds drive-letter symbolic links into the device tree. When a process calls &lt;code&gt;OpenObject&lt;/code&gt;, the Object Manager parses the name, walks the tree, and returns the object whose security descriptor &lt;code&gt;SeAccessCheck&lt;/code&gt; will then evaluate.&lt;/p&gt;
&lt;p&gt;The kernel performs &lt;em&gt;the lookup&lt;/em&gt; before &lt;em&gt;the access check&lt;/em&gt;. This sequencing creates a parallel attack surface that bypasses &lt;code&gt;SeAccessCheck&lt;/code&gt; entirely. If the attacker can influence the name resolution -- redirect a &lt;code&gt;\??\&lt;/code&gt; symbolic link, plant a junction in NTFS that re-targets a directory traversal, hardlink a low-privilege file at a path the kernel will trust because of the parent directory&apos;s descriptor -- then by the time &lt;code&gt;SeAccessCheck&lt;/code&gt; runs, it is being asked about a different object than the original code path intended to open.&lt;/p&gt;
&lt;p&gt;The HiveNightmare lookup path is the canonical worked example. The exploit reads the SAM database not via &lt;code&gt;\Windows\System32\config\SAM&lt;/code&gt; (which has a tight DACL) but via &lt;code&gt;\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy*\Windows\System32\config\SAM&lt;/code&gt;. That path resolves through the Object Manager&apos;s &lt;code&gt;\GLOBAL??&lt;/code&gt; symbolic link, into the device tree, into a Volume Shadow Copy mirror of the original volume, into a &lt;em&gt;copy&lt;/em&gt; of &lt;code&gt;SAM&lt;/code&gt; whose DACL was, until the August 2021 patch, readable by any authenticated local user (BUILTIN\Users) [@nvd-cve-2021-36934].&lt;/p&gt;
&lt;p&gt;The lookup-phase attack class is wider than file-system shadow copies. &lt;em&gt;Object Manager symbolic links&lt;/em&gt; and &lt;em&gt;NTFS hard links&lt;/em&gt; both produce the same primitive: the kernel resolves a name through an attacker-influenced redirect and ends up evaluating &lt;code&gt;SeAccessCheck&lt;/code&gt; against a different security descriptor than the calling code intended.&lt;/p&gt;
&lt;p&gt;CVE-2020-0668, the Service Tracing elevation-of-privilege bug Clément Labro disclosed in February 2020, is the textbook symbolic-link case [@itm4n-cve-2020-0668-blog]. The Service Tracing infrastructure under &lt;code&gt;HKLM\SOFTWARE\Microsoft\Tracing&lt;/code&gt; is user-writable, and several SYSTEM services -- &lt;code&gt;IKEEXT&lt;/code&gt;, &lt;code&gt;RasMan&lt;/code&gt;, the Update Session Orchestrator service -- consult those keys to find a tracing log path. When the log file exceeds the configured &lt;code&gt;MaxFileSize&lt;/code&gt;, the service renames it from &lt;code&gt;MODULE.LOG&lt;/code&gt; to &lt;code&gt;MODULE.OLD&lt;/code&gt;, deleting any existing &lt;code&gt;MODULE.OLD&lt;/code&gt; first.&lt;/p&gt;
&lt;p&gt;itm4n&apos;s exploit is exactly the one his blog post names: &quot;All you need to do is set the target directory as a mountpoint to the &lt;code&gt;\RPC Control&lt;/code&gt; object directory and then create two symbolic links: A symbolic link from &lt;code&gt;MODULE.LOG&lt;/code&gt; to a file you own; A symbolic link from &lt;code&gt;MODULE.OLD&lt;/code&gt; to any file on the file system&quot; [@itm4n-cve-2020-0668-blog]. The mountpoint reroutes the kernel&apos;s name resolution into an Object Manager directory the attacker controls; the two symlinks reroute the rename operation; the SYSTEM service ends up performing an arbitrary file move with kernel authority. Forshaw&apos;s &lt;code&gt;googleprojectzero/symboliclink-testing-tools&lt;/code&gt; repository [@github-projectzero-symlink-tools] provides the primitive library the exploit consumes -- &lt;code&gt;CreateSymlink&lt;/code&gt;, &lt;code&gt;CreateMountPoint&lt;/code&gt;, &lt;code&gt;BaitAndSwitch&lt;/code&gt; -- and the repository is, in effect, the institutional library every Object Manager lookup-phase attack of the past decade has linked against.&lt;/p&gt;
&lt;p&gt;The NTFS hard-link case predates the symbolic-link case by half a decade. James Forshaw&apos;s December 2015 Project Zero post, &quot;Between a Rock and a Hard Link,&quot; is the canonical primary source [@projectzero-blog-rockandhardlink]. Forshaw observes that hard links have been a feature of NTFS &quot;since it was originally designed&quot;, and that their relevance to local privilege escalation is exactly the lookup-vs-access-check sequencing this section describes: &quot;Why are hard links useful for local privilege escalation? One type of vulnerability is exploited by a file planting attack, where a privilege service tries to write to a file in a known location&quot; [@projectzero-blog-rockandhardlink].&lt;/p&gt;
&lt;p&gt;The worked example Forshaw walks is CVE-2015-4481, a Mozilla Maintenance Service hard-link primitive: any user can write a status log to &lt;code&gt;C:\ProgramData\Mozilla\logs\maintenanceservice.log&lt;/code&gt;, and during the service&apos;s pre-write &lt;code&gt;BackupOldLogs&lt;/code&gt; rename a brief window opens in which the attacker can replace the about-to-be-written log path with a hard link to an arbitrary system file. The service&apos;s subsequent write -- which it performs with its own SYSTEM authority -- ends up overwriting the system file. The DACL on the source file (the Mozilla log directory) was correct; the DACL on the destination file (the system binary) was correct; the kernel arrived at the destination by resolving a hard-link name the attacker had planted, and &lt;code&gt;SeAccessCheck&lt;/code&gt; saw only the destination DACL, not the planted-link DACL [@projectzero-blog-rockandhardlink]. Microsoft&apos;s MS15-115 mitigation tightened the kernel&apos;s hard-link semantics for sandboxed callers (the kernel&apos;s &lt;code&gt;NtSetInformationFile&lt;/code&gt; now requires &lt;code&gt;FILE_WRITE_ATTRIBUTES&lt;/code&gt; on the target handle when the caller&apos;s token has the sandboxed-token flag, matching what the user-mode &lt;code&gt;CreateHardLink&lt;/code&gt; wrapper had always opened the target with). The fix closes the sandboxed-process branch of the bug class but, as Forshaw notes, does nothing for the Maintenance Service vulnerability itself, which is exploited by a non-sandboxed local user; the structural fix is to write the log to a directory the user cannot create files in, not to enforce a hard-link mask -- a structural fix to the lookup phase, not the access-check phase.&lt;/p&gt;
&lt;p&gt;The two examples generalise the rule. The kernel resolves names &lt;em&gt;before&lt;/em&gt; checking the requesting user&apos;s authority over the destination. The DACL at &lt;code&gt;target.path&lt;/code&gt; and the DACL at &lt;code&gt;attacker.planted.path&lt;/code&gt; after a junction or hard-link redirect can be different; &lt;code&gt;SeAccessCheck&lt;/code&gt; evaluates the descriptor it arrives at, not the descriptor the original caller intended. Capability systems would resolve names through unforgeable handles instead of strings, and the redirect class would not exist by construction [@en-wiki-capability-based-security]. Windows checks the descriptor on every &lt;code&gt;OpenObject&lt;/code&gt; because the name is a forgeable string. The Object Manager namespace is therefore an attack surface whose load-bearing fix is structural rather than per-bug.The Object Manager&apos;s namespace is not documented as policy in the same way &lt;code&gt;SeAccessCheck&lt;/code&gt;&apos;s algorithm is. The de facto modern documentation is empirical: practitioners enumerate the namespace with tools that read kernel structures directly. James Forshaw&apos;s NtObjectManager PowerShell module, part of the Project Zero &lt;code&gt;sandbox-attacksurface-analysis-tools&lt;/code&gt; repository, is the dominant such tool [@github-projectzero-sandbox-attacksurface]. The repository&apos;s banner is exact: &quot;NtObjectManager: A powershell module which uses NtApiDotNet to expose the NT object manager.&quot;&lt;/p&gt;
&lt;p&gt;The James Forshaw / Project Zero corpus is the systematic reference for the Object Manager attack surface. Forshaw&apos;s &quot;Sharing a Logon Session a Little Too Much&quot; (April 2020) names a primitive PrintSpoofer would later consume: when the LSA creates a token for a new logon session, it caches the token for later retrieval. Forshaw&apos;s verbatim explanation: &quot;when LSASS creates a Token for a new Logon session it stores that Token for later retrieval. For the most part this isn&apos;t that useful, however there is one case where the session Token is repurposed, network authentication&quot; [@tiraniddo-sharing-logon-session]. The cached token plus a named-pipe path-validation bug becomes a non-DCOM SYSTEM-token primitive that no DACL touches.&lt;/p&gt;
&lt;p&gt;The lookup surface is half the attack story. The other half is the token surface, and the canonical example of the token surface is a six-year, eight-tool lineage.&lt;/p&gt;
&lt;h2&gt;10. The Potato Lineage: Eight Tools, One Bug (2016-2021)&lt;/h2&gt;
&lt;p&gt;January 16, 2016. Stephen Breen of Foxglove Security publishes &quot;Hot Potato.&quot; The disclosure post opens with a sentence the article will earn the right to repeat: &quot;Microsoft is aware of all of these issues and has been for some time (circa 2000). These are unfortunately hard to fix without breaking backward compatibility and have been [used] by attackers for over 15 years&quot; [@foxglove-hot-potato-blog]. Six years and seven tools later, that admission still describes the situation.&lt;/p&gt;
&lt;p&gt;The single underlying primitive. A low-privileged service account holding &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; (which IIS, SQL Server, the Print Spooler, scheduled tasks, Docker, Citrix, and almost every managed-service account hold by default) tricks SYSTEM into authenticating to a TCP, RPC, or named-pipe endpoint the attacker controls. The attacker&apos;s endpoint accepts the authentication, ends up with an impersonation handle to a SYSTEM token, and calls &lt;code&gt;ImpersonateLoggedOnUser&lt;/code&gt; followed by &lt;code&gt;CreateProcessAsUser&lt;/code&gt; to run arbitrary code as SYSTEM. Same primitive, eight tools, six years.&lt;/p&gt;

sequenceDiagram
    participant Attacker as Attacker (service account, SeImpersonate)
    participant Coercer as Coercion primitive (NBNS / DCOM / EFSRPC / Spooler RPC)
    participant SYSTEM as SYSTEM service
    participant Endpoint as Attacker-controlled local endpoint
    Attacker-&amp;gt;&amp;gt;Coercer: Trigger coercion (e.g. EfsRpcOpenFileRaw, BITS CoGetInstanceFromIStorage)
    Coercer-&amp;gt;&amp;gt;SYSTEM: Tell SYSTEM to authenticate
    SYSTEM-&amp;gt;&amp;gt;Endpoint: NTLM authentication to attacker endpoint
    Endpoint-&amp;gt;&amp;gt;Endpoint: Accept NTLM, build impersonation token
    Attacker-&amp;gt;&amp;gt;Attacker: ImpersonateLoggedOnUser(SYSTEM token)
    Attacker-&amp;gt;&amp;gt;Attacker: CreateProcessAsUser(SYSTEM token, &quot;cmd.exe&quot;)
    Note over Attacker: SYSTEM shell
&lt;p&gt;Walk the lineage one paragraph at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hot Potato (Stephen Breen / Foxglove, January 2016)&lt;/strong&gt; [@foxglove-hot-potato-blog]. The original. Hot Potato chains three Windows defaults: NBT-NS (NetBIOS name service) spoofing on the local network, the Web Proxy Auto-Discovery (WPAD) protocol&apos;s automatic proxy lookup, and Windows Update&apos;s HTTP-to-SMB NTLM relay. The exploit poisons NBT-NS so that the SYSTEM-running Windows Update service resolves &lt;code&gt;WPAD&lt;/code&gt; to the attacker&apos;s local listener; the attacker&apos;s listener serves a proxy configuration that proxies SMB through localhost; the Windows Update service authenticates to the attacker&apos;s localhost SMB endpoint as SYSTEM. Foxglove&apos;s verbatim summary: &quot;Hot Potato (aka: Potato) takes advantage of known issues in Windows to gain local privilege escalation in default configurations, namely NTLM relay (specifically HTTP-&amp;gt;SMB relay) and NBNS spoofing&quot; [@foxglove-hot-potato-blog].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rotten Potato (Stephen Breen and Chris Mallz / Foxglove, September 2016)&lt;/strong&gt; [@foxglove-rotten-potato-blog]. The successor abandons the network-protocol fragility for a DCOM activation trick that James Forshaw had published as Project Zero issue 325. The attacker calls &lt;code&gt;CoGetInstanceFromIStorage&lt;/code&gt; with the BITS CLSID &lt;code&gt;{4991d34b-80a1-4291-83b6-3328366b9097}&lt;/code&gt; and a custom marshalled &lt;code&gt;IStorage&lt;/code&gt; pointer; the COM activation runs on the local DCOM server (which runs as SYSTEM) and authenticates back to a TCP listener the attacker controls.&lt;/p&gt;
&lt;p&gt;The Foxglove blog states the three steps verbatim: &quot;1. Trick the &apos;NT AUTHORITY\SYSTEM&apos; account into authenticating via NTLM to a TCP endpoint we control. 2. Man-in-the-middle this authentication attempt (NTLM relay) to locally negotiate a security token for the &apos;NT AUTHORITY\SYSTEM&apos; account. 3. Impersonate the token... This can only be done if the attackers current account has the privilege to impersonate security tokens&quot; [@foxglove-rotten-potato-blog]. The same post credits the Project Zero work directly: &quot;this work is derived directly from James Forshaw&apos;s BlackHat talk and Google Project Zero research&quot; [@foxglove-rotten-potato-blog].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Juicy Potato (decoder_it and ohpe, 2018)&lt;/strong&gt; [@github-ohpe-juicy-potato]. A weaponised, configurable Rotten Potato. The repository README is exact about lineage and entry conditions: &quot;RottenPotatoNG and its variants leverages the privilege escalation chain based on BITS service having the MiTM listener on 127.0.0.1:6666 and when you have SeImpersonate or SeAssignPrimaryToken privileges. ... We decided to weaponize RottenPotatoNG: Say hello to Juicy Potato&quot; [@github-ohpe-juicy-potato].&lt;/p&gt;
&lt;p&gt;Juicy Potato adds a CLSID brute-list (so the attacker can cycle through DCOM activations until one works on the target Windows version), a configurable listener port, and a configurable target binary. The repo&apos;s own framing of when it works is the article&apos;s PullQuote-worthy line: &quot;If the user has SeImpersonate or SeAssignPrimaryToken privileges then you are SYSTEM&quot; [@github-ohpe-juicy-potato]. Juicy Potato was killed by a Windows 10 1809 / Server 2019 mitigation that prevented the OXID resolver from being queried on a port other than 135. The mitigation was the first time Microsoft had touched the underlying primitive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rogue Potato (Antonio Cocomazzi and Andrea Pierini, May 2020)&lt;/strong&gt; [@decoder-rogue-potato-blog]. The bypass for the loopback-OXID mitigation. Decoder&apos;s blog states the engineering problem and the fix in two sentences: &quot;Starting from Windows 10 1809 &amp;amp; Windows Server 2019, its no more possible to query the OXID resolver on a port different than 135&quot; [@decoder-rogue-potato-blog]. Rogue Potato works around the constraint by routing the OXID resolution through an attacker-controlled &lt;em&gt;remote&lt;/em&gt; OXID resolver, typically reached via &lt;code&gt;socat tcp-listen:135,fork TCP:attacker:9999&lt;/code&gt;. The remote resolver returns a string binding pointing back at the attacker&apos;s local listener; the constraint is satisfied (the OXID resolver is on port 135) but the listener it ultimately reaches is the attacker&apos;s. The lineage extends.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PrintSpoofer (Clément Labro / itm4n, May 2020)&lt;/strong&gt; [@github-itm4n-printspoofer, @itm4n-printspoofer-blog]. The same week as Rogue Potato, a different no-DCOM, no-OXID variant lands. PrintSpoofer drops DCOM entirely. It uses the Print Spooler RPC method &lt;code&gt;RpcRemoteFindFirstPrinterChangeNotificationEx&lt;/code&gt; to coerce the spooler (running as SYSTEM) to call back to a named pipe whose path the attacker controls. A path-validation bypass on the named-pipe name lets the attacker capture the SYSTEM credential the spooler offers.&lt;/p&gt;
&lt;p&gt;itm4n&apos;s repository description summarises the entry condition: &quot;From LOCAL/NETWORK SERVICE to SYSTEM by abusing SeImpersonatePrivilege on Windows 10 and Server 2016/2019&quot; [@github-itm4n-printspoofer]. The blog post opens with credit and the canonical decoder_it quote: &quot;I want to start things off with this quote from @decoder_it: &apos;if you have SeAssignPrimaryToken or SeImpersonate privilege, you are SYSTEM&apos;&quot; [@itm4n-printspoofer-blog].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RemotePotato0 (Cocomazzi and Pierini, 2021)&lt;/strong&gt; [@github-antoniococo-remotepotato0]. The first Potato to escape the local machine. A cross-session DCOM activation lets the attacker reach a token from a &lt;em&gt;different&lt;/em&gt; logged-on user; a cross-protocol RPC-to-LDAP relay then turns that user&apos;s authentication into a domain-administrator action against Active Directory.&lt;/p&gt;
&lt;p&gt;The README is candid about the shape of the response: &quot;UPDATE 21-10-2022: The main exploit scenario RPC-&amp;gt;LDAP of RemotePotato0 has been fixed... Just another &apos;Won&apos;t Fix&apos; Windows Privilege Escalation from User to Domain Admin. RemotePotato0 is an exploit that allows you to escalate your privileges from a generic User to Domain Admin... It abuses the DCOM activation service and trigger an NTLM authentication of any user currently logged on in the target machine&quot; [@github-antoniococo-remotepotato0]. &lt;em&gt;Just another &quot;Won&apos;t Fix&quot; Windows Privilege Escalation&lt;/em&gt; is the precise framing: the underlying primitive is structural, the 2022 fix addressed the specific RPC-to-LDAP path, and the construction continues to work for other relay targets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PetitPotam (Lionel Gilles / topotam77, July 2021)&lt;/strong&gt; [@github-topotam-petitpotam, @nvd-cve-2021-36942]. Not strictly a local-LPE Potato, but the source of the EFSRPC coercion primitive that local-LPE Potatoes consume. PetitPotam exploits the Encrypting File System remote-procedure-call protocol&apos;s &lt;code&gt;EfsRpcOpenFileRaw&lt;/code&gt; (and several other functions) to coerce a Windows host to authenticate to an attacker-controlled endpoint.&lt;/p&gt;
&lt;p&gt;The README is exact about the interface choices: &quot;PoC tool to coerce Windows hosts to authenticate to other machines via MS-EFSRPC EfsRpcOpenFileRaw or other functions :) The tools use the LSARPC named pipe with inteface c681d488-d850-11d0-8c52-00c04fd90f7e because it&apos;s more prevalent. But it&apos;s possible to trigger with the EFSRPC named pipe and interface df1941c5-fe89-4e79-bf10-463657acf44d&quot; [@github-topotam-petitpotam]. PetitPotam&apos;s most-cited use case is cross-machine relay against Active Directory Certificate Services, but the EFSRPC coercion is also locally consumable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SharpEfsPotato (bugch3ck, 2021)&lt;/strong&gt; [@github-bugch3ck-sharpefspotato]. The local-machine variant of the PetitPotam coercion. The README is precise about lineage: &quot;Local privilege escalation from SeImpersonatePrivilege using EfsRpc. Built from SweetPotato by &lt;code&gt;@_EthicalChaos_&lt;/code&gt; and SharpSystemTriggers/SharpEfsTrigger by &lt;code&gt;@cube0x0&lt;/code&gt;&quot; [@github-bugch3ck-sharpefspotato]. SharpEfsPotato uses &lt;code&gt;EfsRpcOpenFileRaw&lt;/code&gt; against the local LSARPC pipe to coerce SYSTEM to authenticate to a local endpoint, then performs the by-now-familiar token capture and &lt;code&gt;CreateProcessAsUser&lt;/code&gt;.This article&apos;s stage-4 source verification corrected an attribution that had been carried forward from the original scope file: SharpEfsPotato&apos;s canonical repository is &lt;code&gt;bugch3ck/SharpEfsPotato&lt;/code&gt;, not the often-cited &lt;code&gt;ly4k/SharpEfsPotato&lt;/code&gt;. The latter URL returns HTTP 404. Cross-references that point at the &lt;code&gt;ly4k&lt;/code&gt; URL should be updated to point at &lt;code&gt;bugch3ck&lt;/code&gt; [@github-bugch3ck-sharpefspotato].&lt;/p&gt;
&lt;p&gt;The pattern across the lineage is that &lt;em&gt;the mitigation that did break a tool was always specific&lt;/em&gt; (the loopback-OXID restriction, the cross-session DCOM partial fix, the EFSRPC coercion partial mitigation in KB5005413), and &lt;em&gt;the mitigation that would break the family is structural&lt;/em&gt; (remove &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; from service accounts, end NTLM-to-self-as-machine relay, retire the bearer-credential property of tokens). Microsoft has shipped the first kind of fix seven times across the lineage and continues to ship them; the second kind requires architectural changes that arrive in successor articles in this series.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Author(s)&lt;/th&gt;
&lt;th&gt;Coercion vector&lt;/th&gt;
&lt;th&gt;Mitigation that broke it&lt;/th&gt;
&lt;th&gt;Mitigation that did not&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hot Potato&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Breen&lt;/td&gt;
&lt;td&gt;NBT-NS + WPAD + HTTP-to-SMB relay&lt;/td&gt;
&lt;td&gt;Disable WPAD; KB3146965&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rotten Potato&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Breen, Mallz&lt;/td&gt;
&lt;td&gt;DCOM &lt;code&gt;CoGetInstanceFromIStorage&lt;/code&gt; (BITS)&lt;/td&gt;
&lt;td&gt;(none specific until Juicy fix)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Juicy Potato&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;decoder_it, ohpe&lt;/td&gt;
&lt;td&gt;DCOM CLSID brute-list, configurable port&lt;/td&gt;
&lt;td&gt;Loopback-OXID restriction (1809 / 2019)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rogue Potato&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Cocomazzi, Pierini&lt;/td&gt;
&lt;td&gt;Remote OXID resolver via &lt;code&gt;socat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross-session DCOM partial fix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PrintSpoofer&lt;/td&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;Labro&lt;/td&gt;
&lt;td&gt;Spooler RPC + named-pipe path bypass&lt;/td&gt;
&lt;td&gt;KB5005010 (PrintNightmare-era spooler hardening) [@nvd-cve-2021-34527]&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RemotePotato0&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Cocomazzi, Pierini&lt;/td&gt;
&lt;td&gt;Cross-session DCOM + RPC-to-LDAP relay&lt;/td&gt;
&lt;td&gt;RPC-to-LDAP relay fix (October 2022)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services; remaining relay targets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PetitPotam&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Gilles&lt;/td&gt;
&lt;td&gt;EFSRPC coercion via LSARPC&lt;/td&gt;
&lt;td&gt;KB5005413 partial; ADCS hardening [@nvd-cve-2021-36942]&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services; other relay targets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SharpEfsPotato&lt;/td&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;bugch3ck&lt;/td&gt;
&lt;td&gt;Local EFSRPC coercion&lt;/td&gt;
&lt;td&gt;(none specific to local variant)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeImpersonate&lt;/code&gt; on services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Eight tools. One privilege. Every &quot;mitigation that did not&quot; cell points at the same thing: a bearer-token model plus &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; on a service account.&lt;/p&gt;

Eight tools in six years against the same underlying primitive is not a tooling coincidence. It is the empirical signature of the bearer-credential property and the omnipresent service-account `SeImpersonate` privilege. The Hot Potato post&apos;s verbatim &quot;hard to fix without breaking backward compatibility&quot; admission [@foxglove-hot-potato-blog] is the same argument Microsoft eventually formalised in the security-servicing-criteria position: this surface is intentionally retained for compatibility, and structural changes belong in a different architecture. The article earns the bridge to the Adminless and NTLMless successors here, six years before the calendar gets there.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A service account holding &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; plus &lt;em&gt;any&lt;/em&gt; RPC interface that authenticates to attacker-controllable endpoints equals SYSTEM. Eight Potatoes in six years prove this is structural, not a tooling fad. Audit every server: any non-LocalSystem-equivalent process holding &lt;code&gt;SeImpersonate&lt;/code&gt; or &lt;code&gt;SeAssignPrimaryToken&lt;/code&gt; should be treated as a Potato target until proven otherwise. Pre-deploy per-service SIDs and Group Managed Service Accounts where possible to constrain the blast radius [@github-itm4n-printspoofer].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Eight Potatoes prove the bearer-token property is unkillable by point fixes. But what does an attacker who is &lt;em&gt;already admin&lt;/em&gt; do? The answer is the most-cited offensive Windows tool of the past fifteen years.&lt;/p&gt;
&lt;h2&gt;11. Mimikatz, Conditional ACEs, and the Edges of the Model&lt;/h2&gt;
&lt;p&gt;May 2011. Benjamin Delpy releases the first version of Mimikatz [@en-wiki-mimikatz]. Wikipedia&apos;s biographical summary is precise: &quot;He released the first version of the software in May 2011 as closed source software&quot; [@en-wiki-mimikatz]. Fifteen years later, every offensive Windows engagement on Earth still reaches for it.&lt;/p&gt;
&lt;p&gt;Mimikatz contains many modules. Two of them sit directly on the access-control surface and are worth naming explicitly. The repository&apos;s own command surface lists them as &lt;code&gt;privilege::debug&lt;/code&gt; and &lt;code&gt;token::elevate&lt;/code&gt; [@github-gentilkiwi-mimikatz].&lt;/p&gt;
&lt;p&gt;&lt;code&gt;privilege::debug&lt;/code&gt; is one line of code: the command enables &lt;code&gt;SeDebugPrivilege&lt;/code&gt; on the caller&apos;s token. Any local administrator on stock Windows holds the privilege on the token by default; the command flips it from &lt;code&gt;Available&lt;/code&gt; to &lt;code&gt;Enabled&lt;/code&gt; via &lt;code&gt;AdjustTokenPrivileges&lt;/code&gt;. With &lt;code&gt;SeDebugPrivilege&lt;/code&gt; enabled, the calling process can &lt;code&gt;OpenProcess&lt;/code&gt; against any other process on the machine, including SYSTEM-level services such as &lt;code&gt;lsass.exe&lt;/code&gt;. Every Mimikatz session that wants to read process memory begins with &lt;code&gt;privilege::debug&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;token::elevate&lt;/code&gt; is three lines of code in spirit. The command opens a SYSTEM-owned process (typically &lt;code&gt;lsass.exe&lt;/code&gt;), calls &lt;code&gt;OpenProcessToken&lt;/code&gt; to retrieve a handle to the SYSTEM token, calls &lt;code&gt;DuplicateTokenEx&lt;/code&gt; to duplicate the handle for impersonation, and calls &lt;code&gt;SetThreadToken&lt;/code&gt; to attach the duplicated SYSTEM token to the calling thread. The thread is now SYSTEM. The bearer-token property in three lines of code.&lt;/p&gt;
&lt;p&gt;This article does not cover &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt;. That command is the most-cited Mimikatz capability in journalism (it reads cached credentials from &lt;code&gt;lsass.exe&lt;/code&gt;), but the credential-storage surface and the Credential Guard mitigation belong to the &lt;a href=&quot;https://paragmali.com/blog/the-windows-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;Secure Kernel sibling article&lt;/a&gt; in this series [@secure-kernel-sibling]. For the purposes of &lt;em&gt;this&lt;/em&gt; article, the lesson stops at &lt;code&gt;token::elevate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The lesson is the structural concession. With administrator rights on the local machine and &lt;code&gt;SeDebugPrivilege&lt;/code&gt; enabled, the access-control model has &lt;em&gt;no defence&lt;/em&gt; for &quot;I will pretend to be a different process,&quot; because admin equals kernel by Microsoft&apos;s own boundary definition [@msrc-servicing-criteria]. The DACL evaluation algorithm does not protect against a caller who can read and write arbitrary kernel memory. The privilege list does not protect against a caller who can rewrite the privilege check. The integrity check does not protect against a caller who can edit the integrity label. Every primitive in the model is, by construction, defenceless against an attacker who has crossed the boundary the model considers itself responsible for defending.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; With administrator rights and &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, the Windows access-control model has no defence for &quot;I will pretend to be a different process,&quot; because admin equals kernel by Microsoft&apos;s own boundary definition. Mimikatz &lt;code&gt;token::elevate&lt;/code&gt; is the canonical demonstration. The structural fix for &lt;em&gt;selected&lt;/em&gt; secrets is Credential Guard, which moves the secret out of the NT kernel&apos;s address space entirely. See the Secure Kernel sibling article for the architecture [@secure-kernel-sibling, @msrc-servicing-criteria].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The model has been extended in only one structural direction since 1993, and that direction is the &lt;em&gt;subject&lt;/em&gt; of the access matrix. &lt;em&gt;Conditional ACEs&lt;/em&gt; and &lt;em&gt;Dynamic Access Control&lt;/em&gt; (DAC) shipped together in Windows Server 2012 and Windows 8 [@ms-learn-dac]. They are the only extension of the access-matrix subject Microsoft has shipped in thirty-three years.&lt;/p&gt;
&lt;p&gt;The mechanism is twofold. First, ACEs gain an expression syntax. The SDDL ACE strings page documents &lt;code&gt;XA&lt;/code&gt;, &lt;code&gt;XD&lt;/code&gt;, &lt;code&gt;XU&lt;/code&gt;, and &lt;code&gt;ZA&lt;/code&gt; as conditional callback variants of the basic allow / deny / audit / object-allow ACE types [@ms-learn-ace-strings]. A conditional ACE carries an expression in addition to a SID and an access mask, and the kernel evaluates the expression against the token&apos;s claims at access time. The canonical example is &lt;code&gt;(XA;;FA;;;AU;(@User.Department==&quot;Finance&quot;))&lt;/code&gt; -- an allow-callback ACE that grants &lt;code&gt;FILE_ALL_ACCESS&lt;/code&gt; to Authenticated Users &lt;em&gt;if&lt;/em&gt; the token carries a &lt;code&gt;Department&lt;/code&gt; claim equal to &lt;code&gt;&quot;Finance&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Second, the token gains &lt;em&gt;claims&lt;/em&gt;. A claim is a typed key-value pair attached to the token by Active Directory at logon. Claims can be sourced from the user&apos;s AD attributes, the device&apos;s AD attributes, or resource properties on the object. Microsoft Learn states the role they play: &quot;A central access rule is an expression of authorization rules that can include one or more conditions involving user groups, user claims, device claims, and resource properties. Multiple central access rules can be combined into a central access policy&quot; [@ms-learn-dac].&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;Central Access Policy&lt;/em&gt; (CAP) is a set of &lt;em&gt;Central Access Rules&lt;/em&gt; (CARs), each of which is a conditional-ACE expression. The CAP is applied to file shares; the file-share metadata says &quot;evaluate this CAP for every access,&quot; and the CAP&apos;s expressions reference token claims. The DAC scenario guidance enumerates the deployment-side primitives -- automatic and manual file classification, central access policies for safety-net authorisation, central audit policies for compliance reporting, and Rights Management Service encryption for data-in-use protection [@ms-learn-dac-scenario].&lt;/p&gt;
&lt;p&gt;The reason DAC has not displaced classic DAC outside file-server scenarios is in the same Microsoft Learn page: &quot;Dynamic Access Control is not supported in Windows operating systems prior to Windows Server 2012 and Windows 8. When Dynamic Access Control is configured in environments with supported and non-supported versions of Windows, only the supported versions will implement the changes&quot; [@ms-learn-dac]. Heterogeneous environments fall back to classic DAC. Airgapped environments without a working AD plus AD FS plane have no claims to evaluate. Conditional ACEs are a real extension of the model&apos;s subject dimension; they are also a real bet that the AD-and-Kerberos plane is healthy enough to evaluate them on every access.AppContainer&apos;s Package SIDs (Windows 8) and conditional ACEs (Server 2012) shipped the same year. Both extend the &lt;em&gt;subject&lt;/em&gt; dimension of the access matrix -- one with code identity, one with attribute claims. Neither closes the kernel-equals-admin gap. The two extensions are coordinate, not stacked: a conditional ACE can reference a Package SID; a Package SID can be the subject of a conditional ACE [@ms-learn-dac, @app-identity-sibling].&lt;/p&gt;
&lt;p&gt;The model has been extended in two coordinate dimensions in thirty-three years. It has not been replaced. So what does the whole thing look like put together -- and what does it actually fail at?&lt;/p&gt;
&lt;h2&gt;12. The 2026 Plane: Ten Primitives, One Decision&lt;/h2&gt;
&lt;p&gt;Run a single &lt;code&gt;OpenObject&lt;/code&gt; call on a Windows 11 machine and walk the kernel&apos;s path. Every primitive the article has introduced fires for that one call.&lt;/p&gt;

flowchart LR
    A[User-mode caller] --&amp;gt;|OpenObject name, DesiredAccess, Token| B[Object Manager]
    B --&amp;gt;|Resolve name in namespace| C[Namespace lookup]
    C --&amp;gt;|Fetch security descriptor| D[Object header SD]
    D --&amp;gt; E[SeAccessCheck]
    E --&amp;gt; F[Generic-to-specific mapping]
    F --&amp;gt; G[Mandatory Integrity Control check]
    G --&amp;gt;|Pass| H[AppContainer / capability check]
    H --&amp;gt; I[Privilege bypass: SeBackup / SeRestore / SeDebug]
    I --&amp;gt; J[DACL walk in canonical order]
    J --&amp;gt; K[Conditional ACE expression evaluation]
    K --&amp;gt; L[GrantedAccess accumulated]
    L --&amp;gt; M[SACL audit ACE emit if matched]
    M --&amp;gt; N[Return HANDLE or STATUS_ACCESS_DENIED]
&lt;p&gt;The diagram is the article in one figure. Read it left to right. Every box is a primitive named in Sections 3 to 11. Every famous Windows escalation tool of the last twenty-five years targets one of those boxes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Section introduced&lt;/th&gt;
&lt;th&gt;Year shipped&lt;/th&gt;
&lt;th&gt;Canonical primary citation&lt;/th&gt;
&lt;th&gt;Canonical attack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Security Reference Monitor&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-access-control]&lt;/td&gt;
&lt;td&gt;(Underlying surface; not directly attacked)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Security Identifier (SID)&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-security-identifiers]&lt;/td&gt;
&lt;td&gt;Misused well-known SIDs (&quot;Everyone is just a SID&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Access Token&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-access-tokens]&lt;/td&gt;
&lt;td&gt;The Potato lineage; Mimikatz &lt;code&gt;token::elevate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Security Descriptor&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-how-dacls-control-access]&lt;/td&gt;
&lt;td&gt;HiveNightmare (CVE-2021-36934)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;DACL + ACE&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-how-dacls-control-access, @ms-learn-order-of-aces]&lt;/td&gt;
&lt;td&gt;NULL DACL misconfigurations; out-of-order ACEs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;SACL + audit&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-access-control]&lt;/td&gt;
&lt;td&gt;Tools that copy DACL but not SACL silently drop integrity labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Privilege&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;[@ms-learn-privileges]&lt;/td&gt;
&lt;td&gt;Mimikatz &lt;code&gt;privilege::debug&lt;/code&gt;; &lt;code&gt;SeBackup&lt;/code&gt; abuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Mandatory Integrity Control&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;[@ms-learn-mic]&lt;/td&gt;
&lt;td&gt;IE7 Protected Mode broker bypasses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;UAC split-token&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;[@ms-learn-uac]&lt;/td&gt;
&lt;td&gt;UACMe: 70+ AutoElevate-redirect methods [@github-hfiref0x-uacme]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Conditional ACE / DAC&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;[@ms-learn-dac, @ms-learn-ace-strings]&lt;/td&gt;
&lt;td&gt;Falls back to classic DAC in heterogeneous environments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Windows access-control model is one decision plane, not a feature catalogue. Every securable Windows operation resolves through &lt;code&gt;SeAccessCheck&lt;/code&gt; against five fixed inputs. Every famous escalation tool of the last twenty-five years attacks one of those inputs. Recognising the model as a single plane is the key to using its vocabulary against any specific attack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The plane is whole. It is also full of structural holes its own keepers have publicly admitted. What are they?&lt;/p&gt;
&lt;h2&gt;13. The Five Structural Limits&lt;/h2&gt;
&lt;p&gt;Microsoft&apos;s &lt;em&gt;Security Servicing Criteria for Windows&lt;/em&gt; defines a security boundary as &quot;a logical separation between the code and data of security domains with different levels of trust... the separation between kernel mode and user mode is a classic [...] security boundary&quot; [@msrc-servicing-criteria]. The criteria document then enumerates which boundaries Microsoft commits to servicing. The kernel-mode / user-mode boundary qualifies. UAC and admin-to-kernel are not in the enumerated list. Once that admission is on the public record, the model&apos;s structural arc becomes legible. Five derived limits flow from the concession.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 1: Admin equals kernel.&lt;/strong&gt; Any compromise with administrator rights can rewrite the model&apos;s own enforcement code. &lt;em&gt;Consequence:&lt;/em&gt; Mimikatz, every kernel-driver-loading attack, every signed-driver bring-your-own-vulnerable-driver path. &lt;em&gt;Successor:&lt;/em&gt; VBS Trustlets, which host secrets and policy enforcement in the &lt;em&gt;Virtual Trust Level 1&lt;/em&gt; user-mode environment that the VTL0 NT kernel cannot read or modify. Detailed coverage belongs to the Secure Kernel sibling article in this series [@secure-kernel-sibling].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 2: Tokens are bearer credentials.&lt;/strong&gt; Whichever process holds the handle gets the rights. The kernel does not ask how the handle was obtained. &lt;em&gt;Consequence:&lt;/em&gt; the entire Potato lineage (eight tools, six years), Mimikatz &lt;code&gt;token::elevate&lt;/code&gt;, every cross-session token-theft attack. &lt;em&gt;Successor:&lt;/em&gt; Adminless / Administrator Protection, which retires the long-lived filtered/full token pair in favour of a fresh, time-limited, just-in-time elevation flow gated by Windows Hello plus a hidden, system-generated, profile-separated user account that issues an isolated admin token [@ms-learn-administrator-protection, @techcommunity-admin-protection]. The forthcoming Adminless article in this series will cover the architecture in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 3: &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; on every service account.&lt;/strong&gt; IIS, SQL Server, the Print Spooler, scheduled tasks, Docker, Citrix, almost every managed-service account by default. &lt;em&gt;Consequence:&lt;/em&gt; every Potato, by construction. &lt;em&gt;Partial successor today:&lt;/em&gt; per-service SIDs and Group Managed Service Accounts let administrators constrain the blast radius of a compromised service. &lt;em&gt;Structural successor:&lt;/em&gt; Adminless, which removes the privilege from the daily authentication path and demands a fresh elevation per privileged action [@ms-learn-administrator-protection, @techcommunity-admin-protection].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 4: NTLM relay surface.&lt;/strong&gt; As long as Windows services accept NTLM and the operating system signs NTLM challenges with the local-machine credential, the local-NTLM-to-self attack is structurally available. &lt;em&gt;Consequence:&lt;/em&gt; PetitPotam, RemotePotato0, every cross-protocol relay. &lt;em&gt;Successor:&lt;/em&gt; NTLMless, which formally retires NTLM as a default Windows authentication protocol [@techcommunity-windows-auth-evolution]. The on-ramp is the NTLM auditing channel introduced in Windows 11 24H2 and Windows Server 2025 (KB5064479, original publish date July 11, 2025), which records NTLMv1 usage in &lt;code&gt;Microsoft\Windows\NTLM\Operational&lt;/code&gt; and gives administrators a per-workload deprecation telemetry [@ms-support-ntlm-auditing]. The forthcoming NTLMless article in this series will cover the architecture in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 5: The DACL is local.&lt;/strong&gt; Conditional ACEs and Dynamic Access Control claims need a working AD-and-AD-FS plane to evaluate; airgapped or heterogeneous environments fall back to user / group SIDs as the only available subject. &lt;em&gt;Consequence:&lt;/em&gt; the access-matrix subject is, in practice, still &quot;user and group&quot; for most non-file-server workloads. The 2012 extension to claims and code identity is real but operationally bounded.&lt;/p&gt;
&lt;p&gt;The deepest of the five limits is the one Norm Hardy named in 1988. Hardy&apos;s framing returned in Section 2 [@en-wiki-confused-deputy] holds: capability systems close the gap structurally; ACL engineering does not.&lt;/p&gt;
&lt;p&gt;seL4 closes it with machine-checked correctness proofs and a capability-based design that makes ambient authority a category error [@en-wiki-capability-based-security]. Windows closes it, when it closes it at all, with VBS Trustlets that move &lt;em&gt;the right to perform the operation&lt;/em&gt; into a separate execution domain. The Potato lineage is the textbook confused-deputy instance: a service running with &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; is the privileged compiler; the attacker is the user holding a billing-records-shaped pointer; the service uses &lt;em&gt;its own&lt;/em&gt; authority on every authentication it accepts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Hardy&apos;s 1988 paper [@wayback-cap-lore-hardy] and the Wikipedia summary [@en-wiki-confused-deputy] both say the same thing: ACL systems are structurally vulnerable to confused-deputy attacks; capability systems are not. The gap is not asymptotic. ACL engineering does not close it. The Potato lineage is what the gap looks like in the field, repeated against eight different coercion primitives over six years.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; &lt;strong&gt;The next generation of Windows defences cannot live inside the kernel, because the kernel is on the wrong side of the boundary the model draws.&lt;/strong&gt; Microsoft&apos;s own servicing criteria admit it. Adminless, NTLMless, VBS Trustlets, and Credential Guard are the four non-overlapping ways to fix it. Each successor was scoped to close a specific gap the access-control model could not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The five limits are named. The successors are shipping. What replaces what?&lt;/p&gt;
&lt;h2&gt;14. The Successors: Adminless, NTLMless, VBS Trustlets, Credential Guard&lt;/h2&gt;
&lt;p&gt;One paragraph each. This section is a forward-reference index, not a detailed walk-through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adminless.&lt;/strong&gt; Removes the local Administrators group from the daily authentication path. The long-lived filtered / full token pair the UAC model produces at logon is replaced with a fresh, time-limited, just-in-time elevation flow: when a user wants to perform a privileged action, the system gates the action behind &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;Windows Hello&lt;/a&gt; plus a hidden, system-generated, profile-separated user account that issues an isolated admin token, and the resulting token is bounded in time and scope [@ms-learn-administrator-protection].&lt;/p&gt;
&lt;p&gt;The Microsoft Tech Community announcement (modified November 19, 2024) summarises the security argument: &quot;By requiring explicit authorization for every administrative task, Administrator protection protects Windows from accidental changes by users and changes by malware... Malicious software often relies on admin privileges to change device settings and execute harmful actions. Administrator protection breaks the attack kill chain&quot; [@techcommunity-admin-protection]. &lt;em&gt;Closes:&lt;/em&gt; limits #2 and #3 -- there is no long-lived bearer credential to steal, and &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; does not need to live on every service account because services run as bounded principals issued capabilities at the moment of need. Forthcoming article in this series.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NTLMless.&lt;/strong&gt; Formally retires NTLM as a default Windows authentication protocol [@techcommunity-windows-auth-evolution]. The Tech Community announcement is unambiguous about direction: &quot;Reducing the use of NTLM will ultimately culminate in it being disabled in Windows 11. We are taking a data-driven approach and monitoring reductions in NTLM usage to determine when it will be safe to disable&quot; [@techcommunity-windows-auth-evolution].&lt;/p&gt;
&lt;p&gt;The transition rests on a local KDC (IAKerb) that lets Kerberos service both local and domain accounts, plus an audit channel introduced in Windows 11 24H2 and Windows Server 2025 (KB5064479, original publish date July 11, 2025) that records NTLMv1 usage in &lt;code&gt;Microsoft\Windows\NTLM\Operational&lt;/code&gt; and gives administrators per-service telemetry on which workloads still require the protocol [@ms-support-ntlm-auditing]. The local-NTLM-to-self attack class -- coerce a SYSTEM service to authenticate with the local-machine credential, accept the challenge, relay it back to a local service that trusts the credential -- ends when the local-machine NTLM credential ends. &lt;em&gt;Closes:&lt;/em&gt; limit #4. Forthcoming article in this series.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;VBS Trustlets and Isolated User Mode.&lt;/strong&gt; Shipped in Windows 10 1507 in 2015. &lt;em&gt;Virtualization-Based Security&lt;/em&gt; (VBS) uses the Hyper-V hypervisor to host a second user-mode environment, &lt;em&gt;Virtual Trust Level 1&lt;/em&gt; (VTL1), whose memory the NT kernel running in VTL0 cannot read or write. A &lt;em&gt;Trustlet&lt;/em&gt; is a process that runs in VTL1. &lt;em&gt;Closes:&lt;/em&gt; limit #1, for selected secrets. The ordinary NT kernel still runs the show for ordinary processes; VTL1 is a side-channel for secrets and policy decisions that the model wants to protect even from a kernel-level attacker. Detailed coverage in the Secure Kernel sibling article [@secure-kernel-sibling].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Credential Guard.&lt;/strong&gt; The canonical first Trustlet. &lt;code&gt;lsass.exe&lt;/code&gt; continues to run in VTL0 and answer authentication requests; the credential blobs &lt;code&gt;lsass.exe&lt;/code&gt; historically held are moved to a Trustlet called &lt;code&gt;LsaIso&lt;/code&gt; in VTL1. The VTL0 &lt;code&gt;lsass.exe&lt;/code&gt; retains &lt;em&gt;handles&lt;/em&gt; to the blobs but cannot read their contents; authentication happens by calling into the Trustlet. Mimikatz &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; returns no plaintext credentials against a Credential-Guard-on system, because the plaintext does not live in VTL0 memory at all. The default-enablement timeline and the SKU-specific configuration matrix are covered in the Secure Kernel sibling article [@secure-kernel-sibling].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pluton-rooted attestation and the hardware foundation.&lt;/strong&gt; The successor architectures rest on a hardware identity chain that begins below the firmware, in Microsoft&apos;s Pluton in-die security processor. Pluton holds the keys that vouch for the boot measurements that the OS in turn uses to attest its own integrity to a remote relying party. The &lt;a href=&quot;https://paragmali.com/blog/pluton-a-tpm-on-silicon-microsoft-can-patch/&quot; rel=&quot;noopener&quot;&gt;Pluton article&lt;/a&gt; in this series covers the architecture and the Caliptra root-of-trust direction it foreshadows [@pluton-sibling]. The &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM 2.0 architecture&lt;/a&gt; that the same chain extends and the &lt;a href=&quot;https://paragmali.com/blog/secure-boot-in-windows-the-chain-from-sector-zero-to-userini/&quot; rel=&quot;noopener&quot;&gt;Secure Boot chain&lt;/a&gt; that runs before the access-control model boots are covered in their own sibling articles in this series.&lt;/p&gt;

The five limits enumerated in Section 13 and the four successor articles in this section are in one-to-one correspondence: Adminless closes #2 and #3, NTLMless closes #4, VBS Trustlets close #1, and Credential Guard is the canonical first Trustlet that demonstrates #1 closing for a specific high-value secret. Limit #5 -- the DACL is local -- is operational rather than architectural and is closed by deployment investment in AD plus AD FS rather than by a new mechanism. The correspondence is not a coincidence. Each successor was scoped to close a specific gap the access-control model could not close from inside.
&lt;p&gt;With the gaps named and the successors mapped, what does an administrator actually do today?&lt;/p&gt;
&lt;h2&gt;15. Practical Guide&lt;/h2&gt;
&lt;p&gt;Six concrete recommendations for 2026, each tied to a primary Microsoft Learn or named-expert source.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;whoami /all&lt;/code&gt; prints the SIDs in the calling thread&apos;s token, the integrity level, every privilege with its &lt;code&gt;Enabled&lt;/code&gt; / &lt;code&gt;Disabled&lt;/code&gt; / &lt;code&gt;Default Enabled&lt;/code&gt; state, and -- on AD-joined machines with claims -- the user and device claim set. It is the single most useful diagnostic command for understanding what a session can do. Read the &lt;code&gt;Enabled&lt;/code&gt; column carefully: an available-but-disabled privilege does not affect any access check until the process explicitly enables it [@ms-learn-access-tokens].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;icacls &amp;lt;path&amp;gt;&lt;/code&gt; prints the DACL on a file or directory; the mass-rights letters are &lt;code&gt;(F)&lt;/code&gt; full, &lt;code&gt;(M)&lt;/code&gt; modify, &lt;code&gt;(RX)&lt;/code&gt; read and execute, &lt;code&gt;(R)&lt;/code&gt; read, &lt;code&gt;(W)&lt;/code&gt; write, &lt;code&gt;(D)&lt;/code&gt; delete, &lt;code&gt;(GA)&lt;/code&gt; generic all, &lt;code&gt;(GR)&lt;/code&gt; generic read, &lt;code&gt;(GW)&lt;/code&gt; generic write [@ms-learn-how-dacls-control-access]. PowerShell&apos;s &lt;code&gt;Get-Acl&lt;/code&gt; returns the same descriptor as a structured object that can be filtered and audited at scale. Sysinternals &lt;code&gt;accesschk.exe&lt;/code&gt; answers the inverted query (which paths grant a given SID a given right) and is the right tool for catching descriptor misconfigurations across a large file system. Treat NULL DACL and empty DACL surfaces as the most-likely misconfiguration vectors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On every Windows server, enumerate the principals whose tokens hold &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; or &lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt; in their &lt;em&gt;Default&lt;/em&gt; or &lt;em&gt;Available&lt;/em&gt; lists. Treat any non-LocalSystem-or-equivalent holder as a Potato target until proven otherwise. Where a service must hold the privilege (most managed-service workloads do), constrain the blast radius with per-service SIDs and Group Managed Service Accounts so that a compromise of one service does not extend to a compromise of every service that shares the host&apos;s identity [@github-itm4n-printspoofer].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The UACMe README is the institutional memory for the seventy-method bypass canon. Every method&apos;s &lt;code&gt;Fixed in:&lt;/code&gt; field cites a specific Windows version or &lt;code&gt;unfixed&lt;/code&gt;. Before declaring a binary &quot;patched,&quot; consult the README; a method with a &lt;code&gt;Fixed in: unfixed&lt;/code&gt; annotation is structurally available on every supported Windows version. The institutional position is that UAC bypasses do not, by Microsoft&apos;s own servicing-criteria policy, earn CVEs of their own, so the mitigations are issued per-redirect rather than per-feature [@github-hfiref0x-uacme, @msrc-servicing-criteria].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Windows Event ID 4688 (&quot;A new process has been created&quot;) is the most-cited detection signal for the Potato lineage and the UAC bypass tradition, because almost every member of both families ends in a &lt;code&gt;CreateProcessAsUser&lt;/code&gt; or a redirected AutoElevate launch with a command-line argument that does not match the legitimate use of the parent binary. Enable command-line auditing under &lt;em&gt;Audit Process Creation&lt;/em&gt; and forward the log; Sysmon Event ID 1 is the equivalent and richer signal in environments that deploy Sysinternals&apos; Sysmon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The access matrix is the part of the model with deliberately extensible &lt;em&gt;subjects&lt;/em&gt;. New code that lives behind an AppContainer SID gets a Low-IL token, a Package SID, and a capability list that constrain what it can touch even when the user running it is an administrator. New file shares that need attribute-based authorization should use conditional ACEs and Dynamic Access Control rather than ad-hoc group membership. Cross-link to the &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;App Identity sibling article&lt;/a&gt; for Package SID derivation [@app-identity-sibling] and to the Dynamic Access Control overview [@ms-learn-dac].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;{`
// Inputs:
//   token       -- {sids:[...], integrity:&apos;Low&apos;|&apos;Medium&apos;|&apos;High&apos;|&apos;System&apos;,
//                   appContainer:bool, capabilities:[...], claims:{...},
//                   privileges:{enabled:[...]}}
//   descriptor  -- {dacl:[...], integrityLabel:&apos;Low&apos;|...|&apos;System&apos;,
//                   policyNoWriteUp:bool}
//   desired     -- access mask (number)
// Output: {granted, status, fired:[...]}&lt;/p&gt;
&lt;p&gt;function fullAccessCheck(token, descriptor, desired) {
  const fired = [];
  const ilOrder = {Low:1, Medium:2, High:3, System:4};&lt;/p&gt;
&lt;p&gt;  // 1. MIC check fires before DACL walk.
  if (descriptor.policyNoWriteUp) {
    const writeBits = 0x00040000 | 0x00000002 | 0x00000004; // WRITE_DAC|WRITE|APPEND
    if ((desired &amp;amp; writeBits) &amp;amp;&amp;amp; ilOrder[token.integrity] &amp;lt; ilOrder[descriptor.integrityLabel]) {
      fired.push(&apos;MIC no-write-up: token IL &apos; + token.integrity + &apos; &amp;lt; object IL &apos; + descriptor.integrityLabel);
      return {granted:0, status:&apos;DENIED at integrity check&apos;, fired};
    }
  }&lt;/p&gt;
&lt;p&gt;  // 2. Privilege bypass short-circuit.
  if (token.privileges.enabled.includes(&apos;SeBackupPrivilege&apos;) &amp;amp;&amp;amp; (desired &amp;amp; 0x80000000)) {
    fired.push(&apos;SeBackupPrivilege bypass: GENERIC_READ granted&apos;);
    return {granted: desired, status:&apos;GRANTED via SeBackupPrivilege&apos;, fired};
  }&lt;/p&gt;
&lt;p&gt;  // 3. AppContainer capability check (simplified).
  if (token.appContainer &amp;amp;&amp;amp; descriptor.requiresCapability) {
    if (!token.capabilities.includes(descriptor.requiresCapability)) {
      fired.push(&apos;AppContainer capability check: missing &apos; + descriptor.requiresCapability);
      return {granted:0, status:&apos;DENIED at AppContainer check&apos;, fired};
    }
  }&lt;/p&gt;
&lt;p&gt;  // 4. DACL walk, deny-first, in canonical order.
  let remaining = desired;
  let granted = 0;
  for (const ace of (descriptor.dacl || [])) {
    if (!token.sids.includes(ace.sid)) continue;
    if (ace.condition &amp;amp;&amp;amp; !evalCondition(ace.condition, token.claims)) continue;
    if (ace.type === &apos;DENY&apos; &amp;amp;&amp;amp; (ace.mask &amp;amp; remaining) !== 0) {
      fired.push(&apos;Conditional/plain DENY: &apos; + ace.sid);
      return {granted:0, status:&apos;DENIED at DACL&apos;, fired};
    }
    if (ace.type === &apos;ALLOW&apos;) {
      const newBits = ace.mask &amp;amp; remaining;
      granted |= newBits;
      remaining &amp;amp;= ~newBits;
      fired.push(&apos;ALLOW &apos; + ace.sid + &apos;: granted 0x&apos; + newBits.toString(16));
    }
    if (remaining === 0) {
      return {granted, status:&apos;GRANTED&apos;, fired};
    }
  }
  return {granted, status: remaining === 0 ? &apos;GRANTED&apos; : &apos;DENIED end of DACL&apos;, fired};
}&lt;/p&gt;
&lt;p&gt;function evalCondition(expr, claims) {
  // Toy evaluator for &quot;(@User.Department == \&quot;Finance\&quot;)&quot;-style expressions.
  const m = expr.match(/@User\.(\w+)\s*==\s*&quot;([^&quot;]+)&quot;/);
  if (!m) return true;
  return claims[m[1]] === m[2];
}&lt;/p&gt;
&lt;p&gt;// Demo: a Medium-IL user trying to write to a System-IL object via an allow ACE.
console.log(fullAccessCheck(
  {sids:[&apos;S-1-5-21-X-Y-Z-1001&apos;], integrity:&apos;Medium&apos;, appContainer:false, capabilities:[], claims:{Department:&apos;Finance&apos;},
   privileges:{enabled:[]}},
  {dacl:[{type:&apos;ALLOW&apos;, sid:&apos;S-1-5-21-X-Y-Z-1001&apos;, mask:0xFFFFFFFF}],
   integrityLabel:&apos;System&apos;, policyNoWriteUp:true},
  0x00040000));  // WRITE_DAC
`}&lt;/p&gt;
&lt;p&gt;The simulator runs the full plane in order: MIC integrity check, privilege bypass short-circuit, AppContainer capability check, DACL walk with conditional-ACE evaluation. Reading the &lt;code&gt;fired&lt;/code&gt; log in the output tells you which primitive made the decision and why. It is the mental model the rest of the article has been building toward.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The six tips and the simulator together close the practical loop. With them, the practitioner can reason about any specific access decision the way the kernel does -- not by remembering features, but by walking the same plane.&lt;/p&gt;
&lt;p&gt;The Sysinternals &lt;code&gt;accesschk&lt;/code&gt; and &lt;code&gt;psgetsid&lt;/code&gt; utilities have long been first-line investigative tools for ACL audits. Both ship in the Sysinternals Suite today and continue to surface the same descriptors &lt;code&gt;Get-Acl&lt;/code&gt; and &lt;code&gt;icacls&lt;/code&gt; print, in the form most useful to an administrator working at scale.&lt;/p&gt;
&lt;h2&gt;16. Frequently Asked Questions&lt;/h2&gt;


No. By Microsoft&apos;s own *Security Servicing Criteria for Windows*, UAC and admin-to-kernel are not on the enumerated security-boundary list [@msrc-servicing-criteria]; bypasses are not, by policy, eligible for security servicing as boundary violations. The seventy-plus methods catalogued in UACMe are the empirical consequence of the classification, not a long string of bugs in a feature that was meant to defend against the techniques [@github-hfiref0x-uacme].


No. `Everyone` (the well-known SID `S-1-1-0`) is just a SID. ACEs that reference it are subject to the same DACL walk as any other SID; if a deny ACE for `Everyone` precedes an allow ACE for `Authenticated Users`, access is denied. The DACL evaluation algorithm does not know `Everyone` is special. Forshaw made the point with a meme-able rant in 2020: &quot;S-1-1-0 is NOT A SECURITY BOUNDARY&quot; [@tiraniddo-sharing-logon-session].


No. *Empty* DACL denies all access. *No* DACL (a NULL DACL) grants full access. Newly-written code that &quot;creates a file with no protection&quot; almost always gets this distinction wrong [@ms-learn-how-dacls-control-access]. Verify with `Get-Acl` or `icacls` after creation; an `icacls` output of `Everyone:(F)` on an object you intended to lock down is almost always a NULL DACL, not the policy you meant to write.


For DACL alone, yes. For MIC, no -- a System-IL administrator still cannot write to a process at higher integrity if MIC denies the request, because the integrity check fires before the DACL walk [@ms-learn-mic]. For AppContainer, no -- AppContainer-bound objects require the capability SID, not just an administrator SID. For VBS Trustlets, no -- secrets in VTL1 are unreachable from VTL0 even with administrator rights, which is the whole point of the architecture [@secure-kernel-sibling].


No. The list shows *available* privileges. The `Enabled` column is the one that matters for runtime decisions; available-but-disabled privileges must be enabled via `AdjustTokenPrivileges` before any privileged operation can use them. Most privileges are disabled by default precisely so that a process must explicitly opt in to using one, which lets a security-conscious application minimise the window in which a bug can abuse the privilege [@ms-learn-privileges].


No. The underlying NTLM-to-self surface is still open. SharpEfsPotato (2021) [@github-bugch3ck-sharpefspotato] is the most recent member of the lineage; new tools using fresh coercion primitives (EFSRPC, the spooler, the schedule-task COM interface, cross-session DCOM) appear every twelve to eighteen months. Microsoft&apos;s own Hot Potato post called the underlying issue &quot;hard to fix without breaking backward compatibility&quot; [@foxglove-hot-potato-blog]; the structural fix is the Adminless and NTLMless successor articles, not a point patch on any one primitive.


Partially. AppContainer is a process-level isolation mechanism with a Low-IL token plus a capability-SID list. It is also a *named principal* in the Windows access-control model -- something Chrome&apos;s sandbox is not -- which means AppContainer-bound code can be referenced by SID in a DACL or conditional ACE, and Windows can refuse access to it as a principal in its own right. The sibling App Identity article in this series covers the Package SID derivation and the relationship to Authenticode and App Control for Business [@app-identity-sibling].

&lt;h2&gt;17. Closing: Return to the Hook&lt;/h2&gt;
&lt;p&gt;Open a Windows PowerShell window again. Run &lt;code&gt;whoami /priv&lt;/code&gt;. Read the column on the right, this time with the article&apos;s vocabulary annotated above each line.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeShutdownPrivilege&lt;/code&gt; -- a privilege, in the kernel sense of a pre-checked superpower; bookkeeping rather than power.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeIncreaseWorkingSetPrivilege&lt;/code&gt; -- the same. Most of the twenty rows are housekeeping that the kernel checks at specific call sites to gate non-security-critical operations.&lt;/p&gt;
&lt;p&gt;The five rows that matter are easy to spot once you know what to look for. &lt;code&gt;SeDebugPrivilege&lt;/code&gt; -- Mimikatz starts here. &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; -- the entire Potato lineage starts here. &lt;code&gt;SeAssignPrimaryTokenPrivilege&lt;/code&gt; -- the second half of every token-replay attack. &lt;code&gt;SeBackupPrivilege&lt;/code&gt; -- HiveNightmare&apos;s privilege class. &lt;code&gt;SeRestorePrivilege&lt;/code&gt; -- service-binary replacement. The kernel reads the same column on every securable operation, billions of times a second, and the answer to &quot;can this code do this?&quot; is built out of this list every time.&lt;/p&gt;
&lt;p&gt;Now run &lt;code&gt;icacls C:\Windows\System32\drivers\etc\hosts&lt;/code&gt; again. &lt;code&gt;BUILTIN\Administrators:(F)&lt;/code&gt; is an allow ACE granting full control to the well-known SID &lt;code&gt;S-1-5-32-544&lt;/code&gt;. &lt;code&gt;NT AUTHORITY\SYSTEM:(F)&lt;/code&gt; is an allow ACE granting full control to &lt;code&gt;S-1-5-18&lt;/code&gt;. &lt;code&gt;BUILTIN\Users:(R)&lt;/code&gt; is an allow ACE granting &lt;code&gt;FILE_GENERIC_READ&lt;/code&gt; to &lt;code&gt;S-1-5-32-545&lt;/code&gt;. The DACL is in canonical order: explicit entries before inherited entries, deny entries (none here) before allow entries within each group. &lt;code&gt;SeAccessCheck&lt;/code&gt; will walk this DACL on every read of &lt;code&gt;hosts&lt;/code&gt; from any process on the machine, and the output will be deterministic -- the same answer every time, for the same caller -- because the model that produces it is closed and finite.&lt;/p&gt;
&lt;p&gt;The article&apos;s payoff. Every later post in this series starts where this one ends. The Adminless article retires the bearer-credential property of long-lived tokens. The NTLMless article retires the local-NTLM-to-self relay surface. The Secure Kernel article hosts secrets in VTL1 outside the NT kernel&apos;s address space and tells the Credential Guard story in detail [@secure-kernel-sibling]. The Pluton article roots the hardware identity chain that the successor architectures all eventually verify against [@pluton-sibling]. The TPM article and the Secure Boot article cover the static-time and boot-time chains that run before the access-control model even loads. Each successor was scoped to close a specific gap the access-control model could not close from inside.&lt;/p&gt;
&lt;p&gt;NT 3.1 froze a model in July 1993 because federal procurement demanded it. That model has not structurally changed in thirty-three years. The accumulated attack surface against it -- twenty-five years, eight Potatoes, seventy UAC bypasses, one Mimikatz -- is the empirical proof that &quot;frozen&quot; was always going to mean &quot;attackable from below.&quot; The next generation of defences takes that lesson and stops trying to fix the model from inside. The model is not a feature catalogue. It is a decision plane with five inputs, ten primitives, and five publicly conceded structural limits, and the four successor architectures of the next decade are the four non-overlapping ways to close those limits without re-evaluating against TCSEC C2 again.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SeAccessCheck&lt;/code&gt; decides every time. The next decade decides what it decides about.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;windows-access-control-twenty-five-years&quot; keyTerms={[
  { term: &quot;SeAccessCheck&quot;, definition: &quot;The kernel routine that decides whether a thread may perform a requested set of operations on an object. Takes a security descriptor, an access token, a desired-access mask, a generic-mapping table, and previously-granted access; returns the granted access mask plus a status code.&quot; },
  { term: &quot;Security Reference Monitor (SRM)&quot;, definition: &quot;The kernel-mode component that owns SeAccessCheck and the audit log generation routines. Every other kernel component that needs to grant or deny access calls into it.&quot; },
  { term: &quot;Access Token&quot;, definition: &quot;A kernel object that names the security identity of a thread or process. Carries the user&apos;s SID, group SIDs, privileges, integrity level, primary/impersonation flag, and (for restricted tokens) a list of restricting SIDs. The kernel consults the token on every access check.&quot; },
  { term: &quot;Discretionary Access Control List (DACL)&quot;, definition: &quot;The ordered list of allow / deny ACEs attached to a securable object. The object&apos;s owner controls the contents, in contrast to a mandatory list.&quot; },
  { term: &quot;Mandatory Integrity Control (MIC)&quot;, definition: &quot;A Vista-era addition that adds an integrity-level check to SeAccessCheck. The integrity check fires before the DACL walk and enforces no-write-up by default.&quot; },
  { term: &quot;User Account Control (UAC)&quot;, definition: &quot;A Vista-era split-token mechanism in which an administrative user receives two linked tokens at logon: a filtered Medium-IL standard-user token and a full High-IL administrative counterpart. Not, by Microsoft&apos;s own servicing criteria, an enforced security boundary.&quot; },
  { term: &quot;SeImpersonatePrivilege&quot;, definition: &quot;The privilege that lets a service accept an impersonation token from a client. Held by every Windows service account by default. The load-bearing privilege for the entire Potato lineage.&quot; },
  { term: &quot;Confused Deputy&quot;, definition: &quot;Norm Hardy&apos;s 1988 framing of the structural failure mode of any ambient-authority access-control system: a privileged service can be tricked into using its own authority on the attacker&apos;s behalf because the system cannot distinguish authority the service has from authority the service is being asked to use.&quot; },
  { term: &quot;VBS Trustlet&quot;, definition: &quot;A Windows process that runs in Virtual Trust Level 1, a hardware-isolated user-mode environment whose memory the NT kernel running in VTL0 cannot read or write. The architectural answer to the admin-equals-kernel concession of the access-control model.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>access-control</category><category>privilege-escalation</category><category>security-tokens</category><category>uac</category><category>mimikatz</category><category>potato-attacks</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>