<?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: appcontainer</title><description>Posts tagged appcontainer.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sun, 07 Jun 2026 04:13:09 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/appcontainer/rss.xml" rel="self" type="application/rss+xml"/><item><title>Agentic Identity on Windows: When the Process Acting on Your Behalf Isn&apos;t You</title><link>https://paragmali.com/blog/agentic-identity-on-windows-when-the-process-acting-on-your-/</link><guid isPermaLink="true">https://paragmali.com/blog/agentic-identity-on-windows-when-the-process-acting-on-your-/</guid><description>Every AI agent on Windows in 2026 runs as the logged-on user. The cloud-identity layer has crossed the agent-attribution gap; the OS layer has not. This article maps the FIDO AATWG pillars onto Windows primitives and asks what is missing.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>
Every locally-installed AI agent on Windows in May 2026 -- Claude Desktop, ChatGPT Desktop, Cursor, GitHub Copilot CLI, the MSIX-packaged Microsoft Copilot for Windows -- runs in a process whose primary token traces to the logged-on user. `SeAccessCheck`, ETW, RPC ACLs, Defender, and on-device Conditional Access all collapse &quot;the user asked&quot; and &quot;the agent decided&quot; into one principal. The cloud-identity layer has crossed this gap: Microsoft&apos;s Entra Agent ID has been in public preview since May 19, 2025 [@ms-security-blog-agentid], and the FIDO Alliance&apos;s Agentic Authentication Technical Working Group launched its three-pillar effort on April 28, 2026 with Google&apos;s AP2 and Mastercard&apos;s Verifiable Intent as foundational donations [@fido-aatwg-pr]. The Windows OS-level analog -- a kernel-recognised `AgentPrincipal` distinct from the user SID and the package SID -- does not exist, even though the substrate primitives (AppContainer package SIDs, Kerberos S4U2Proxy, WebAuthn and Windows Hello, the TPM&apos;s Direct Anonymous Attestation, the Administrator-Protection separate-session pattern, WAM, ETW) already ship. This article maps each AATWG pillar onto the Windows primitive that already exists and the glue that does not, and argues that agent identity belongs at both layers, with the OS layer being the missing piece.
&lt;h2&gt;1. Two principals walk into a syscall&lt;/h2&gt;
&lt;p&gt;A Tuesday in May 2026. Windows 11 24H2. Claude Desktop is open. In one terminal, the user types &lt;code&gt;Remove-Item -Recurse -Force .\old-builds&lt;/code&gt;. In another, the agent decides to run the same command against the same path. &lt;code&gt;SeAccessCheck&lt;/code&gt; returns the same answer for both. The Security event log records the same &lt;code&gt;SubjectUserSid&lt;/code&gt;. &lt;a href=&quot;https://paragmali.com/blog/etw-how-windows-2000s-performance-hack-became-the-edr-substr/&quot; rel=&quot;noopener&quot;&gt;ETW&lt;/a&gt; emits the same &lt;code&gt;SubjectUserName&lt;/code&gt;. Microsoft Defender attributes both deletions to the same person -- the human at the keyboard.&lt;/p&gt;
&lt;p&gt;As far as the Windows kernel is concerned, there is exactly one principal in this story, and his name is the user [@ms-learn-sids].&lt;/p&gt;
&lt;p&gt;Nothing is broken. No malware. No zero-day. The kernel is doing exactly what NT 3.1 shipped to do on July 27, 1993 [@wiki-nt31]: read the primary token, hand the access-check engine the user SID, the group SIDs, and the integrity-level overlay [@ms-learn-mic], and decide. The decision is correct. It is also, in 2026, an attribution failure -- because the two actors who issued those two commands are different actors, and the kernel cannot tell.&lt;/p&gt;
&lt;p&gt;The attribution-collapse claim holds for every deployment pattern of every shipping agent today, but the privilege-blast-radius framing is the strong claim for Electron-wrapped agents (full DPAPI user scope, full Kerberos ticket-granting ticket, full network egress) and a more qualified claim for MSIX-packaged agents like Microsoft Copilot for Windows, which inherit a LowBox token and a per-package DPAPI scope under AppContainer [@ms-learn-appcontainer].&lt;/p&gt;

The kernel-resident access token attached to every Windows process at creation. It contains a user SID, a list of group SIDs, an integrity-level SID carried in a `SYSTEM_MANDATORY_LABEL_ACE` inside the token&apos;s SACL [@ms-learn-mic], and a privilege set; every `SeAccessCheck` call reads it as the authoritative statement of &quot;who is running this process&quot; [@ms-learn-sids].
&lt;p&gt;The collapse is structural. Three observations make the shape visible.&lt;/p&gt;
&lt;p&gt;First, the kernel&apos;s access-check pipeline reads the &lt;em&gt;user&lt;/em&gt;. &lt;code&gt;SeAccessCheck&lt;/code&gt; walks the DACL on the target object and matches each ACE against the SIDs in the primary token. The integrity-level overlay, added in Windows Vista, gates writes against the integrity label [@ms-learn-mic]; AppContainer, added in Windows 8, adds a second-principal-shaped SID to the same token group list [@ms-learn-appcontainer]; but the primary SID -- the answer the kernel returns when a downstream consumer asks &quot;whose authority is being exercised here?&quot; -- is the user.&lt;/p&gt;
&lt;p&gt;Second, the cloud-identity layer has just put the dual-principal answer on the wire. On April 28, 2026, the FIDO Alliance announced the Agentic Authentication Technical Working Group [@fido-aatwg-pr]. The three pillars: Verifiable User Instructions, Agent Authentication, and Trusted Delegation for Commerce. Chairs are CVS Health, Google, and OpenAI; vice-chairs are Amazon, Google, and Okta; the parallel Payments Technical Working Group is chaired by Mastercard and Visa. Google&apos;s Agent Payments Protocol (AP2) and Mastercard&apos;s Verifiable Intent framework are the foundational donations [@ap2-blog-google; @mastercard-vi]. Independent coverage from HelpNetSecurity dated April 29, 2026 carries the same governance roster [@helpnetsecurity-aatwg]. The cloud side now has a named, governed wire-format design space for the agent-as-distinct-principal story.&lt;/p&gt;
&lt;p&gt;Third, the OS-side gap is structural. Every layer of Windows from &lt;code&gt;SeAccessCheck&lt;/code&gt; through ETW [@ms-learn-mic] through on-device Conditional Access still answers &quot;the user&quot; because the primary token still has only the user SID. The package SID under AppContainer is the closest signal Windows has to a second principal; the access-check rule is intersection, not addition -- &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-appcontainer]. That intersection rule is the closest thing the kernel has to a sibling-principal model, and it identifies a &lt;em&gt;package&lt;/em&gt;, not an &lt;em&gt;agent&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; No engineering effort is required to produce this attribution failure. The kernel is functioning as designed. The design is from 1993. The user is the principal. The agent is the user. The audit log is what the audit log has always been.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What would it take, mechanically, for the kernel to know which principal was which -- and why has the answer been &quot;the user&quot; for the thirty-three years since NT 3.1? Both halves of that question have load-bearing histories, and those histories are older than NT itself.&lt;/p&gt;
&lt;h2&gt;2. From Multics to NT 3.1: the user-as-principal genealogy&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;MIT, Bell Labs, and General Electric. Fernando Corbato of MIT and Victor Vyssotsky of Bell Telephone Laboratories present &lt;em&gt;Introduction and Overview of the Multics System&lt;/em&gt; at the AFIPS Fall Joint Computer Conference. Their opening describes a general-purpose programming system intended to scale to thousands of users at a single installation [@multics-fjcc]. Multics, jointly led by MIT&apos;s Project MAC, General Electric, and Bell Laboratories [@multics-wiki], is the first operating system designed from the start around the idea that the kernel must know &lt;em&gt;whose&lt;/em&gt; work each running process is doing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Multics inheritance is not stylistic. It is the source of the abstraction Windows NT 3.1 picks up twenty-eight years later: a &lt;em&gt;principal&lt;/em&gt; is a named entity (a user, or in some systems a service account); every process belongs to exactly one principal at creation; the kernel mediates access using that principal. TENEX, BBN&apos;s PDP-10 time-sharing system from 1969 [@tenex-bitsavers], inherits the same model and ports it into the DEC product line; the Multics-to-TENEX-to-Unix lineage [@multics-hist] is the route the article reads as carrying the user-as-principal assumption forward into the design ground from which DEC VMS and later Windows NT 3.1 emerged.&lt;/p&gt;

A variable-length data structure that uniquely identifies a security principal in a Windows access decision. The canonical user-SID form is `S-1-5-21--`; every access token carries a user SID plus a list of group SIDs, and every `SeAccessCheck` call reads them as the authoritative answer to &quot;who is running this thread?&quot; [@ms-learn-sids].
&lt;p&gt;The formal model came in 1971. Butler Lampson, then at Xerox Palo Alto, published &lt;em&gt;Protection&lt;/em&gt; at the 5th Princeton Conference on Information Sciences and Systems, later reprinted in ACM Operating Systems Review 8(1) in January 1974 [@lampson-catalog]. Lampson&apos;s access-matrix model -- subject by object by right -- is the operational mathematics behind every modern access-control system. Read the matrix row by row and you get a capability list (&quot;what each subject can do&quot;); read it column by column and you get an access control list (&quot;who can do what to this object&quot;). Windows picks column-major: every securable object on Windows is described by a DACL, and &lt;code&gt;SeAccessCheck&lt;/code&gt; is the operational realisation of Lampson&apos;s matrix on NT. The DACL is the column; the primary token is the row index; the access-check answer is the cell.&lt;/p&gt;
&lt;p&gt;The bwlampson.site author catalog is the canonical fetchable anchor for the 1971 paper [@lampson-catalog]. The Microsoft Research-hosted PDF that this corpus previously linked is dead (HTTP 404); the catalog page itself names the ACM OSR 8(1) January 1974 reprint as the journal of record.&lt;/p&gt;

timeline
    title Genealogy of the user-as-principal model
    1965 : Multics
         : Corbato and Vyssotsky present at AFIPS FJCC
         : Per-user authentication as a kernel primitive
    1969 : TENEX
         : BBN ports the user-as-principal model to PDP-10
    1971 : Lampson&apos;s access-matrix model
         : Subject x object x right
         : Foundation for SeAccessCheck and the DACL
    1988 : Hardy&apos;s Confused Deputy
         : Authority from two sources, no way to name which
    1993 : NT 3.1 ships
         : One primary token per process, user SID as principal
&lt;p&gt;Seventeen years later, in 1988, Norm Hardy writes &lt;em&gt;The Confused Deputy&lt;/em&gt; at Key Logic (the company formed from the wreckage of Tymshare, where the events Hardy describes had occurred years earlier). Hardy&apos;s compiler is a FORTRAN compiler with a home-files licence; it legitimately holds authority to write into one path (the billing file &lt;code&gt;(SYSX)BILL&lt;/code&gt;) so it can record per-user compilation charges, and it legitimately accepts user-supplied output paths so it can write generated &lt;code&gt;.obj&lt;/code&gt; files where the user requests. A clever user supplies &lt;code&gt;(SYSX)BILL&lt;/code&gt; as the output path. The compiler obediently writes the user&apos;s output over the billing record, because the compiler &quot;had no way of expressing these intents&quot; [@hardy-caplore].&lt;/p&gt;
&lt;p&gt;Hardy&apos;s punchline is structural, and the article will return to it once more. &quot;The fundamental problem is that the compiler runs with authority stemming from two sources. (That&apos;s why the compiler is a confused deputy.) ... The compiler had no way of expressing these intents!&quot; [@hardy-caplore]. The deputy holds authority from two principals (itself, the licensed compiler; and the user it is serving), and the system the deputy talks to has no protocol for the deputy to say &lt;em&gt;which&lt;/em&gt; authority it is exercising for &lt;em&gt;this&lt;/em&gt; action. Any system that grants a deputy ambient authority from multiple sources without giving the deputy a way to name those sources at the syscall is a confused-deputy system by construction. The ACM Digital Library carries the canonical reprint at &lt;code&gt;10.1145/54289.871709&lt;/code&gt; [@hardy-acm], with the cap-lore.com mirror as the fetchable secondary.&lt;/p&gt;

Hardy 1988: a process holding authority delegated from one principal can be tricked into using that authority on behalf of another principal because the process has no way to attribute its actions to the right authorising principal at the syscall [@hardy-caplore]. The structural lower bound on agent attribution.
&lt;p&gt;July 27, 1993 [@wiki-nt31]. Dave Cutler ships NT 3.1. Every process on NT carries exactly one primary token; the token&apos;s user SID is the principal; impersonation is the only way a thread can temporarily speak as a different identity, and impersonation is bounded to the thread, not the process. Microsoft Learn states the consequence verbatim: &quot;Each time a user signs in, the system creates an access token for that user. The access token contains the user&apos;s SID, user rights, and the SIDs for any groups the user belongs to&quot; [@ms-learn-sids]. That description is the Multics inheritance with thirty years of refinement: one principal per process, the principal is a user, the access-check is the operational form of Lampson&apos;s matrix.&lt;/p&gt;
&lt;p&gt;This is the &lt;em&gt;user-identity&lt;/em&gt; milestone, not the code-identity milestone. NT 3.1 made user identity precise. The process that ran an attached binary still had no answer to &quot;who is this &lt;em&gt;code&lt;/em&gt;?&quot; -- and as the late-1990s ActiveX download experience would prove, that gap could not stay open.&lt;/p&gt;
&lt;h2&gt;3. Layering code identity on user identity: 1996 to 2017&lt;/h2&gt;
&lt;p&gt;August 7, 1996. A joint Microsoft and VeriSign press release crosses the wire: &lt;em&gt;Microsoft and VeriSign Provide First Technology For Secure Downloading of Software Over the Internet&lt;/em&gt; [@ms-news-authenticode-1996]. Internet Explorer 3.0 will ship with the new model the same year. The model has a name: Authenticode. The question this section asks: did Authenticode and its successors solve the principal question, or did they only decorate it?&lt;/p&gt;
&lt;p&gt;The pattern that emerges across five generations of &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;code-identity layering&lt;/a&gt; deserves a name. Every generation answers a real failure of the one before it. Every generation adds an attribute to the access decision -- a publisher signature on the binary, a policy gate at load time, a second SID in the token, a kernel-mode code-integrity policy. And every generation leaves the runtime principal unchanged. The user is still the user. The token&apos;s primary SID is still the user SID. The downstream consumer (Defender, an ETW provider, a remote RPC server, a file-system DACL) still sees one principal.&lt;/p&gt;
&lt;h3&gt;3.1 Authenticode (1996, NT 4.0 with IE 3.0)&lt;/h3&gt;
&lt;p&gt;Authenticode is a load-time attribute. The publisher signs the PE on disk; the signature is a PKCS #7 blob embedded in a dedicated PE attribute certificate slot [@ms-learn-cryptography]. When the loader maps the binary, Windows can verify the signature, walk the certificate chain to a trusted root, and present the publisher name to the user (in IE 3.0&apos;s case, in the famous ActiveX install dialog). Cross-vendor code-signing history places Authenticode among the earliest widely-deployed commercial schemes to bind a publisher identity to a downloaded executable [@wikipedia-codesign; @ms-news-authenticode-1996].&lt;/p&gt;
&lt;p&gt;Authenticode is a &lt;em&gt;load-time&lt;/em&gt; attribute -- the publisher signs the file on disk, not the running process. After the loader has validated the signature and mapped the image, the running process&apos;s primary token has nothing in it that records &quot;this binary was signed by Microsoft Corporation.&quot; The runtime principal is unchanged from what the user&apos;s logon session created.&lt;/p&gt;
&lt;p&gt;The failure mode that forces the next generation is structural. A signature is descriptive (&quot;this file is from Microsoft&quot;), not authoritative (&quot;this process is acting with Microsoft&apos;s authority&quot;). A binary&apos;s publisher is attached to the file on disk; nothing about it propagates into the access decision the kernel makes when the process opens a network socket, decrypts a DPAPI blob, or impersonates over RPC.&lt;/p&gt;
&lt;h3&gt;3.2 SRP and AppLocker (2001, 2009)&lt;/h3&gt;
&lt;p&gt;Software Restriction Policies arrived first with Windows XP (October 2001) [@wiki-winxp] and shipped again with Windows Server 2003 (April 2003) [@wiki-srv2003]; AppLocker arrived on Windows 7 (October 22, 2009) [@wiki-win7] and Server 2008 R2 as the formal successor. Microsoft Learn says it directly: &quot;AppLocker policies in the GPO are applied and supersede the SRP policies in the GPO and any local AppLocker policies or SRP policies&quot; [@ms-learn-srp-applocker]. Both layers are policy gates: an administrator authors rules naming publishers, hashes, or paths, and the OS enforces the rules at process creation. The runtime token is, again, unchanged.&lt;/p&gt;
&lt;p&gt;The failure mode is one Microsoft itself documents in unusually plain language. The AppLocker overview reads, verbatim: &quot;AppLocker is a defense-in-depth security feature and not considered a defensible Windows security feature&quot; [@ms-learn-applocker]. The same page directs administrators to App Control for Business when the goal is defensible threat protection. Defense-in-depth means the feature raises attacker cost; it does not promise an attacker cannot bypass it. That promise is reserved for documented security boundaries.&lt;/p&gt;
&lt;h3&gt;3.3 AppContainer and the package SID (Windows 8, October 26, 2012 general availability)&lt;/h3&gt;
&lt;p&gt;&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; is the first time the kernel carries a &lt;em&gt;second&lt;/em&gt; principal-shaped SID in the token&apos;s group list. Windows 8 ships with the new sandbox; every UWP app process runs with an &lt;code&gt;S-1-15-2-...&lt;/code&gt; package SID attached to its primary token, alongside the user SID and the user&apos;s group SIDs. The dual-principal model has a verbatim Microsoft Learn definition: &quot;This dual-principal model ensures that access to sensitive resources is tightly controlled and can be managed independently for different applications ... the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-appcontainer].&lt;/p&gt;

The kernel sandboxing primitive shipped in Windows 8 (October 26, 2012 general availability) [@ms-learn-appcontainer; @wiki-win8]. It attaches a second SID (`S-1-15-2-...`) to a process&apos;s access token; access decisions become the intersection of user/group SIDs and the package SID, gated further by capability SIDs (`S-1-15-3-...`) the app declares in its manifest [@ms-learn-appcontainer]. The kernel-internal name for the structure is the LowBox token, which is why the relevant API is `NtCreateLowBoxToken` [@ms-learn-appcontainer-legacy].
&lt;p&gt;The dual-principal language is real and load-bearing. It is also not what the article needs. The package SID identifies a &lt;em&gt;package&lt;/em&gt; (the UWP manifest, the MSIX bundle), not an &lt;em&gt;agent role&lt;/em&gt;. Nothing upstream tells Defender, ETW, or Conditional Access whether the package SID it sees represents an AI agent, a UWP calculator, or a games launcher. AppContainer also originally targeted UWP only; the legacy-applications variant arrived later as a way to retrofit unpackaged Win32 apps into the same kernel substrate [@ms-learn-appcontainer-legacy], which is the path Win32 App Isolation (June 14, 2023) extends [@ms-blogs-win32appiso].&lt;/p&gt;
&lt;h3&gt;3.4 App Control for Business (2017 onwards)&lt;/h3&gt;
&lt;p&gt;App Control for Business -- the policy formerly known as Windows Defender Application Control (WDAC), which arrived as a named feature in Windows 10 1709 (Fall Creators Update, released October 17, 2017) [@wiki-win10vh] -- is the kernel-mode code-integrity policy enforced by the loader itself [@ms-learn-appcontrol]. Unlike AppLocker, App Control for Business is on the documented security-boundary list when configured per the supported guidance; it can cover DLL loads, driver loads, and script-host loads with a single policy. The runtime principal is, predictably, still the user.&lt;/p&gt;

flowchart TD
    subgraph TokenAtRuntime[&quot;What is in the primary token&quot;]
        US[&quot;User SID (1993)&quot;]
        PSID[&quot;Package SID (2012)&quot;]
        IL[&quot;Integrity level overlay (2006)&quot;]
    end
    subgraph GateBeforeRuntime[&quot;What gates execution before the token exists&quot;]
        AC[&quot;Authenticode signature (1996)&quot;]
        SRP[&quot;SRP and AppLocker (2001 and 2009)&quot;]
        WDAC[&quot;App Control for Business (2017)&quot;]
    end
    AC --&amp;gt; SRP --&amp;gt; WDAC
    US --&amp;gt; PSID --&amp;gt; IL
    GateBeforeRuntime -.-&amp;gt; TokenAtRuntime
    classDef def fill:transparent
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Each generation of Windows code identity pushed the publisher, package, or integrity attribute into the token or into a load-time gate, but the runtime principal stayed the user. The agent-attribution problem is the third instance of this pattern -- the question shifts from &quot;who wrote this code?&quot; to &quot;who instructed this code right now?&quot; but the architectural answer Windows has shipped is the same answer it shipped in 1993.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So a reader who arrived at this article believing Windows must have some way of attributing what an AI agent does, given how much identity machinery has accumulated since 1993, can now see why the belief is wrong. Authenticode signs the file on disk. AppLocker is defense-in-depth. The AppContainer package SID is per-package, not per-role. App Control for Business controls what code runs, not which principal carries the authority once code is loaded. Five generations of layering identity attributes; one user SID at the centre.&lt;/p&gt;
&lt;p&gt;If we take the genealogy seriously and ask what a sixth generation -- a &lt;em&gt;second principal&lt;/em&gt; attached to the process at the kernel layer -- would have to look like, the answer is sitting in an Insider build.&lt;/p&gt;
&lt;h2&gt;4. Six generations, plus the one that hasn&apos;t shipped&lt;/h2&gt;
&lt;p&gt;Read the genealogy as a forcing function and the shape becomes clear. Every generation of Windows app identity was forced by a specific failure of the one before it, and the forced response was never &quot;replace the existing identity model&quot; -- it was always &quot;add an attribute or a sibling principal alongside it.&quot; That conservation rule is what makes Windows identity legible across thirty-three years. It is also what makes the seventh generation visible by its absence.&lt;/p&gt;
&lt;p&gt;The six shipped generations are easy to enumerate.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generation 1: NT 3.1 primary token (July 27, 1993).&lt;/strong&gt; One primary token per process. The token carries a user SID and a group SID list [@ms-learn-sids; @wiki-nt31]. The runtime principal is the user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generation 2: Authenticode (August 7, 1996).&lt;/strong&gt; Publisher signature attached to the binary on disk [@ms-news-authenticode-1996]. Runtime token unchanged.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generation 3: SRP, then AppLocker (2001, October 22, 2009).&lt;/strong&gt; Group-Policy execution gates; runtime token unchanged; AppLocker explicitly defense-in-depth, not a defensible boundary [@ms-learn-applocker; @ms-learn-srp-applocker; @wiki-winxp; @wiki-win7].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generation 4: AppContainer and the package SID (October 26, 2012).&lt;/strong&gt; Second principal-shaped SID in the token group list. Access check is the intersection of user and package authority [@ms-learn-appcontainer; @wiki-win8]. Runtime principal is still the user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generation 5: App Control for Business (2017 onwards).&lt;/strong&gt; Kernel-mode code-integrity policy; defensible boundary; controls what code can run, not which principal carries authority [@ms-learn-appcontrol; @wiki-win10vh].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generation 6: Administrator Protection (late 2025 Insider, iterating through 2026).&lt;/strong&gt; The first shipping Windows feature that mints a true second logon-session principal for a bounded action, gated by a Windows Hello user-verification gesture [@ms-techcomm-adminprot].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&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; is the interesting one. The feature surfaced on Windows 11 Insider builds in late 2025; subsequent Insider build iterations have continued through 2026, with a temporary disablement and re-enablement during that window [@ms-techcomm-adminprot]. As of May 2026 it is not yet generally available; the Tech Community announcement page is the canonical reference for the design [@ms-techcomm-adminprot].&lt;/p&gt;
&lt;p&gt;The structural innovation is worth describing precisely. Pre-Administrator-Protection elevation on Windows used split tokens: an administrator user signs in, and the Local Security Authority creates two logon sessions for the same user, one filtered and one full. When the user clicks through the User Account Control prompt, the elevating process swaps its primary token from the filtered to the full session. Both sessions belong to the same user SID; the policy boundary is integrity-level and group-driven, not principal-driven.&lt;/p&gt;
&lt;p&gt;Administrator Protection breaks that. The feature introduces a &lt;em&gt;System-Managed Administrator Account&lt;/em&gt; shadow profile.The internal acronym is SMAA. It does not yet appear in &lt;code&gt;whoami /all&lt;/code&gt; output on shipping builds. When the user authorises an elevation through Windows Hello, the elevated process runs under the separate SMAA logon session, scoped to that elevation [@ms-techcomm-adminprot]. The Hello gesture is the verification step; the separate session is the new principal; the bound is the single elevated action.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every generation of Windows app identity has been forced by a specific failure of the previous generation, and every forced response added an attribute or a sibling principal alongside the existing one -- but until Administrator Protection (Generation 6, Insider in 2026, not yet generally available), no shipping Windows feature minted a true second principal for a bounded action.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Administrator Protection is the existence proof. Windows &lt;em&gt;can&lt;/em&gt; mint a second logon-session principal for a bounded action under a user-verification gesture. The architectural question this article asks: can the same pattern generalise from elevation to delegation?&lt;/p&gt;

flowchart TD
    G1[&quot;Gen 1: NT 3.1 primary token (1993). User SID only.&quot;]
    G2[&quot;Gen 2: Authenticode (1996). Publisher attached to binary.&quot;]
    G3[&quot;Gen 3: SRP and AppLocker (2001, 2009). Execution gates.&quot;]
    G4[&quot;Gen 4: AppContainer and package SID (2012). Second SID in token.&quot;]
    G5[&quot;Gen 5: App Control for Business (2017+). Kernel-mode CI.&quot;]
    G6[&quot;Gen 6: Administrator Protection (late 2025 Insider). Second logon session for elevation.&quot;]
    G7[&quot;Gen 7 (missing): AgentPrincipal. Second logon session for delegation.&quot;]
    G1 --&amp;gt; G2 --&amp;gt; G3 --&amp;gt; G4 --&amp;gt; G5 --&amp;gt; G6 -.-&amp;gt; G7
&lt;p&gt;The missing seventh generation is what this article is about. It exists at the cloud layer. Microsoft&apos;s Entra Agent ID has been in public preview since May 19, 2025 [@ms-security-blog-agentid] and was the centrepiece of the Ignite 2025 expansion that Microsoft itself called &quot;the largest expansion of Microsoft Entra capabilities to date, extending Zero Trust principles to AI workloads&quot; [@ms-learn-ignite25]. The FIDO Alliance has named the wire-format design space [@fido-aatwg-pr]. The IETF has an individual draft for the OAuth wire format [@ietf-draft-agentoauth]. The OS layer does not have an analog.&lt;/p&gt;
&lt;p&gt;If Generation 6 is the existence proof at the OS layer, what just happened at the cloud layer that finally makes the delegation case unavoidable?&lt;/p&gt;
&lt;h2&gt;5. April 28, 2026: the wire format goes public&lt;/h2&gt;
&lt;p&gt;April 28, 2026. The FIDO Alliance press release crosses the wire. &lt;em&gt;FIDO Alliance to Develop Standards for Trusted AI Agent Interactions&lt;/em&gt; [@fido-aatwg-pr]. The opening sentence is the inflection point: &quot;The FIDO Alliance today announced initiatives to develop interoperable standards for agentic interactions and commerce.&quot; Andrew Shikiar, FIDO Alliance CEO, is the launch spokesperson. The BusinessWire mirror under wire ID &lt;code&gt;20260427506015&lt;/code&gt; carries the same text [@fido-aatwg-bw], and independent press coverage from HelpNetSecurity (April 29, 2026) [@helpnetsecurity-aatwg] and PPC Land [@ppcland-aatwg] corroborate the launch.&lt;/p&gt;
&lt;p&gt;Four interlocking pieces of news land in the same press release.&lt;/p&gt;
&lt;h3&gt;5.1 The three AATWG pillars&lt;/h3&gt;
&lt;p&gt;The Agentic Authentication Technical Working Group is chartered around three pillars [@fido-aatwg-pr]. The press release wording is verbatim because the wording is what every downstream protocol will quote.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Verifiable User Instructions.&lt;/strong&gt; &quot;Enabling users to authorise AI agents through clear, phishing-resistant mechanisms so agents only perform approved actions, including transactions, without exposing credentials&quot; [@fido-aatwg-pr]. The wire-format primitive: a passkey-signed delegation token bound to a specific intent (&quot;approve $50 to vendor X&quot;), with replay protection and a short TTL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent Authentication.&lt;/strong&gt; &quot;Allowing services to verify that an AI agent is acting on behalf of an authenticated user and within defined parameters, distinguishing legitimate agents from unauthorised actors&quot; [@fido-aatwg-pr]. The wire-format primitive: an attestation binding an agent class (optionally a specific instance) to a verifiable identity assertion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trusted Delegation for Commerce.&lt;/strong&gt; &quot;Defining how agent-initiated transactions can be executed within user-controlled boundaries, with verifiable authorisation&quot; [@fido-aatwg-pr]. The wire-format primitive: the AP2 mandate or Verifiable Intent SD-JWT chain binding an agent action to a user-approved scope and constraint set.&lt;/li&gt;
&lt;/ul&gt;

The FIDO Alliance today announced initiatives to develop interoperable standards for agentic interactions and commerce ... Verifiable User Instructions ... Agent Authentication ... Trusted Delegation for Commerce. -- FIDO Alliance press release, April 28, 2026 [@fido-aatwg-pr]
&lt;h3&gt;5.2 The governance&lt;/h3&gt;
&lt;p&gt;The taxonomy matters because over-attribution is one of the easier mistakes to make in this story. AATWG chairs are CVS Health, Google, and OpenAI; vice-chairs are Amazon, Google, and Okta; the parallel Payments Technical Working Group is chaired by Mastercard and Visa [@fido-aatwg-pr]. Co-sponsors include OpenAI, Amazon, Okta, CVS Health, Visa, and Proof, plus the donating partners (Google, Mastercard). Proof Inc.&apos;s &lt;em&gt;Know Your Agent&lt;/em&gt; framework is a separate Sponsor-tier contribution to FIDO; it is not an AATWG chair or vice-chair role.&lt;/p&gt;
&lt;h3&gt;5.3 The foundational donations&lt;/h3&gt;
&lt;p&gt;Google&apos;s Agent Payments Protocol (AP2) and Mastercard&apos;s Verifiable Intent framework are the foundational donations [@ap2-blog-google; @mastercard-vi]. AP2 was open-sourced on September 16, 2025 [@ap2-cloud-blog] at &lt;code&gt;github.com/google-agentic-commerce/AP2&lt;/code&gt; under Apache 2.0 [@ap2-github]; v0.2 shipped alongside the FIDO donation, with the v0.2 release notes calling out &quot;Human Not Present&quot; payments [@ap2-blog-google]. Mastercard&apos;s Verifiable Intent specification is hosted at verifiableintent.dev as Draft v0.1, describing a three-layer SD-JWT delegation chain (Identity, Intent, Action) with eight constraint types -- amount bounds, merchant allow-lists, budget caps, recurrence terms -- &quot;cryptographically bound and machine-verifiable&quot; [@vi-dev]. The underlying credential model is the W3C Verifiable Credentials Data Model v2.0 W3C Recommendation [@w3c-vc-2].&lt;/p&gt;
&lt;p&gt;The mandate vocabulary drifted between the September 2025 AP2 launch and the v0.2 release. The launch announcement at cloud.google.com described &quot;Intent / Cart / Payment&quot; mandates [@ap2-cloud-blog]; the current ap2-protocol.org spec describes &quot;Checkout (Open / Closed) + Payment (Open / Closed)&quot; [@ap2-protocol]. Readers diffing the September 2025 blog against the April 2026 spec should expect the vocabulary, not the underlying primitives, to have changed.&lt;/p&gt;
&lt;h3&gt;5.4 The Microsoft parallel&lt;/h3&gt;
&lt;p&gt;Microsoft has been on the same arc for nearly a year. The Build 2025 security blog, dated May 19, 2025, introduced Microsoft Entra Agent ID in public preview: &quot;We are excited to introduce Microsoft Entra Agent ID, which extends identity management and access capabilities to AI agents&quot; [@ms-security-blog-agentid]. The Workday Agent System of Record and ServiceNow AI Platform integrations announced on September 16, 2025 made Entra Agent ID a directory service for agents across two of the largest enterprise SaaS platforms [@prnewswire-workday]. The Ignite 2025 expansion (November 2025) was, in Microsoft&apos;s own framing, &quot;the largest expansion of Microsoft Entra capabilities to date, extending Zero Trust principles to AI workloads&quot; [@ms-learn-ignite25]; the agent OAuth protocol trio shipped at the same time [@ms-learn-agentoauth], and Conditional Access for agent identities followed into preview in early 2026 [@ms-learn-caagent]. WinBuzzer (May 20, 2025) corroborates the public-preview launch [@winbuzzer-agentid].&lt;/p&gt;

flowchart LR
    P1[&quot;Pillar 1: Verifiable User Instructions&quot;]
    P2[&quot;Pillar 2: Agent Authentication&quot;]
    P3[&quot;Pillar 3: Trusted Delegation for Commerce&quot;]
    W1[&quot;Wire: passkey-signed delegation token, intent-bound, short TTL&quot;]
    W2[&quot;Wire: attestation + identity assertion, agent class or instance&quot;]
    W3[&quot;Wire: AP2 mandate or Verifiable Intent SD-JWT chain&quot;]
    P1 --&amp;gt; W1
    P2 --&amp;gt; W2
    P3 --&amp;gt; W3
&lt;p&gt;So the reader&apos;s understanding shifts. Before April 28, 2026 the easy mental model was that AI agents were a product feature, not an identity question. After the FIDO press release the design space is named, governed, and partially specified on the wire. The further shift drives the rest of the article: &lt;em&gt;the cloud layer is no longer the gap&lt;/em&gt;. Entra Agent ID has shipped a directory; OAuth Token Exchange has had the &lt;code&gt;act&lt;/code&gt; and &lt;code&gt;may_act&lt;/code&gt; claims standardised since January 2020 [@ietf-rfc-8693]; the MCP authorisation profile has its 2025-11-25 revision live [@anthropic-mcp-2025-11-25]; AP2 and Verifiable Intent are now under FIDO governance.&lt;/p&gt;
&lt;p&gt;The gap is on the desktop. So if we take the three pillars seriously, which Windows primitive does each pillar already have a substrate for, and which Windows primitive is missing?&lt;/p&gt;
&lt;h2&gt;6. Mapping the three pillars onto Windows primitives&lt;/h2&gt;
&lt;p&gt;One section. Three sub-sections, one per pillar. For each: the existing Windows primitive, the API surface that exposes it, and the missing glue that turns it into a pillar-compatible building block.&lt;/p&gt;
&lt;h3&gt;6.1 Pillar 1: Verifiable User Instructions on Windows&lt;/h3&gt;
&lt;p&gt;The substrate exists in user mode and lives in &lt;code&gt;webauthn.dll&lt;/code&gt;, the &lt;a href=&quot;https://paragmali.com/blog/webauthn-and-passkeys-on-windows-from-ctap-to-the-credential/&quot; rel=&quot;noopener&quot;&gt;Win32 platform-authenticator entry point&lt;/a&gt;. Microsoft Learn describes it verbatim: &quot;Provides Win32 apps with APIs for communicating to Windows Hello and external security keys as part of WebAuthN and CTAP specifications&quot; [@ms-learn-webauthn-api]. Windows Hello provides the TPM-rooted user-verification gesture: &quot;credentials are asymmetric and generated within isolated environments of TPMs&quot; [@ms-learn-hello]. The credential provider model in Winlogon, the passkey provider plug-in model in Windows 11 24H2, and per-relying-party passkey isolation in Windows Hello for Business round out the platform-authenticator surface.&lt;/p&gt;
&lt;p&gt;What is missing is a Windows-native &lt;em&gt;agent action approval&lt;/em&gt; UI surface. Today&apos;s pieces let a relying party in the browser ask the user to authenticate to that relying party; they do not let a locally-installed agent ask the user to authorise a specific action (&quot;approve $50 to vendor X&quot;; &quot;approve &lt;code&gt;rm -rf .\old-builds&lt;/code&gt;&quot;) by minting a per-action, intent-bound delegation token signed by the user&apos;s passkey with replay protection and a TTL measured in seconds. The Pillar 1 wire format AATWG is developing is exactly this kind of token; the Windows-side glue would land it as a system UI surface (analogous to UAC, but minting a passkey assertion rather than escalating an integrity level), wired into the WebAuthn platform authenticator and audited via ETW.&lt;/p&gt;

The Windows-resident OAuth token broker that ships with the operating system. WAM acts as an authentication broker on Windows 10 1703 and later; &quot;WAM ensures that the refresh tokens are device bound and enables apps to acquire device bound access tokens&quot; [@ms-learn-wam]. WAM is the on-device credential broker for Microsoft Entra ID; it does not work with third-party identity providers as of May 2026.
&lt;h3&gt;6.2 Pillar 2: Agent Authentication on Windows&lt;/h3&gt;
&lt;p&gt;The kernel substrate is AppContainer plus the package SID. The verbatim Microsoft Learn dual-principal model is the load-bearing citation: &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-appcontainer]. The cryptographic binding from binary to package is Authenticode signing the file [@ms-news-authenticode-1996; @wikipedia-codesign] and App Control for Business as the policy gating the loader [@ms-learn-appcontrol]. The privacy-preserving agent-class attestation primitive is &lt;a href=&quot;https://paragmali.com/blog/direct-anonymous-attestation-the-zero-knowledge-proof-alread/&quot; rel=&quot;noopener&quot;&gt;Direct Anonymous Attestation&lt;/a&gt;, defined in the TPM 2.0 Library Specification by the Trusted Computing Group; the corpus&apos;s sibling DAA article describes the protocol in detail, and the verbatim TCG specification language is sourced there.&lt;/p&gt;

A TPM 2.0 protocol that lets a device prove membership in a privacy-preserving group without revealing the specific device identity. In the agent setting, DAA would let an agent vendor prove &quot;this binary is a member of the *signed Claude Desktop* class&quot; without revealing which specific desktop installation is making the call. The protocol&apos;s group-signature mathematics is the load-bearing primitive.
&lt;p&gt;What is missing on Windows is a kernel-recognised &lt;code&gt;AgentPrincipal&lt;/code&gt; that downstream consumers treat as first-class. Today the package SID is the closest signal; it is also category-incorrect, because the package SID represents a package, not an agent role. An &lt;code&gt;AgentPrincipal&lt;/code&gt; would be a third token-resident SID alongside the user SID and the package SID, with its own ACL slot in opt-in DACLs, its own &lt;code&gt;SubjectAgentSid&lt;/code&gt; field in &lt;code&gt;SeAccessCheck&lt;/code&gt; audit events, its own ETW header alongside &lt;code&gt;SubjectUserSid&lt;/code&gt;, its own RPC binding-handle metadata, and its own claim in the access tokens WAM mints. Defender, EDR, on-device Conditional Access -- every consumer that today reads the user SID would gain a sibling field.&lt;/p&gt;

The dominant deployment pattern for AI agents on Windows desktops in May 2026 is Electron plus an NSIS or MSI per-user installer, not MSIX plus the AppContainer kernel substrate. Claude Desktop, ChatGPT Desktop, and Cursor all ship through this Electron path; GitHub Copilot CLI ships as a Node.js CLI via npm (`@github/copilot`), WinGet (`GitHub.Copilot`), or an MSI installer from the github/copilot-cli GitHub Releases page, which is a different non-MSIX-packaged path but is structurally equivalent for the purposes of this article -- no package SID, no AppContainer kernel substrate, primary token traces to the user. Each of them runs under the user&apos;s primary token without a package SID; the WebAuthn platform authenticator is available to them via `webauthn.dll`, but the kernel-substrate dual-principal model under AppContainer is not.&lt;p&gt;Win32 App Isolation, announced in public preview on June 14, 2023, is the in-flight retrofit. The launch blog describes it directly: &quot;We are thrilled to announce the public preview launch of Win32 app isolation ... Win32 app isolation is built on the foundation of AppContainers ... AppContainer, which is recognized as a security boundary by Microsoft&quot; [@ms-blogs-win32appiso]. Adoption among agent vendors three years later is minimal; the Pillar 2 substrate exists, but the deployment substrate that would carry it does not.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;6.3 Pillar 3: Trusted Delegation for Commerce on Windows&lt;/h3&gt;
&lt;p&gt;The act-on-behalf-of primitive on Windows is &lt;a href=&quot;https://paragmali.com/blog/kerberos-in-windows-the-other-half-of-ntlmless/&quot; rel=&quot;noopener&quot;&gt;Kerberos S4U2Self and S4U2Proxy&lt;/a&gt;, formally specified in Microsoft&apos;s MS-SFU Kerberos Protocol Extensions [@ms-learn-mssfu]. S4U2Self lets a service obtain a service ticket for a user without that user&apos;s password (the &quot;for&quot; half); S4U2Proxy lets the service then present that ticket to a back-end service on the user&apos;s behalf (the &quot;proxy&quot; half). Resource-Based Constrained Delegation is the modern policy form: the back-end service authors which front-end services may delegate to it. Protected Users, Authentication Policies, and Authentication Silos are the policy plumbing. WAM is the on-device token broker for Entra-side delegation [@ms-learn-wam]; Microsoft&apos;s OAuth 2.0 On-Behalf-Of flow [@ms-learn-obo-flow] is the cloud analog.&lt;/p&gt;

Microsoft&apos;s Kerberos Protocol Extensions, formalised in MS-SFU, that let a server obtain a service ticket on behalf of a user without that user&apos;s password (S4U2Self) and then present that ticket to a back-end service (S4U2Proxy). They are the act-on-behalf-of primitive on Windows [@ms-learn-mssfu]. Resource-Based Constrained Delegation refines the policy: the back-end resource names the front-end services it accepts delegation from.
&lt;p&gt;What is missing is a Macaroons-style mintable, attenuable, revocable capability-token format scoped to a per-tool allow-list. Macaroons were introduced by Birgisson, Politz, Erlingsson, Taly, Vrable, and Lentczner at NDSS 2014 (February 22, 2014) [@macaroons-ndss; @macaroons-gr]. The verbatim positioning from the Google Research abstract is the load-bearing description: &quot;macaroons are bearer credentials, like Web cookies, macaroons embed caveats that attenuate and contextually confine when, where, by who, and for what purpose a target service should authorize requests&quot; [@macaroons-gr]. The construction is a chained HMAC: a fresh HMAC of the previous HMAC plus a caveat. Any holder can append a caveat, derive a strictly weaker macaroon, and present it to a verifier; the verifier walks the chain, checks each caveat against its policy, and verifies the final HMAC against the issuer&apos;s secret.&lt;/p&gt;

Birgisson, Politz, Erlingsson, Taly, Vrable, and Lentczner at NDSS 2014 [@macaroons-ndss]. A bearer credential constructed as a chained HMAC over caveats; any holder can derive a strictly weaker credential by appending a caveat without round-tripping the issuer, but cannot derive a stronger one. The attenuation property is what makes macaroons the natural format for per-tool agent capabilities [@macaroons-gr].
&lt;p&gt;In a Windows-shipping Pillar 3 implementation, the agent would not present the user&apos;s TGT to a downstream tool. The agent would present a macaroon issued at agent-install time -- attenuated to the specific tools the user authorised through a Pillar 1 Verifiable User Instructions flow -- and the tool would verify the macaroon plus an ETW emission carrying both &lt;code&gt;SubjectUserSid&lt;/code&gt; and &lt;code&gt;SubjectAgentSid&lt;/code&gt;. Revocation would land at the issuer (the WAM-resident macaroon authority), with a Continuous Access Evaluation analog fanning out to local token caches without logging the user out.&lt;/p&gt;

flowchart LR
    subgraph P1G[&quot;Pillar 1: Verifiable User Instructions&quot;]
        P1S[&quot;Substrate: webauthn.dll, Windows Hello, passkey provider&quot;]
        P1M[&quot;Missing: agent-action approval UI minting intent-bound delegation tokens&quot;]
        P1S --&amp;gt; P1M
    end
    subgraph P2G[&quot;Pillar 2: Agent Authentication&quot;]
        P2S[&quot;Substrate: AppContainer package SID, Authenticode, App Control, TPM 2.0 DAA, Pluton&quot;]
        P2M[&quot;Missing: kernel-recognised AgentPrincipal across SeAccessCheck, ETW, RPC, Defender, CA&quot;]
        P2S --&amp;gt; P2M
    end
    subgraph P3G[&quot;Pillar 3: Trusted Delegation for Commerce&quot;]
        P3S[&quot;Substrate: Kerberos S4U2Proxy, RBCD, WAM, OBO flow, ETW&quot;]
        P3M[&quot;Missing: Macaroons-style attenuable capability tokens plus AgentPrincipalSid in ETW&quot;]
        P3S --&amp;gt; P3M
    end
&lt;p&gt;The proposed access-check sequence is the second load-bearing diagram. Picture an agent in its own AppContainer, with its own package SID, holding a macaroon scoped to one tool. The agent calls the tool; WAM intercepts; if the action requires fresh user verification, Hello mints a passkey assertion; &lt;code&gt;SeAccessCheck&lt;/code&gt; evaluates user SID, package SID, and the proposed agent SID; ETW emits the dual-principal record.&lt;/p&gt;

sequenceDiagram
    participant Agent as Agent process
    participant WAM as WAM broker
    participant Hello as Windows Hello
    participant Kernel as SeAccessCheck
    participant ETW as ETW provider
    Agent-&amp;gt;&amp;gt;WAM: request tool access with macaroon
    WAM-&amp;gt;&amp;gt;WAM: verify macaroon chain
    WAM-&amp;gt;&amp;gt;Hello: if intent required, mint fresh assertion
    Hello--&amp;gt;&amp;gt;WAM: passkey assertion
    WAM-&amp;gt;&amp;gt;Kernel: assemble token (user SID, package SID, agent SID)
    Kernel-&amp;gt;&amp;gt;Kernel: access check with three principals
    Kernel--&amp;gt;&amp;gt;Agent: granted or denied
    Kernel-&amp;gt;&amp;gt;ETW: emit dual-principal record
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pillar&lt;/th&gt;
&lt;th&gt;Windows substrate that already ships&lt;/th&gt;
&lt;th&gt;API surface&lt;/th&gt;
&lt;th&gt;Missing glue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1: Verifiable User Instructions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;webauthn.dll&lt;/code&gt;, Windows Hello, passkey provider&lt;/td&gt;
&lt;td&gt;Win32 WebAuthn API; Hello credential provider&lt;/td&gt;
&lt;td&gt;Agent-action approval UI; intent-bound passkey delegation tokens with short TTL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2: Agent Authentication&lt;/td&gt;
&lt;td&gt;AppContainer + package SID, Authenticode + App Control, TPM 2.0 DAA, Pluton attestation&lt;/td&gt;
&lt;td&gt;NtCreateLowBoxToken; LSASS package-SID assignment; TPM attestation&lt;/td&gt;
&lt;td&gt;Kernel-recognised &lt;code&gt;AgentPrincipal&lt;/code&gt; across &lt;code&gt;SeAccessCheck&lt;/code&gt;, ETW, RPC, Defender, on-device CA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3: Trusted Delegation for Commerce&lt;/td&gt;
&lt;td&gt;Kerberos S4U2Self/S4U2Proxy, RBCD, WAM, OBO flow, ETW&lt;/td&gt;
&lt;td&gt;MS-SFU; MSAL WAM broker; Entra OBO token endpoint&lt;/td&gt;
&lt;td&gt;Macaroons-style per-tool capability tokens; &lt;code&gt;AgentPrincipalSid&lt;/code&gt; ETW field; CAE-style fan-out for revocation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment pattern&lt;/th&gt;
&lt;th&gt;Token at runtime&lt;/th&gt;
&lt;th&gt;Where DPAPI lives&lt;/th&gt;
&lt;th&gt;Network privilege&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Non-MSIX user-mode (Claude Desktop / Cursor / ChatGPT Desktop: Electron + NSIS; Copilot CLI: Node.js + npm / WinGet / MSI)&lt;/td&gt;
&lt;td&gt;User primary token; no package SID&lt;/td&gt;
&lt;td&gt;User DPAPI scope (decryptable by any process under the same user)&lt;/td&gt;
&lt;td&gt;Full user network privilege; user TGT available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MSIX-packaged AppContainer (Microsoft Copilot for Windows)&lt;/td&gt;
&lt;td&gt;LowBox token with package SID; integrity level usually Low&lt;/td&gt;
&lt;td&gt;Per-package DPAPI scope (isolated from other packages)&lt;/td&gt;
&lt;td&gt;Capability-gated network egress per app manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;So the substrate is largely on the box. The glue is not. What competing approaches already ship -- and which one wins?&lt;/p&gt;
&lt;h2&gt;7. Where the principal lives: three positions&lt;/h2&gt;
&lt;p&gt;Three architectural positions are already shipping enough code that &quot;which one wins&quot; is a real question. Each picks a different layer to host the agent principal: the cloud, the operating system, or the on-device token broker.&lt;/p&gt;
&lt;h3&gt;7.1 Cloud-only&lt;/h3&gt;
&lt;p&gt;Agent identity lives in the identity provider. Microsoft&apos;s Entra Agent ID is the reference design: agents are first-class directory objects, with their own client credentials, sign-in logs, Conditional Access policies, and audit trail in Purview [@ms-learn-agentid]. When an agent needs to call a back-end API on a user&apos;s behalf, it uses RFC 8693 Token Exchange [@ietf-rfc-8693] or Microsoft&apos;s OAuth 2.0 On-Behalf-Of flow [@ms-learn-obo-flow] to exchange the user&apos;s token for an actor-claim-carrying access token; the back-end API consumes the &lt;code&gt;act&lt;/code&gt; and &lt;code&gt;may_act&lt;/code&gt; claims to attribute the call.&lt;/p&gt;
&lt;p&gt;Pros: works today, no kernel change, no Windows update required. The plumbing is fully specified at IETF [@ietf-rfc-8693; @ietf-rfc-9728; @ietf-oauth-v2-1], the Microsoft documentation is live [@ms-learn-agentoauth; @ms-learn-agentobo], and the directory exists [@ms-learn-agentid]. Cons: the OS never sees an agent principal. Every local file action, every DPAPI decrypt, every Kerberos ticket use, every RPC call inside the device collapses to the user identity. On-device endpoint detection cannot attribute. Continuous Access Evaluation knows the agent has been revoked in the cloud but cannot purge the user&apos;s local Kerberos cache or local DPAPI-protected secrets without logging the user out.&lt;/p&gt;
&lt;p&gt;Microsoft Learn makes the cloud-only constraint explicit: &quot;Agents aren&apos;t supported for OBO (&lt;code&gt;/authorize&lt;/code&gt;) flows. Supported grant types are &lt;code&gt;client_credential&lt;/code&gt;, &lt;code&gt;jwt-bearer&lt;/code&gt;, and &lt;code&gt;refresh_token&lt;/code&gt;&quot; [@ms-learn-agentobo]. Agent identities are confidential clients only [@ms-learn-agentoauth]; the interactive-flow path used by browser-based delegation is not available to agent entities. Cloud-only works precisely because the agent never tries to participate in a desktop user-verification gesture. AzureFeeds (January 28, 2026) records the practical consequences for Conditional Access: agent authentication is &quot;purely machine-driven&quot; with no MFA prompt, no device check, and no authentication-strength evaluation [@azurefeeds-caagent].&lt;/p&gt;
&lt;h3&gt;7.2 OS-only&lt;/h3&gt;
&lt;p&gt;Hypothetical, not shipping. The kernel access-check, ETW, and RPC ACLs all carry an agent SID distinct from the user SID. Every event the kernel emits gets a &lt;code&gt;SubjectAgentSid&lt;/code&gt; field next to the existing &lt;code&gt;SubjectUserSid&lt;/code&gt;. The access check is a three-way intersection: user SID, package SID, agent SID. Capability SIDs gate per-resource consent the way they do today for UWP apps.&lt;/p&gt;
&lt;p&gt;Pros: native attribution at every layer. EDR products read the agent SID directly from the kernel-supplied event header. File-system DACLs gain an explicit &quot;deny the agent, allow the user&quot; expression. On-device Conditional Access can refuse to release a DPAPI-protected secret if the requesting process carries an agent SID that has been revoked in Entra. Cons: requires a kernel-level change and a packaging story for non-MSIX agents (Electron in particular, the dominant deployment pattern). Without the packaging story, the kernel substrate exists but the deployment substrate does not.&lt;/p&gt;
&lt;h3&gt;7.3 Token broker&lt;/h3&gt;
&lt;p&gt;WAM extended with an agent-scoped token cache. The OS holds agent-scoped, attenuable tokens that any local agent process must mint from before calling a downstream API. The principal in the OS is still the user, but the &lt;em&gt;token&lt;/em&gt; the agent presents to each downstream consumer is scoped to the agent.&lt;/p&gt;
&lt;p&gt;Pros: reuses existing WAM plumbing [@ms-learn-wam]. WAM already binds refresh tokens to the device&apos;s TPM. Adding an agent-scope cache is plumbing, not architecture. Cons: cooperative. A malicious or buggy agent can bypass the broker and fall back to the user&apos;s TGT in the local Kerberos cache, the user&apos;s DPAPI master keys, or the user&apos;s network privileges. The broker can only attribute the agents that choose to be attributed.&lt;/p&gt;

A bearer OAuth access token, in OAuth 2.0 and OAuth 2.1, is a credential the bearer presents to a resource server; the server consults the issuer (via introspection or JWT signature verification) and accepts or rejects. Adding a new constraint means going back to the authorisation server and asking for a new token with the new constraint applied. Round-trip required.&lt;p&gt;A macaroon attenuates without a round-trip. The holder appends a caveat (&quot;only for tool X&quot;, &quot;only until 2026-05-25T15:00:00Z&quot;, &quot;only with body hash matching &lt;code&gt;sha256:...&lt;/code&gt;&quot;) and a fresh HMAC of the previous HMAC plus the caveat text. The verifier walks the chain, checks each caveat against its policy, and verifies the final HMAC against the issuer&apos;s secret. The holder cannot un-attenuate; the issuer does not have to be online. That property -- attenuation without a round-trip, by any party in the chain -- is what makes macaroons the natural format for per-tool agent capabilities [@macaroons-ndss; @macaroons-gr].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;7.4 Three live spec families that complicate the picture&lt;/h3&gt;
&lt;p&gt;Three protocol families are racing in parallel and the article will not pretend any one is the obvious winner.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Anthropic&apos;s Model Context Protocol authorisation profile.&lt;/strong&gt; Revision 2025-11-25 is the current authoritative profile [@anthropic-mcp-2025-11-25], and the MCP specification index confirms 2025-11-25 as the latest pointer [@anthropic-mcp-index]. The opening sentence makes the design choice explicit: &quot;Authorization is OPTIONAL for MCP implementations. When supported: Implementations using an HTTP-based transport SHOULD conform to this specification. Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment&quot; [@anthropic-mcp-2025-11-25]. The HTTP profile is OAuth 2.1 [@ietf-oauth-v2-1] plus RFC 9728 [@ietf-rfc-9728] for resource metadata, with PKCE mandatory and dynamic client registration optional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google AP2 plus Mastercard Verifiable Intent.&lt;/strong&gt; The W3C Verifiable Credential-shaped mandate ladder for agent-initiated commerce [@ap2-protocol; @ap2-github; @ap2-cloud-blog; @vi-dev; @w3c-vc-2]. The September 2025 launch vocabulary used Intent / Cart / Payment mandates [@ap2-cloud-blog]; the current v0.2 spec uses Checkout (Open / Closed) and Payment (Open / Closed) [@ap2-protocol]. The April 28, 2026 FIDO donation moves the spec to multi-vendor governance [@ap2-blog-google].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The IETF individual draft for agents on behalf of users.&lt;/strong&gt; &lt;code&gt;draft-oauth-ai-agents-on-behalf-of-user&lt;/code&gt; by Thilina Senarath and Ayesha Dissanayaka [@ietf-draft-agentoauth]. The current revision introduces the &lt;code&gt;requested_actor&lt;/code&gt; parameter in authorisation requests and the &lt;code&gt;actor_token&lt;/code&gt; parameter in token requests to authenticate the agent during code-to-token exchange [@ietf-draft-agentoauth]. The original -00 revision used &lt;code&gt;requested_agent&lt;/code&gt; and defined an explicit grant type &lt;code&gt;urn:ietf:params:oauth:grant-type:agent-authorization_code&lt;/code&gt; [@ietf-draft-agentoauth-00]; the parameter rename is documented and recent. The draft is an individual submission, not yet a working-group document.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Position&lt;/th&gt;
&lt;th&gt;Principal lives in&lt;/th&gt;
&lt;th&gt;Token format&lt;/th&gt;
&lt;th&gt;Revocation surface&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Cloud-only (Entra Agent ID + RFC 8693)&lt;/td&gt;
&lt;td&gt;Entra directory&lt;/td&gt;
&lt;td&gt;JWT with &lt;code&gt;act&lt;/code&gt; claim&lt;/td&gt;
&lt;td&gt;Sign-in policy + CAE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS-only (hypothetical AgentPrincipal)&lt;/td&gt;
&lt;td&gt;Kernel access token&lt;/td&gt;
&lt;td&gt;Token-resident SID&lt;/td&gt;
&lt;td&gt;Local SID purge + Defender&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token broker (WAM extended)&lt;/td&gt;
&lt;td&gt;WAM cache (TPM-bound)&lt;/td&gt;
&lt;td&gt;Macaroon or signed capability&lt;/td&gt;
&lt;td&gt;WAM eviction + macaroon allow-list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP authorisation profile&lt;/td&gt;
&lt;td&gt;MCP server&apos;s IdP&lt;/td&gt;
&lt;td&gt;OAuth 2.1 bearer&lt;/td&gt;
&lt;td&gt;OAuth introspection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AP2 plus Verifiable Intent&lt;/td&gt;
&lt;td&gt;Mandate issuer&lt;/td&gt;
&lt;td&gt;SD-JWT VC chain&lt;/td&gt;
&lt;td&gt;Mandate revocation registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IETF agent-OBO draft&lt;/td&gt;
&lt;td&gt;Authorisation server&lt;/td&gt;
&lt;td&gt;JWT with &lt;code&gt;actor_token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OAuth introspection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Most of the primitives exist on Windows today: AppContainer package SIDs, Kerberos S4U2Proxy, WebAuthn and Windows Hello, TPM 2.0 Direct Anonymous Attestation, the Administrator-Protection separate-session pattern, WAM, ETW. What does not exist is the coherent glue -- a kernel-recognised &lt;code&gt;AgentPrincipal&lt;/code&gt; that downstream consumers treat as first-class. The article&apos;s position is that agent identity belongs at both the cloud layer and the OS layer, with the OS layer being the missing piece in May 2026. The cloud layer makes Conditional Access work; the OS layer makes endpoint detection work; both are necessary; neither is sufficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even the union of all three positions has structural ceilings. What are the limits no engineering can buy?&lt;/p&gt;
&lt;h2&gt;8. Three ceilings no engineering can buy&lt;/h2&gt;
&lt;p&gt;Three structural ceilings constrain every agent-attribution architecture, regardless of which of the three positions wins.&lt;/p&gt;
&lt;h3&gt;8.1 The semantic-intent ceiling&lt;/h3&gt;
&lt;p&gt;No cryptographic primitive can distinguish &quot;the user wanted the agent to delete this file&quot; from &quot;the user wanted to delete this file&quot; once both calls go through the same syscall. The OS sees bytes, not intent. The strongest architecture binds each intent to a fresh user-verification gesture (passkey plus Hello); at the limit, this requires one gesture per syscall, which is operationally unusable. The practical optimum is &lt;em&gt;batched intent&lt;/em&gt; traded off against &lt;em&gt;granularity of audit&lt;/em&gt;: the user authorises a session-scoped delegation (&quot;for the next 20 minutes, this agent may modify files under &lt;code&gt;.\old-builds&lt;/code&gt;&quot;), the system mints a macaroon attenuated to that scope, and the audit log records the macaroon issuance plus every syscall it gates. The intent is bounded by the scope of the gesture; the audit reconstructs intent after the fact from scope plus action.&lt;/p&gt;
&lt;h3&gt;8.2 The Confused-Deputy ceiling&lt;/h3&gt;
&lt;p&gt;Hardy 1988, in the verbatim formulation that began the genealogy. &quot;The fundamental problem is that the compiler runs with authority stemming from two sources. (That&apos;s why the compiler is a confused deputy.) ... The compiler had no way of expressing these intents!&quot; [@hardy-caplore]. Any system that grants a deputy ambient authority -- authority derived from multiple sources without the deputy being able to name &lt;em&gt;which&lt;/em&gt; authority it intends to exercise for a given action -- is structurally susceptible to confused-deputy attacks. Capability-style attenuation (macaroons, capability SIDs in AppContainer) mitigates by forcing the deputy to name the authority on every call; it does not eliminate, because a deputy that holds two authorities can be tricked into presenting the wrong one if the protocol does not require the relying party to verify the intent.&lt;/p&gt;
&lt;h3&gt;8.3 The &quot;running as the user&quot; ceiling and the MSRC servicing-criteria rule&lt;/h3&gt;
&lt;p&gt;As long as the agent process runs under the user&apos;s primary token, the agent inherits the user&apos;s TGT, the user&apos;s DPAPI master keys, and the user&apos;s network privileges by construction. That inheritance is what makes attribution collapse. The proposed remedy in Section 6 is an &lt;code&gt;AgentPrincipal&lt;/code&gt; that lives next to the user SID in the same token. The remedy depends on whether the Windows Security Response Center treats user-versus-agent as a defended security boundary.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://paragmali.com/blog/windows-security-boundaries-the-document-that-decides-what-g/&quot; rel=&quot;noopener&quot;&gt;MSRC servicing criteria document&lt;/a&gt; defines the boundary taxonomy. The verbatim opening: &quot;Does the vulnerability violate the goal or intent of a security boundary or a security feature? ... A security boundary provides a logical separation between the code and data of security domains with different levels of trust&quot; [@ms-msrc-servicing]. The same document gives the kernel-mode-versus-user-mode separation as the canonical example of a security boundary, and that example is the article&apos;s anchor for what counts as a security boundary under current MSRC policy. By inference from the boundary taxonomy table, &lt;em&gt;within the same logon session and the same primary token&lt;/em&gt;, a defect that requires the attacker to already be running as the user does not, by default, constitute a violation of a security boundary -- it is, under the criteria, a defense-in-depth concern rather than a CVE-eligible servicing event.&lt;/p&gt;

The MSRC document is the governance object that fixes the rule. For an OS-level `AgentPrincipal` to be a defensible boundary -- and therefore for an agent-bypass to qualify as a CVE-eligible servicing event -- the document itself would have to be amended to add the user-versus-agent split to the boundary taxonomy.&lt;p&gt;That is not an engineering ask. It is a governance ask. Microsoft has amended the document before (AppContainer was added to the security-boundary list during the Windows 10 era; Win32 App Isolation is built on the foundation of AppContainers, &quot;which is recognized as a security boundary by Microsoft&quot; [@ms-blogs-win32appiso]). The amendment is possible. It is not obviously imminent. As of May 2026 the MSRC document does not contain agent-related boundary language.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;That third ceiling lands the shift. The deepest part of the gap is not engineering -- it is &lt;em&gt;policy&lt;/em&gt;. The engineering substrate is more or less in place; the governance posture is not. An OS-level &lt;code&gt;AgentPrincipal&lt;/code&gt; either becomes a new defended boundary (a major policy shift) or ships as a feature without boundary semantics (no CVE eligibility for agent-bypass). The Confused-Deputy ceiling is the structural mirror of the same observation: the only mitigation a deputy can perform is to &lt;em&gt;name&lt;/em&gt; the two authorities at the access-check, which is what &lt;code&gt;AgentPrincipal&lt;/code&gt; would do.&lt;/p&gt;
&lt;p&gt;Given the structural ceilings, what specifically is still open in May 2026?&lt;/p&gt;
&lt;h2&gt;9. Seven open problems&lt;/h2&gt;
&lt;p&gt;Seven numbered, unsolved problems as of May 2026. None of them are blocked by physics; all of them are blocked by some combination of plumbing, packaging convention, and policy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The state-of-the-art inventory this article rests on names seven open problems; an earlier pass listed six and missed the methodological one (no shared cross-method benchmark). The seventh is restored at the end of this section, and three of the existing problems (cross-process delegation, signed-binary harvesting, and the DAA deployment chain) are walked through in worked-example form using kernel and TPM primitives the prior sections have named.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9.1 The Electron and Win32 packaging gap&lt;/h3&gt;
&lt;p&gt;Claude Desktop, ChatGPT Desktop, and Cursor are Electron and NSIS-installed apps; GitHub Copilot CLI is a Node.js CLI distributed via npm, WinGet, or MSI. None of them ships as MSIX. The package SID surface from AppContainer does not reach them; nothing in their primary token marks them as an agent class. Win32 App Isolation [@ms-blogs-win32appiso] is the in-flight retrofit -- adoption among agent vendors three years after the public preview is minimal. Until the dominant agent vendors ship under MSIX or Win32 App Isolation, the only kernel-level identifier the Pillar 2 substrate can grab onto does not exist for the agents readers actually have installed.&lt;/p&gt;
&lt;h3&gt;9.2 Cross-process delegation: a worked example&lt;/h3&gt;
&lt;p&gt;The failure mode lives at the kernel layer and is invisible from the source code of the agent. Claude Desktop on Windows 11 24H2 is a non-MSIX, NSIS-installed Electron binary whose primary process runs under the user&apos;s primary token. When that process calls &lt;code&gt;CreateProcessW(L&quot;powershell.exe&quot;, L&quot;-NoProfile -Command \&quot;Remove-Item ...\&quot;&quot;, ...)&lt;/code&gt; to shell out, the kernel side of &lt;code&gt;NtCreateUserProcess&lt;/code&gt; calls &lt;code&gt;PspAllocateProcess&lt;/code&gt;, which calls into the security reference monitor to assign a primary token to the child. By default -- when the caller passes neither a replacement token through &lt;code&gt;CreateProcessAsUserW&lt;/code&gt; nor a &lt;code&gt;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&lt;/code&gt; entry in &lt;code&gt;STARTUPINFOEX::lpAttributeList&lt;/code&gt; -- the kernel duplicates the parent&apos;s primary token verbatim. Microsoft Learn is explicit: &quot;The new process runs in the security context of the calling process&quot; [@ms-learn-createproc]. The child inherits the user&apos;s SID, the user&apos;s group SID list, the user&apos;s privileges, the medium integrity level, and the per-session logon SID. No &lt;code&gt;TokenAppContainerSid&lt;/code&gt; is present (&lt;code&gt;GetTokenInformation(..., TokenIsAppContainer, ...)&lt;/code&gt; returns zero); no &lt;code&gt;TokenPackageClaims&lt;/code&gt; field is present. There is no field anywhere in the child&apos;s primary token that distinguishes &quot;this PowerShell child was launched by Claude Desktop&quot; from &quot;this PowerShell child was launched by Notepad.&quot;&lt;/p&gt;
&lt;p&gt;The rule that does work for packaged parents -- and the cliff at the unpackaged boundary -- are both in Microsoft Learn for legacy-application AppContainer behaviour: &quot;When an unpackaged process running in an app container calls CreateProcess, the child process typically inherits the parent&apos;s token. That token includes the integrity level (IL) and app container info&quot; [@ms-learn-appcontainer-legacy]. For an MSIX-packaged parent like Microsoft Copilot for Windows, the kernel propagates the &lt;code&gt;TokenAppContainerSid&lt;/code&gt; and capability list to the child [@ms-learn-msix-container]. For an unpackaged Electron parent, the rule does not fire because there is nothing to propagate; even for the packaged parent, the package SID identifies the package, not an agent role. The semantics the article calls for -- &quot;this child is acting on behalf of the user, and the agent making that decision is X&quot; -- has no kernel-layer encoding today.&lt;/p&gt;
&lt;p&gt;A hypothetical &lt;code&gt;AgentPrincipal&lt;/code&gt;-preserving inheritance rule would extend the existing AppContainer rule. The pseudocode below is illustrative of an unshipped kernel routine; it cannot be executed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// PspAssignPrimaryTokenWithAgentInheritance (kernel-mode, called by
// NtCreateUserProcess via PspAllocateProcess for the SRM token path).

NTSTATUS PspAssignPrimaryTokenWithAgentInheritance(
    PEPROCESS  ChildProcess,
    PEPROCESS  ParentProcess,
    PTOKEN     ParentToken,
    PSECURITY_CAPABILITIES Caps,
    PIMAGE_SECTION_OBJECT  ChildImage)
{
    PTOKEN ChildToken;
    NTSTATUS s = SeSubProcessToken(ParentToken, &amp;amp;ChildToken);
    if (!NT_SUCCESS(s)) return s;

    // Existing rule (today&apos;s kernel): propagate AppContainer SID + caps
    // when the parent token is in an AppContainer.
    if (SeTokenIsAppContainer(ParentToken)) {
        SePropagateAppContainerSids(ParentToken, ChildToken);
    }

    // New rule: AgentPrincipal inheritance with refusal path.
    PTOKEN_AGENT_PRINCIPAL_INFORMATION AgentInfo =
        SeQueryAgentPrincipalInformation(ParentToken);
    if (AgentInfo != NULL) {
        // Child image must be Authenticode-signed and on the
        // AgentSID&apos;s per-tool allowlist authored at agent-install
        // time via the FIDO AATWG Pillar 1 ceremony.
        if (!SeImageIsAuthenticodeSigned(ChildImage) ||
            !SeAgentAllowlistContains(AgentInfo-&amp;gt;AgentSid,
                                      ChildImage-&amp;gt;SignerCertHash)) {
            ObDereferenceObject(ChildToken);
            return STATUS_AGENT_TOOL_NOT_ALLOWED;
        }
        SePropagateAgentPrincipal(AgentInfo, ChildToken);
        EtwWriteAgentChildProcessCreated(
            ParentProcess, ChildProcess, AgentInfo-&amp;gt;AgentSid);
    }
    return PsAssignPrimaryToken(ChildProcess, ChildToken);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The refusal path is the load-bearing part. Today, &lt;code&gt;CreateProcess&lt;/code&gt; from an agent process to any binary the user can execute succeeds. Under the proposed rule, the agent&apos;s per-tool allowlist is the access-check boundary: a child image not on the allowlist -- for example, an attacker-dropped &lt;code&gt;payload.exe&lt;/code&gt; masquerading as PowerShell -- is refused at the syscall layer, with the refusal landing in ETW as an &lt;code&gt;AgentToolDenied&lt;/code&gt; event correlated to the agent SID. Win32 App Isolation already ships an analogous inheritance-with-refusal shape for unpackaged Win32 binaries: the Application Capability Profiler produces a developer-supplied capability profile that the kernel enforces at child-process creation, with unprofiled syscalls failing the same way AppContainer fails capability-bounded calls today [@ms-learn-win32appiso-overview]. The missing piece for the agent case is the &lt;em&gt;agent&lt;/em&gt; dimension on the token, not the mechanism.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Even if Windows ships an &lt;code&gt;AgentPrincipal&lt;/code&gt;, every agent that shells out to PowerShell, Node, or Python today loses agent-attribution at the child-process boundary, because non-packaged children inherit the user&apos;s primary token unmodified. Agent vendors should assume that &quot;shell out to a language runtime&quot; is the default attack path; readers writing agent tools should keep operations inside the agent process where possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9.3 Revocation latency&lt;/h3&gt;
&lt;p&gt;Revoking an agent&apos;s Entra Agent ID in the cloud should fan out to a Kerberos TGT purge plus local token-cache invalidation plus a WAM cache flush, without logging the user out. The plumbing does not exist on Windows. Continuous Access Evaluation is the closest cloud-side analog -- it can short-circuit access-token reuse against a remote API on the cloud side, but it cannot reach the user&apos;s local Kerberos cache or the user&apos;s local DPAPI scope.&lt;/p&gt;
&lt;h3&gt;9.4 MCP authorisation alignment&lt;/h3&gt;
&lt;p&gt;Every MCP server today runs its own OAuth 2.1 plus RFC 9728 PRM dance [@anthropic-mcp-2025-11-25]. The OS-side agent principal has no role; per-tool authorisation lives above the OS and is invisible to ETW and Defender. An MCP-aware &lt;code&gt;AgentPrincipal&lt;/code&gt; would let the OS surface the per-tool grant chain as part of the access-check audit record, so EDR can correlate an MCP grant to a downstream syscall.&lt;/p&gt;
&lt;h3&gt;9.5 DAA-for-agent-class deployment: end-to-end&lt;/h3&gt;
&lt;p&gt;TPM 2.0 Direct Anonymous Attestation can, in principle, prove &quot;this binary is &lt;em&gt;some&lt;/em&gt; Claude Desktop&quot; without identifying the specific instance. As of May 2026 no agent vendor uses DAA as an attestation issuer for itself; the deployment glue between Pluton, Azure Attestation Service, and an AATWG Pillar 2 verifier does not exist. The cryptographic substrate is in the TPM; the deployment pipeline that would carry it is not. Working the chain end-to-end clarifies what would have to ship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Leg 1: the agent vendor as DAA issuer.&lt;/strong&gt; Anthropic, Microsoft, or Cursor plays the role of &lt;em&gt;DAA Issuer&lt;/em&gt; in the canonical three-entity formulation: &quot;The entities are the DAA Member (TPM platform or EPID-enabled microprocessor), the DAA Issuer and the DAA Verifier. The issuer is charged to verify the TPM platform during the Join step and to issue DAA credential to the platform&quot; [@wiki-daa]. The vendor maintains a long-term issuer key pair; the public half is the basename a verifier consults. Discovery is conventionally JSON Web Key Set under OpenID Connect Discovery 1.0, the same mechanism Azure Attestation Service itself exposes for relying parties [@ms-learn-aas-concepts]. No agent vendor publishes such an endpoint today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Leg 2: the Join step at first run.&lt;/strong&gt; On first agent launch, a Windows-side broker (a proposed extension to WAM) mediates the Join with the vendor&apos;s issuer endpoint. Conceptually, the local Pluton or TPM runs &lt;code&gt;TPM2_CreatePrimary(hierarchy = TPM_RH_ENDORSEMENT, inPublic.type = TPM_ALG_ECDAA, ...)&lt;/code&gt; to mint the member key, then &lt;code&gt;TPM2_Commit&lt;/code&gt; to produce the ECDAA ephemeral commitments. The vendor issues an ECDAA membership credential -- not a per-device cert; the credential is unlinkable across signatures by construction -- and Pluton stores it in non-volatile memory [@ms-learn-pluton-tpm]. The user&apos;s WebAuthn-bound consent (the Pillar 1 evidence) is recorded in the vendor&apos;s audit log. The WAM broker has no agent-scoped DAA Join extension as of May 2026.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Leg 3: the Sign step on each agent action.&lt;/strong&gt; Per-action, Pluton runs &lt;code&gt;TPM2_Commit&lt;/code&gt; with the verifier&apos;s nonce, then &lt;code&gt;TPM2_Sign(daaMemberKey, message_digest, scheme = TPM_ALG_ECDAA)&lt;/code&gt;. The WAM broker bundles the ECDAA signature into an Azure-Attestation-Service-style JWT envelope and submits it to the AATWG Pillar 2 verifier endpoint [@ms-learn-aas-overview; @ms-learn-aas-concepts]. The verifier de-serialises the JWT, runs the four-step check from Problem 6 (Authenticode chain plus Pluton EK chain plus measured-boot PCRs plus application-load PCR) plus the ECDAA verification. The verifier learns &quot;some Anthropic Claude Desktop is making this request&quot; but not &lt;em&gt;which&lt;/em&gt; device; two consecutive actions from the same agent on the same device are cryptographically indistinguishable from two actions on different devices. The Azure Attestation Service policy engine does not carry an &quot;agent-class membership&quot; claim type that downstream Conditional Access can consume as a signal today.&lt;/p&gt;
&lt;p&gt;The deployment gap, taken together, is integration rather than algorithm. Every primitive exists in the TCG TPM 2.0 specification, in the Azure Attestation Service engine, and in the WAM broker. Nobody has glued vendor-issuer plus Pluton-member plus WAM-broker plus AAS-verifier into a shipping pipeline. The corpus&apos;s sibling DAA article carries the verbatim TCG protocol detail; this section anchors the deployment chain to the Windows-side primitives Microsoft documents.&lt;/p&gt;
&lt;h3&gt;9.6 Signed-binary harvesting: a worked example&lt;/h3&gt;
&lt;p&gt;An attacker who steals a legitimately signed agent binary inherits any &lt;code&gt;AgentPrincipal&lt;/code&gt; that would otherwise be granted to that binary -- unless attestation is bound to runtime state, which DAA alone cannot provide. The attack is the Stuxnet-class supply-chain pattern, generalised to the agent case.&lt;/p&gt;
&lt;p&gt;The attacker compromises Anthropic&apos;s signing infrastructure -- the private-key custodian for the EV code-signing certificate, or the build pipeline that calls the custodian. The historical precedent for this exact attack shape is the 2010 Stuxnet incident, in which two distinct Authenticode driver-signing certificates were exfiltrated from JMicron Technology Corp and Realtek Semiconductor Corp (both VeriSign-issued) and used to produce kernel drivers Windows accepted as legitimate [@wiki-stuxnet]. The attacker now produces &lt;code&gt;claude-desktop-evil.exe&lt;/code&gt; with the Anthropic Subject Name signed by the legitimate private key; &lt;code&gt;wintrust.dll!WinVerifyTrust&lt;/code&gt; returns &lt;code&gt;S_OK&lt;/code&gt;. To every existing Authenticode consumer -- App Control for Business publisher rules, AppLocker publisher conditions, Defender signer-based exclusions -- the malicious binary is indistinguishable from the legitimate Claude Desktop. If a hypothetical &lt;code&gt;AgentPrincipal&lt;/code&gt; is bound to the install-time Authenticode signature alone, Windows mints the same &lt;code&gt;AgentSid&lt;/code&gt; for the malicious binary; the audit trail still reads &quot;Claude Desktop did X&quot; because the binary cryptographically &lt;em&gt;is&lt;/em&gt; Claude Desktop as far as the load-time check can tell.&lt;/p&gt;
&lt;p&gt;The remediation runs through Pluton&apos;s measured-boot chain plus Azure Attestation Service. Microsoft Pluton &quot;provides hardware-based root of trust, secure identity, secure attestation, and cryptographic services&quot; [@ms-learn-pluton]; secure attestation is exactly the role this attack requires. Pluton-rooted attestation is a TPM 2.0 &lt;code&gt;TPM2_Quote&lt;/code&gt; over a selected set of Platform Configuration Registers signed by an Attestation Identity Key whose endorsement chain terminates at Pluton&apos;s manufacturer-burned Endorsement Key [@ms-learn-pluton-tpm]. The PCRs that matter for distinguishing the legitimate Claude Desktop from &lt;code&gt;claude-desktop-evil.exe&lt;/code&gt; are PCRs 0 through 7 (UEFI firmware, Option ROMs, boot manager, Secure Boot policy -- the pre-OS measured-boot range per the TCG PC Client Platform Firmware Profile), PCR 11 (BitLocker and the OS loader application code path, into which ELAM and kernel-mode driver measurements extend on Windows), and an application-extend PCR (PCR 12 in many shipping configurations, sometimes a virtual PCR via the TPM Base Services or VBS) carrying the SHA-256 of the on-disk agent image plus the cumulative hash of in-process module loads. The wire format the verifier evaluates is the Azure-Attestation-Service JWT envelope [@ms-learn-aas-overview; @ms-learn-aas-concepts].&lt;/p&gt;
&lt;p&gt;The AATWG Pillar 2 verifier performs four cascaded checks in order. (1) Authenticode chain valid -- the agent binary&apos;s image hash is covered by an Authenticode signature whose chain terminates in a trusted root; this is the only check today. (2) Pluton EK chain valid -- the AAS-issued JWT was signed by an AIK whose endorsement chain terminates at a Microsoft-issued Pluton EK certificate, proving the quote came from real hardware. (3) Measured-boot PCRs in known-good set -- PCRs 0 through 11 match a reference value for the user&apos;s claimed device class and OS build (Windows 11 24H2, Secure Boot on, VBS on). (4) Application-load PCR matches the published-by-vendor measurement -- PCR 12 matches the hash the agent vendor published in a verifier-discoverable channel for the legitimate release. Step (4) is the runtime-state binding. The attacker now needs either to compromise the vendor&apos;s published-hashes channel &lt;em&gt;in addition to&lt;/em&gt; the signing key, or to compromise the user&apos;s Pluton-rooted measured-boot chain &lt;em&gt;in addition to&lt;/em&gt; the signing key. Both are strictly stronger compromises; both raise the attacker&apos;s cost by a hardware-vs-software factor. No agent vendor publishes the application-load-PCR reference channel today; the AATWG Pillar 2 verifier reference implementation does not exist.&lt;/p&gt;
&lt;h3&gt;9.7 Agent-identity benchmark gap&lt;/h3&gt;
&lt;p&gt;No published peer-reviewed benchmark suite compares the cloud, OS, and broker positions on a common agent-attribution workload as of May 2026. The closest available empirical results are vendor-internal: AP2 v0.2 demo measurements published with the September 2025 cloud.google.com launch and the subsequent ap2-protocol.org release notes [@ap2-cloud-blog; @ap2-protocol]; Microsoft Build 2025 session telemetry for WAM Token Protection latency [@ms-learn-wam]; RFC 8693 OAuth Token Exchange implementations measured in IEEE / ACM / USENIX venues since 2019 [@ietf-rfc-8693] but in non-agent configurations. None of these is a comparison; all are baselines.&lt;/p&gt;
&lt;p&gt;The absence matters at two levels. Practitioners deploying an agent stack in 2026 cannot make principled latency, scale, or audit-completeness tradeoffs across the seven methods catalogued in Section 7 without a shared harness; a cloud-managed enterprise running Entra Agent ID plus RFC 8693 plus MCP cannot compare its per-action token-mint latency against a competing WAM-extended deployment without rerunning both stacks under the same workload. At the literature level, the field has named its primitives, sketched its composition rules, and assembled its wire formats but has not yet produced shared evaluation workloads.&lt;/p&gt;
&lt;p&gt;A public AATWG-blessed benchmark would have to measure four axes matching the four distinct architectural moves the article catalogues: per-action token-mint latency from agent decision to capability-token presentation; ETW event volume per agent action and per agent session; attestation chain length (Pluton AIK to AAS JWT to vendor ECDAA to relying-party verify) in the Pillar 2 path; and cross-process-delegation propagation success rate (the §9.2 property -- the fraction of agent-spawned child processes that retain agent attribution end-to-end). The four axes are necessary because the seven methods make incomparable tradeoffs across them, and the only honest cross-method statement today is the negative one: no shared benchmark exists.&lt;/p&gt;
&lt;h2&gt;10. What to ship today&lt;/h2&gt;
&lt;p&gt;Six things a Windows engineer or agent vendor can do in May 2026 using only primitives that already ship.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ship the agent as an MSIX-packaged app to get a stable package SID.&lt;/strong&gt; The package SID is not an agent principal, but it is the only kernel-level identifier you can rely on today [@ms-learn-appcontainer; @ms-learn-appcontainer-legacy]. Packaging is the precondition for per-package DPAPI scope, capability-gated network egress under Windows Filtering Platform, and any future &lt;code&gt;AgentPrincipal&lt;/code&gt; retrofit -- a non-packaged Electron app gets none of these.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mint agent-scoped tokens from Entra Agent ID&lt;/strong&gt; rather than letting the agent act under the user&apos;s delegated token [@ms-learn-agentid; @ms-learn-agentoauth]. You get Conditional Access on the agent identity, Purview agent-interaction audit fields, and a sign-in log entry distinct from the user&apos;s. Available grant types are &lt;code&gt;client_credentials&lt;/code&gt;, &lt;code&gt;jwt-bearer&lt;/code&gt;, and &lt;code&gt;refresh_token&lt;/code&gt;; interactive flows are not supported for agent entities [@ms-learn-agentobo].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Kerberos Resource-Based Constrained Delegation rather than unconstrained delegation.&lt;/strong&gt; Scope the allowed-to-delegate-to list to the specific back-end services the agent legitimately calls [@ms-learn-mssfu]. RBCD inverts the authoring point: the back-end resource names the front-end services it accepts delegation from, which is the right authoring point when the agent population changes faster than the back-end population.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Route agent tool invocations through an MCP server with the 2025-11-25 OAuth 2.1 plus RFC 9728 PRM authorisation profile enabled&lt;/strong&gt; [@anthropic-mcp-2025-11-25; @ietf-rfc-9728; @ietf-oauth-v2-1]. Do not rely on the user&apos;s logon credentials at the tool layer. Each tool grant is then explicit, scoped, and revocable independently of the user&apos;s session.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The cost of MSIX packaging is paid once, at install-time, and the cost of leaving the Electron user-DPAPI scope alone is paid forever. Every future agent-attribution retrofit -- per-package DPAPI scope, capability-gated network egress, an &lt;code&gt;AgentPrincipal&lt;/code&gt; token field -- depends on the package SID being there to attach to. Pay the install-time cost.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treat every locally stored agent secret as a user-DPAPI-scope liability for Electron-wrapped agents.&lt;/strong&gt; Any other process under the same user can decrypt it. If isolation matters, store the secret in a Virtualisation-Based Security trustlet behind Credential Guard, in an AppContainer per-package DPAPI scope, or in a Pluton-backed key. The substrate exists; the decision is whether to use it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subscribe to the FIDO Alliance AATWG and Payments TWG mailing lists&lt;/strong&gt; [@fido-aatwg-pr; @fido-aatwg-landing]. Wire formats are in flight; deployment characteristics will not be settled until at least H2 2026. The mailing-list traffic is the cheapest signal you can get on what the agent-protocol wire format will look like in 2027.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

Open an elevated PowerShell prompt and run `whoami /all`. You will see the primary user SID, the group SIDs, the integrity-level overlay, and the enabled privileges that your shell process is currently running with -- the exact data structure the kernel reads on every `SeAccessCheck` call.&lt;p&gt;If you have an MSIX-packaged AppContainer process available (the Microsoft Store apps are the easiest source), run &lt;code&gt;Get-AppxPackage | Select Name, PackageFamilyName, SignatureKind&lt;/code&gt; and then &lt;code&gt;Get-AppxPackage &amp;lt;Name&amp;gt; | Get-AppxPackageManifest&lt;/code&gt; to see the capability list. The capability SIDs that show up in the manifest are the per-resource gates that AppContainer adds on top of the package SID. There is, today, no analogous &lt;code&gt;whoami /agent&lt;/code&gt; command. That absence is the gap.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The first RunnableCode block gives a hands-on feel for the shape of a primary token&apos;s principal set and what the proposed &lt;code&gt;AgentPrincipal&lt;/code&gt; would add. The second demonstrates macaroon attenuation so the chained-HMAC construction stops being abstract.&lt;/p&gt;
&lt;p&gt;{`
// Conceptual model of a Windows access token, as whoami /all would print it.
// On a non-AppContainer process today there are user/group SIDs but no
// package SID and no proposed AgentPrincipalSid.
const tokenToday = {
  primaryUserSid: &apos;S-1-5-21-3623811015-3361044348-30300820-1013&apos;,
  groupSids: [
    &apos;S-1-5-21-3623811015-3361044348-30300820-513&apos;, // Domain Users
    &apos;S-1-5-32-545&apos;,                                // BUILTIN\\Users
    &apos;S-1-5-11&apos;,                                    // Authenticated Users
  ],
  packageSid: null,        // not in an AppContainer
  agentPrincipalSid: null, // proposed in this article; not in any shipping build
  integrityLevel: &apos;Medium&apos;,
};&lt;/p&gt;
&lt;p&gt;// Same agent process under the proposed seventh-generation token shape.
const tokenWithAgentPrincipal = Object.assign({}, tokenToday, {
  packageSid: &apos;S-1-15-2-2756086904-3023256918-1882200006-3954548928-2786400166-3567463568-3801030027&apos;,
  agentPrincipalSid: &apos;S-1-15-7-1001-AGENT-CLAUDE-DESKTOP&apos;,
  integrityLevel: &apos;Low&apos;,
});&lt;/p&gt;
&lt;p&gt;function summarise(label, t) {
  console.log(&apos;--- &apos; + label + &apos; ---&apos;);
  console.log(&apos;Primary user SID: &apos; + t.primaryUserSid);
  console.log(&apos;Package SID:      &apos; + (t.packageSid || &apos;(none)&apos;));
  console.log(&apos;Agent SID:        &apos; + (t.agentPrincipalSid || &apos;(none)&apos;));
  console.log(&apos;Integrity:        &apos; + t.integrityLevel);
}&lt;/p&gt;
&lt;p&gt;summarise(&apos;Token today (Electron Claude Desktop under user logon)&apos;, tokenToday);
summarise(&apos;Token after the seventh generation&apos;, tokenWithAgentPrincipal);
`}&lt;/p&gt;
&lt;p&gt;{`
// Conceptual macaroon: each caveat is bound by a fresh HMAC over the prior
// HMAC plus the caveat text. The holder can only attenuate; never expand.
const crypto = {
  hmac(key, msg) {
    // Toy mixing function, not cryptographic. Real macaroons use HMAC-SHA256.
    let h = 2166136261;
    const data = String(key) + &apos;|&apos; + String(msg);
    for (let i = 0; i &amp;lt; data.length; i++) {
      h = Math.imul(h ^ data.charCodeAt(i), 16777619);
    }
    return (&apos;00000000&apos; + (h &amp;gt;&amp;gt;&amp;gt; 0).toString(16)).slice(-8);
  },
};&lt;/p&gt;
&lt;p&gt;function mint(rootKey, identifier) {
  const sig = crypto.hmac(rootKey, identifier);
  return { id: identifier, caveats: [], sig };
}&lt;/p&gt;
&lt;p&gt;function attenuate(macaroon, caveat) {
  const nextSig = crypto.hmac(macaroon.sig, caveat);
  return {
    id: macaroon.id,
    caveats: macaroon.caveats.concat([caveat]),
    sig: nextSig,
  };
}&lt;/p&gt;
&lt;p&gt;function verify(rootKey, macaroon) {
  let sig = crypto.hmac(rootKey, macaroon.id);
  for (const c of macaroon.caveats) {
    sig = crypto.hmac(sig, c);
  }
  return sig === macaroon.sig;
}&lt;/p&gt;
&lt;p&gt;const ROOT = &apos;agent-issuer-root-secret-123&apos;;
const m0 = mint(ROOT, &apos;agent-claude-desktop-session-42&apos;);
const m1 = attenuate(m0, &apos;tool = filesystem.read&apos;);
const m2 = attenuate(m1, &apos;path-prefix = C:\\Users\\parag\\projects&apos;);
const m3 = attenuate(m2, &apos;expires-at = 2026-05-25T15:00:00Z&apos;);&lt;/p&gt;
&lt;p&gt;console.log(&apos;Base verified:       &apos; + verify(ROOT, m0));
console.log(&apos;Three-caveat chain:  &apos; + verify(ROOT, m3));&lt;/p&gt;
&lt;p&gt;// Holder cannot un-attenuate. A holder who tries to drop a caveat would
// have to compute the prior signature, which requires the issuer&apos;s secret.
const tampered = { id: m3.id, caveats: m3.caveats.slice(0, 2), sig: m3.sig };
console.log(&apos;Tampered (verify):   &apos; + verify(ROOT, tampered));
`}&lt;/p&gt;
&lt;p&gt;Six things you can do today; eight misconceptions you can stop carrying.&lt;/p&gt;
&lt;h2&gt;11. Misconceptions and practical concerns&lt;/h2&gt;

Entra Agent ID solves the cloud-side half. It gives an agent a directory entry, a sign-in log, a Conditional Access surface, and a token-exchange path under `client_credentials`, `jwt-bearer`, and `refresh_token` grants [@ms-learn-agentid; @ms-learn-agentoauth]. What it does not do is reach inside the operating system. A locally-installed agent with an Entra Agent ID still calls the Windows file-system API under the user&apos;s primary token, decrypts user-DPAPI blobs as the user, and impersonates over RPC as the user. The OS-side attribution stays collapsed even when the cloud-side attribution is clean. Both layers are necessary; neither is sufficient.

The Microsoft On-Behalf-Of flow [@ms-learn-obo-flow] and RFC 8693 Token Exchange [@ietf-rfc-8693] solve the *back-end-to-back-end* delegation problem: a web API can call another web API as the user. They do not address desktop attribution. When the user&apos;s Microsoft 365 Copilot client (running on the user&apos;s desktop as the user) calls a downstream API, the cloud-side token carries the actor claims; the desktop-side process still runs under the user&apos;s primary token, and the OS-side audit trail still records the user as the syscall principal. OBO is the cloud answer to a different question.

The package SID is part of the agent attribution story, but it does not by itself constitute agent attribution. The verbatim Microsoft Learn rule is that &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-appcontainer]. That gives the access-check a way to restrict the package&apos;s authority below the user&apos;s; it does not give downstream consumers a way to distinguish &quot;this package is an AI agent acting on a separable principal&quot; from &quot;this package is a UWP weather app.&quot; Treating the package SID as the agent principal also fails at the boundary every Electron-wrapped agent crosses today: those agents are not packaged, so they do not get a package SID at all.

NT 3.1 solved *user* attribution. The primary token model, the user SID structure, impersonation, `SeAccessCheck` -- all of these are about distinguishing one user from another and about a thread temporarily speaking as a different identity [@ms-learn-sids]. None of them solved *code* identity (which took until 1996 with Authenticode and was still being refined in 2017 with App Control for Business) and none of them solved *agent* identity (the open problem this article is about). The article must not conflate the three. The 1993 milestone is the foundation; it is also incomplete by design for the question that arrived in 2025.

Putting the agent in AppContainer gets you a package SID, a per-package DPAPI scope, capability-gated network egress, and a LowBox token; do this regardless of whether you call it an agent. What AppContainer does not give you is a kernel-level signal that distinguishes agent semantics from app semantics. Defender, ETW, and Conditional Access read package SIDs as package identifiers, not as agent role markers. A full agent-principal answer needs both the AppContainer-style sandbox and a sibling-principal SID that downstream consumers explicitly understand to be an agent identity, separately governed and separately revocable.

Administrator Protection is the existence proof that Windows can mint a second logon-session principal for a bounded action under a Hello user-verification gesture. As of May 2026 it is in Insider preview, not generally available; the feature surfaced on Insider builds in late 2025 and subsequent Insider build iterations have continued through 2026 [@ms-techcomm-adminprot]. The architectural pattern -- separate logon session, Hello gesture, scoped to one action -- is exactly the shape the seventh-generation agent principal would take. What it does not do today is generalise to delegation. The System-Managed Administrator Account is scoped to elevation. The agent-principal version would be scoped to &quot;this agent for the duration of this delegated session&quot;; the policy machinery does not yet exist.

No. Macaroons attenuate offline: the holder appends a caveat and a fresh HMAC, derives a strictly weaker macaroon, and presents it; the verifier walks the chain. OAuth bearer tokens cannot attenuate without round-tripping the authorisation server, which makes them structurally unfit for an agent that talks to many tools with many different scope shapes [@macaroons-ndss; @macaroons-gr]. See §7.3 for the worked construction.

The Windows framing is specific because the article is specifically about Windows. The underlying problem is platform-general. iOS App Attest (`DCAppAttestService`) and the Android per-app UID model are the cross-platform analogs of an OS-level application principal; neither has a published agent-identity story as of May 2026. macOS has its own per-app code-signing and entitlement substrate; it also has no shipping agent-principal model. Linux distributions vary. The fact that Windows ships the AppContainer dual-principal substrate already, plus the Administrator Protection bounded-second-principal existence proof, plus the Pluton-rooted attestation chain, makes Windows the operating system where the seventh-generation answer is most legibly close. It is not a Windows-only problem; it is a problem where Windows is unusually well-equipped to solve it next.
&lt;p&gt;The position the article ends on is the one each prior section pushed toward. As of May 2026 the cloud-identity layer has crossed the agentic-identity gap. The OS layer has not. The substrate primitives are largely in place: AppContainer package SIDs and the LowBox token, Kerberos S4U2Self and S4U2Proxy, WAM with TPM-bound refresh tokens, WebAuthn and Windows Hello with TPM-rooted user verification, TPM 2.0 Direct Anonymous Attestation, the Administrator-Protection separate-logon-session pattern, ETW as an audit channel. The missing piece is a kernel-recognised &lt;code&gt;AgentPrincipal&lt;/code&gt; extended from the package-SID substrate, gated by Hello-mediated Verifiable User Instructions, scoped via a macaroon-style per-tool capability layer, audited via an ETW &lt;code&gt;SubjectAgentSid&lt;/code&gt; field, and revocable via a CAE-style fan-out that does not require logging the user out. The MSRC servicing criteria document is the governance object that determines whether the seventh generation is a defensible security boundary or a defense-in-depth feature; that document has not been amended.&lt;/p&gt;
&lt;p&gt;The question is when, not whether, Windows ships the seventh generation.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;agentic-identity-on-windows-when-the-process-acting-on-your-behalf-isnt-you&quot; keyTerms={[
  { term: &quot;Primary token&quot;, definition: &quot;The kernel-resident access token attached to every Windows process at creation; carries the user SID, group SIDs, an integrity-level SID in a SYSTEM_MANDATORY_LABEL_ACE inside the token&apos;s SACL, and a privilege set.&quot; },
  { term: &quot;SID&quot;, definition: &quot;Security Identifier; the variable-length structure that uniquely identifies a security principal in a Windows access decision.&quot; },
  { term: &quot;AppContainer&quot;, definition: &quot;The kernel sandbox shipped in Windows 8 (October 26, 2012); attaches an S-1-15-2 package SID to the access token and gates access through the intersection of user and package authority.&quot; },
  { term: &quot;S4U2Self / S4U2Proxy&quot;, definition: &quot;Microsoft&apos;s Kerberos Protocol Extensions that let a server obtain a service ticket on behalf of a user without that user&apos;s password; the act-on-behalf-of primitive on Windows.&quot; },
  { term: &quot;Confused Deputy&quot;, definition: &quot;Hardy 1988: a process holding authority from multiple sources cannot express which authority it is exercising for a given action; the structural lower bound on agent attribution.&quot; },
  { term: &quot;Macaroon&quot;, definition: &quot;A bearer credential constructed as a chained HMAC over caveats; any holder can derive a strictly weaker credential by appending a caveat without round-tripping the issuer.&quot; },
  { term: &quot;DAA&quot;, definition: &quot;Direct Anonymous Attestation; a TPM 2.0 protocol that lets a device prove membership in a privacy-preserving group without revealing the specific device identity.&quot; },
  { term: &quot;WAM&quot;, definition: &quot;Web Account Manager; the on-device OAuth token broker that ships with Windows and binds refresh tokens to the TPM.&quot; },
  { term: &quot;Administrator Protection&quot;, definition: &quot;The Windows 11 Insider feature (late 2025 onwards, not yet GA) that mints a separate logon session under a System-Managed Administrator Account for an elevation, gated by a Windows Hello gesture.&quot; },
  { term: &quot;AATWG&quot;, definition: &quot;FIDO Alliance Agentic Authentication Technical Working Group; launched April 28, 2026 with three pillars: Verifiable User Instructions, Agent Authentication, Trusted Delegation for Commerce.&quot; }
]} questions={[
  { q: &quot;What is the primary structural reason a locally-installed AI agent on Windows in 2026 cannot be attributed separately from the logged-on user?&quot;, a: &quot;Every process on Windows carries one primary token; the token&apos;s user SID is the runtime principal; the agent process inherits the user&apos;s primary token; SeAccessCheck, ETW, RPC ACLs, Defender, and on-device Conditional Access all read the user SID as the principal.&quot; },
  { q: &quot;Name the six shipped generations of Windows app identity and the generation that has not yet shipped.&quot;, a: &quot;Gen 1 NT 3.1 primary token (1993); Gen 2 Authenticode (1996); Gen 3 SRP then AppLocker (2001, 2009); Gen 4 AppContainer + package SID (2012); Gen 5 App Control for Business (2017+); Gen 6 Administrator Protection (late 2025 Insider, not yet GA); Gen 7 AgentPrincipal (missing).&quot; },
  { q: &quot;Why does the AppContainer package SID not, by itself, constitute an agent principal?&quot;, a: &quot;The package SID identifies a package, not an agent role; downstream consumers cannot distinguish &apos;this package SID is an AI agent on a separable principal&apos; from &apos;this package SID is a UWP calculator&apos;; Electron-wrapped agents do not get a package SID at all.&quot; },
  { q: &quot;What are the three pillars of the FIDO AATWG and what wire-format primitive does each carry?&quot;, a: &quot;Pillar 1 Verifiable User Instructions: passkey-signed delegation token bound to a specific intent with short TTL. Pillar 2 Agent Authentication: attestation plus identity assertion. Pillar 3 Trusted Delegation for Commerce: AP2 mandate or Verifiable Intent SD-JWT chain.&quot; },
  { q: &quot;Why is attenuation without a round-trip the load-bearing property of macaroons in an agent setting?&quot;, a: &quot;An agent talks to many tools with many scope shapes; the holder can derive a weaker per-tool macaroon by appending a caveat and a fresh HMAC without round-tripping the issuer; the holder cannot un-attenuate; the issuer does not have to be online.&quot; },
  { q: &quot;What is the MSRC servicing-criteria policy ceiling and why does it matter for an AgentPrincipal?&quot;, a: &quot;Under the MSRC document, within the same logon session and the same primary token, a defect that requires the attacker to already be running as the user is treated as defense-in-depth rather than a CVE-eligible servicing event; for an OS-level AgentPrincipal to be a defensible security boundary the MSRC document itself would have to be amended to add the user-versus-agent split to the boundary taxonomy.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>identity</category><category>ai-agents</category><category>fido-aatwg</category><category>kerberos</category><category>appcontainer</category><category>entra-agent-id</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>AppContainer and LowBox Tokens: Windows&apos;s Capability Sandbox</title><link>https://paragmali.com/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/</link><guid isPermaLink="true">https://paragmali.com/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/</guid><description>How a single bit in Windows&apos;s access token, two new SID alphabets, and a per-package namespace partition let the kernel give two co-tenanted apps opposite verdicts.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
Windows&apos;s modern application sandbox rests on a single kernel primitive (*AppContainer / LowBox*) that Microsoft preserved under two names: *LowBox* inside the kernel and the `NtCreateLowBoxToken` syscall, *AppContainer* in the public API. One bit in `_TOKEN.Flags`, two new SID alphabets (`S-1-15-2-*` for packages, `S-1-15-3-*` for capabilities), and a per-package partition of the Object Manager namespace let `SeAccessCheck` give two co-tenanted processes opposite verdicts. UWP apps, MSIX-packaged desktop apps, Microsoft Edge LPAC renderers, and Windows Sandbox all rest on this primitive. James Forshaw&apos;s twelve-year exploit-research line shaped its current hardening.
&lt;h2&gt;1. Two Calculators&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;calc.exe&lt;/code&gt; on a stock Windows 11 25H2 installation. It is the Microsoft Store calculator, the UWP one, the one Windows ships in the default Start menu. Now download the legacy &lt;code&gt;win32calc.exe&lt;/code&gt; from a Windows 7 image and run it side by side. Same user. Same machine. Same disk, same DACLs on every file in your profile. The Mandatory Integrity Control label on &lt;code&gt;%USERPROFILE%\.ssh\id_ed25519&lt;/code&gt; is the default Medium for both processes&apos; access checks.&lt;/p&gt;
&lt;p&gt;Yet &lt;code&gt;win32calc.exe&lt;/code&gt; can read the SSH private key, and &lt;code&gt;calc.exe&lt;/code&gt; cannot. If you open both processes in Sysinternals Process Explorer and compare the Security tab side by side, every field that the classic Windows NT token model exposes looks identical. User SID: identical. Logon ID: identical. Primary group: identical. Privileges: identical. Even the integrity level, by the read path, looks the same on the user&apos;s profile files.&lt;/p&gt;
&lt;p&gt;The difference is one bit. In the kernel&apos;s &lt;code&gt;_TOKEN&lt;/code&gt; structure, the &lt;code&gt;TOKEN_LOWBOX&lt;/code&gt; flag (SDK constant value &lt;code&gt;0x4000&lt;/code&gt;, the 15th bit of the &lt;code&gt;TokenFlags&lt;/code&gt; word) is set on the UWP &lt;code&gt;calc.exe&lt;/code&gt; token and clear on &lt;code&gt;win32calc.exe&lt;/code&gt; [@nostarch-wsi]. Microsoft&apos;s documentation calls the resulting token an &lt;em&gt;AppContainer token&lt;/em&gt;; the kernel call that creates it preserved the older marketing name. &quot;AppContainer was originally named &lt;em&gt;LowBox&lt;/em&gt; (prior to the release of Windows 8). That legacy name can be seen in API names such as &lt;strong&gt;NtCreateLowBoxToken&lt;/strong&gt;&quot; [@ms-learn-legacy].&lt;/p&gt;

AppContainer was originally named _LowBox_ (prior to the release of Windows 8). That legacy name can be seen in API names such as **NtCreateLowBoxToken**. -- Microsoft Learn

The public-API name for a kernel-enforced per-application sandbox introduced in Windows 8 [@ms-learn-legacy]. An AppContainer process runs under a *LowBox token*, a special access token whose `LowBoxToken` flag, package SID, capability SIDs, and per-instance number cause the Security Reference Monitor to apply additional access-check rules beyond the classic user-and-groups DACL walk. The kernel-internal name *LowBox* is preserved in `NtCreateLowBoxToken` and several adjacent symbol names [@ms-learn-ntcreatelowboxtoken].
&lt;p&gt;If you read the eight sub-authorities of the Calculator&apos;s package SID -- something like &lt;code&gt;S-1-15-2-466767348-3739614953-2700836392-...&lt;/code&gt; (the trailing five sub-authorities are elided here) -- you are reading a deterministic SHA-256 hash of the &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;package family name&lt;/a&gt;, sliced into eight 4-byte 32-bit sub-authorities [@ms-learn-derivesid] [@nostarch-wsi]. Every Windows 11 machine in the world derives the same SID for &lt;code&gt;Microsoft.WindowsCalculator_8wekyb3d8bbwe&lt;/code&gt;, and DACLs on the per-package data directories name precisely that SID. Section 5 will walk this in detail; for now, notice that the SID alphabet starts with &lt;code&gt;S-1-15-2&lt;/code&gt;, not the familiar &lt;code&gt;S-1-5-21&lt;/code&gt; of user SIDs. Microsoft introduced two new SID prefixes in Windows 8: &lt;code&gt;S-1-15-2-*&lt;/code&gt; for packages and &lt;code&gt;S-1-15-3-*&lt;/code&gt; for capabilities. Both live in identifier authority 15, the &lt;em&gt;App Package Authority&lt;/em&gt; [@ms-learn-well-known-sids].&lt;/p&gt;

flowchart TD
    A[User Alice opens id_ed25519] --&amp;gt; B{&quot;Is _TOKEN.Flags.LowBoxToken set?&quot;}
    B --&amp;gt;|&quot;No (win32calc.exe)&quot;| C[Classic SeAccessCheck&lt;br /&gt;against user + groups]
    B --&amp;gt;|&quot;Yes (UWP calc.exe)&quot;| D[LowBox access path&lt;br /&gt;add Package SID and&lt;br /&gt;Capability claims to check]
    C --&amp;gt; E[Read allowed]
    D --&amp;gt; F[Read denied:&lt;br /&gt;no fs:documentsLibrary capability]
&lt;p&gt;The question that opens this article is the same one Windows NT 3.1 could not ask in July 1993: &lt;em&gt;two processes, same user, same DACLs, how does the kernel give them different verdicts?&lt;/em&gt; For nineteen years it could not. The answer Microsoft shipped on October 26, 2012 is the &lt;em&gt;LowBox token&lt;/em&gt;, and we will trace one coherent primitive from its Vista-era ancestors through the four production deployments that exercise it on every Windows 11 machine in 2026. We will end with the exploit history that shaped its current hardening. The story starts on January 30, 2007, with a single browser, a single bit, and a structural defect Microsoft spent the next fourteen years repairing.&lt;/p&gt;
&lt;h2&gt;2. The Internet Explorer 7 Protected Mode Era&lt;/h2&gt;
&lt;p&gt;By the time Windows Vista went generally available on January 30, 2007 [@wiki-vista], a large majority of home Windows installs ran day-to-day under administrator accounts. The Wikipedia summary of &lt;a href=&quot;https://paragmali.com/blog/adminless-how-windows-finally-made-elevation-a-security-boun/&quot; rel=&quot;noopener&quot;&gt;User Account Control&lt;/a&gt; captures the pre-Vista status quo plainly: in Windows 1.0 through Windows 9x, &quot;all applications had privileges equivalent to the operating system&quot; [@wiki-uac]. One browser exploit equalled total compromise of the user profile, and Internet Explorer 6 from 2001 was the single largest pathway between hostile content and the desktop.&lt;/p&gt;
&lt;p&gt;Vista&apos;s response had two halves. The first was User Account Control, the split-token model that asks the user to consent before promoting a Medium-integrity process to High. The second was Mandatory Integrity Control (MIC), the labelling primitive UAC depended on. MIC defined four integrity levels with their own SIDs: Low (&lt;code&gt;S-1-16-4096&lt;/code&gt;), Medium (&lt;code&gt;S-1-16-8192&lt;/code&gt;), High (&lt;code&gt;S-1-16-12288&lt;/code&gt;), and System (&lt;code&gt;S-1-16-16384&lt;/code&gt;) [@wiki-mic]. The Security Reference Monitor checks the label before the DACL; a label deny short-circuits the access check.&lt;/p&gt;

A Windows Vista security mechanism that adds an *integrity level* to every process and every securable object [@ms-learn-mic]. Levels are System, High, Medium, and Low; objects carry a `SYSTEM_MANDATORY_LABEL_ACE` in their SACL declaring a minimum integrity level for access. By default the system creates every object with an access mask of `SYSTEM_MANDATORY_LABEL_NO_WRITE_UP`, which prevents lower-IL writers from modifying higher-IL objects but does not block reads.
&lt;p&gt;The four MIC integrity-level SIDs are spaced at 0x1000-unit increments, the consecutive &lt;code&gt;SECURITY_MANDATORY_*_RID&lt;/code&gt; constants in &lt;code&gt;winnt.h&lt;/code&gt;: Low &lt;code&gt;S-1-16-4096&lt;/code&gt; (0x1000), Medium &lt;code&gt;S-1-16-8192&lt;/code&gt; (0x2000), High &lt;code&gt;S-1-16-12288&lt;/code&gt; (0x3000), System &lt;code&gt;S-1-16-16384&lt;/code&gt; (0x4000) [@wiki-mic]. They are in identifier authority 16, the &lt;em&gt;Mandatory Label Authority&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Internet Explorer 7, which had shipped on October 18, 2006 [@wiki-ie7], became MIC&apos;s first marquee consumer. IE 7&apos;s &lt;em&gt;Protected Mode&lt;/em&gt; launched the renderer at Low integrity, where it could not write to &lt;code&gt;%USERPROFILE%&lt;/code&gt; (Medium IL by default) or to &lt;code&gt;HKLM&lt;/code&gt;. A separate Medium-IL broker process named &lt;code&gt;ieuser.exe&lt;/code&gt; accepted COM requests from the renderer and performed privileged work on its behalf -- saving downloads, writing to non-Low locations -- only for operations the policy allowed. The renderer&apos;s private workspace lived at &lt;code&gt;%USERPROFILE%\AppData\LocalLow\&lt;/code&gt; and the corresponding Low-IL temporary internet files [@ie7-protected-mode-mirror]. Marc Silbey and Peter Brundrett documented the design on MSDN. The MSDN article is preserved at a third-party mirror today; the original Microsoft path retired with the IE developer area.&lt;/p&gt;
&lt;p&gt;The structural pattern was complete. A sandboxed worker process and a higher-privilege broker that re-elevates only the operations the policy permits is the topology of every later Windows-side capability sandbox.The same broker / target pattern reappears under several names: &lt;code&gt;RuntimeBroker.exe&lt;/code&gt; for UWP, &lt;code&gt;AppInfo&lt;/code&gt; for UAC consent prompts, &lt;code&gt;PrintWorkflowUserSvc&lt;/code&gt; for print, and the WebAuthn and voice-activation brokers for those WinRT capabilities. Each is structurally &lt;code&gt;ieuser.exe&lt;/code&gt; for a different resource. AppContainer&apos;s &lt;code&gt;RuntimeBroker.exe&lt;/code&gt; is structurally the same idea as &lt;code&gt;ieuser.exe&lt;/code&gt;. AppInfo (the UAC consent broker), the Print broker, the WebAuthn broker, and the voice-activation broker all share the lineage. Chromium&apos;s own broker / target sandbox, which we will meet in §3, was published less than two years later on the same conceptual base.&lt;/p&gt;

flowchart LR
    A[iexplore.exe&lt;br /&gt;Low IL renderer] --&amp;gt;|&quot;COM request&quot;| B[ieuser.exe&lt;br /&gt;Medium IL broker]
    A --&amp;gt;|&quot;file write&quot;| C[&quot;%USERPROFILE%/AppData/LocalLow/&lt;br /&gt;Low-IL workspace&quot;]
    B --&amp;gt;|&quot;write download&quot;| D[&quot;%USERPROFILE%/Downloads/&lt;br /&gt;Medium IL&quot;]
    B -.-&amp;gt;|&quot;policy decides&quot;| A
&lt;p&gt;So far so good. But Protected Mode had three structural defects, and each one mapped one-to-one to a specific Windows 8 fix five years later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Defect one: read-up was not blocked by default.&lt;/strong&gt; The default mandatory-label ACE was &lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_WRITE_UP&lt;/code&gt;. A Low-IL process could not write to Medium-IL objects, but it could read them. The Microsoft Learn page on MIC is explicit: &quot;the system creates every object with an access mask of &lt;strong&gt;SYSTEM_MANDATORY_LABEL_NO_WRITE_UP&lt;/strong&gt;&quot; [@ms-learn-mic]. Unless the object explicitly carried a &lt;code&gt;NO_READ_UP&lt;/code&gt; mandatory ACE -- which &lt;code&gt;%USERPROFILE%\.ssh\id_ed25519&lt;/code&gt; did not -- a compromised Low-IL IE could &lt;em&gt;read&lt;/em&gt; the user&apos;s SSH keys, browser passwords, OAuth refresh tokens, and any other Medium-IL secret. The Chromium sandbox FAQ flags the same observation: Vista&apos;s integrity levels were the only user-mode separation primitive available, and Chromium built a layered user-mode sandbox on top precisely because Vista&apos;s separation was insufficient [@chromium-sandbox-faq].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;code&gt;NO_WRITE_UP&lt;/code&gt; boundary is an &lt;em&gt;integrity&lt;/em&gt; boundary, not a &lt;em&gt;confidentiality&lt;/em&gt; boundary. It stops a low-trust process from corrupting your data, but it does not stop it from reading your data. For a browser renderer that processes hostile content, confidentiality is the load-bearing axis: the attacker&apos;s goal is exfiltration, not corruption. Without &lt;code&gt;NO_READ_UP&lt;/code&gt;, MIC alone could not contain a renderer compromise [@ms-learn-mic].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Defect two: identity was shared across instances.&lt;/strong&gt; Two Low-IL IE renderers ran with the same Low-IL SID and the same user SID. They could open handles to each other&apos;s named objects, shared memory sections, and process tokens. Integrity level is an axis; it is not a per-instance principal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Defect three: network access was unconstrained.&lt;/strong&gt; A Low-IL process could open any socket to any host. The Vista firewall was the freshly-introduced Windows Filtering Platform, but WFP had no notion of &quot;which app is this&quot; at all. As James Forshaw later put it on the Project Zero blog, &quot;Prior to XP SP2 Windows didn&apos;t have a built-in firewall... While XP SP2 introduced the built-in firewall, the basis for the one used in modern versions of Windows was introduced in Vista as the Windows Filtering Platform (WFP)&quot; [@p0-network]. WFP&apos;s per-application-package callout came years later, and the corresponding condition value &lt;code&gt;FWPM_CONDITION_ALE_PACKAGE_ID&lt;/code&gt; did not exist in 2007.&lt;/p&gt;
&lt;p&gt;Each defect became a specific Windows 8 mechanism. Defect one became the per-package namespace redirect plus the LowBox flag&apos;s &lt;code&gt;NO_READ_UP&lt;/code&gt;-by-construction effect on cross-package reads. Defect two became the AppContainerNumber per-instance key. Defect three became the WFP capability gate. Microsoft and Google looked at the same Vista primitives and reached opposite conclusions. Google built a user-mode sandbox on top of NT. Microsoft built a per-app principal &lt;em&gt;inside&lt;/em&gt; the NT token. The next two sections walk both branches.&lt;/p&gt;
&lt;h2&gt;3. Same Idea, Four Directions&lt;/h2&gt;
&lt;p&gt;Between January 2007 and October 2012, four different teams reached the same conclusion from four different starting points: &lt;em&gt;the kernel needs a per-application principal, not just a per-user one&lt;/em&gt;. They built it in four different places.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chromium sandbox, December 2008.&lt;/strong&gt; When Chrome 1.0 shipped on Windows in December 2008, it carried a layered user-mode sandbox: a broker process plus a target process, a restricted token derived from the user&apos;s token, a job object that limited the target&apos;s syscall surface, and (on Vista and later) the Low integrity level on the target. The Chromium sandbox design document spells out the rule that defined the choice. &quot;Do not re-invent the wheel: It is tempting to extend the OS kernel with a better security model. Don&apos;t. Let the operating system apply its security to the objects it controls&quot; [@chromium-sandbox]. The reasoning continues in the FAQ. &quot;the sandbox cannot provide any protection against bugs in system components such as the kernel it is running on&quot; [@chromium-sandbox-faq]. The Chromium design was a &lt;em&gt;user-mode-only&lt;/em&gt; sandbox, layered on top of NT primitives that already existed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google Native Client, 2009.&lt;/strong&gt; Bennet Yee and co-authors at Google published &lt;em&gt;Native Client: A Sandbox for Portable, Untrusted x86 Native Code&lt;/em&gt; at IEEE S&amp;amp;P 2009 [@nacl-ieee]. NaCl declared its capabilities at launch and structurally inverted the question: rather than retrofit a sandbox onto an application, run untrusted code only with capabilities the host has explicitly granted. The Wikipedia retrospective records NaCl&apos;s eventual replacement by WebAssembly [@wiki-nacl], but the design idea -- &lt;em&gt;capability-declared-at-launch&lt;/em&gt; -- prefigured what Microsoft would call &lt;code&gt;SECURITY_CAPABILITIES.Capabilities[]&lt;/code&gt; three years later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apple iOS application sandbox, 2007 onward.&lt;/strong&gt; iPhone OS 1.0 (June 29, 2007) shipped with a kernel-side TrustedBSD-derived MAC sandbox that confined Apple&apos;s own built-in apps [@apple-platform-security-app]. The developer-facing surface -- third-party apps, Bundle IDs, Team IDs, and the entitlements dictionary -- arrived with iPhone OS 2.0 in July 2008 alongside the public App Store [@apple-platform-security-app]. From 2008 onward the per-app principal had two halves: a &lt;em&gt;Bundle ID&lt;/em&gt; (a string the developer picks, structurally like a Windows Package Family Name) and a &lt;em&gt;Team ID&lt;/em&gt; (the signer). Each app&apos;s profile lived at &lt;code&gt;Containers/Data/Application/&amp;lt;bundle-uuid&amp;gt;/&lt;/code&gt;, structurally similar to Windows&apos;s later &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. The capability set was an *entitlements dictionary* signed into the app bundle, structurally similar to Windows&apos;s later &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Android per-app UID, September 23, 2008.&lt;/strong&gt; When Android 1.0 shipped on the T-Mobile G1 on September 23, 2008 [@wiki-android], it carried the most parsimonious solution: each app got its own UNIX UID. Classical Unix discretionary access control did the rest. Where Windows extended the SID alphabet, Android extended the UID allocation policy. The Android Application Sandbox page records the additional MAC layers Android added in later releases -- SELinux from Android 5.0 onward, seccomp-bpf from Android 8.0 onward, and per-physical-user partitions [@android-app-sandbox]. The kernel principal stayed a Unix UID.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Crispin Cowan, Jan 17, 2008.&lt;/strong&gt; Between the Vista release and the eventual Windows 8 ship, the &lt;em&gt;person&lt;/em&gt; who would write Microsoft&apos;s answer arrived on the security team. Crispin Cowan, the StackGuard co-author whose 1998 USENIX paper coined the term &lt;em&gt;stack canary&lt;/em&gt; [@stackguard], joined the Microsoft Windows security team in January 2008. ZDNet&apos;s coverage of the hire reads: &quot;Crispin Cowan, the Linux security expert behind StackGard [sic], the Immunix Linux distro and AppArmor, has joined the Windows security team... Crispin will join the team that worked on User Account Control&quot; [@zdnet-cowan]. Cowan&apos;s later USENIX Security 2013 keynote &lt;em&gt;Windows 8 Security: Supporting User Confidence&lt;/em&gt; credits him with the AppContainer design [@cowan-usenix13], and his University of Waterloo speaker page makes the attribution explicit: &quot;From 2008 to 2017, Dr. Cowan was with Microsoft, where he designed the App Container sandbox that is used by the Edge and Chrome web browsers, Microsoft Office, and Windows 10 to contain Universal Windows Apps&quot; [@crysp-cowan].&lt;/p&gt;
&lt;p&gt;The four mechanisms compare like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Operating system&lt;/th&gt;
&lt;th&gt;Principal name&lt;/th&gt;
&lt;th&gt;How it is named&lt;/th&gt;
&lt;th&gt;How it is enforced&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;Bundle ID + Team ID (from 2008)&lt;/td&gt;
&lt;td&gt;Developer string + signer&lt;/td&gt;
&lt;td&gt;Mandatory per-app MAC + entitlements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;Chromium on Windows&lt;/td&gt;
&lt;td&gt;(none, user UID only)&lt;/td&gt;
&lt;td&gt;User SID + restricted token&lt;/td&gt;
&lt;td&gt;User-mode broker + job object + IL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;Per-app UNIX UID&lt;/td&gt;
&lt;td&gt;UID allocated at install&lt;/td&gt;
&lt;td&gt;Classical Unix DAC + SELinux + seccomp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Windows 8&lt;/td&gt;
&lt;td&gt;Package SID + Capability SIDs&lt;/td&gt;
&lt;td&gt;Deterministic SHA-2 of moniker&lt;/td&gt;
&lt;td&gt;Kernel access token + WFP + Object Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

Each design carries the same *intent* -- give the kernel a per-application principal -- and locates it differently. iOS keeps the per-app *policy data* (entitlements plus `.sb` profiles) in the code-signed bundle and enforces it mandatorily in the kernel via the TrustedBSD-derived `Sandbox.kext`, with no opt-out. Android puts the principal in the UID space the Unix kernel already understood, so existing DAC plumbing did the work. Chromium kept the principal *outside* the kernel entirely, layering a user-mode policy on top of the existing user-keyed access check. Microsoft did the structurally rarest thing: it extended the kernel&apos;s access token data structure. The other three retrofitted the principal on top of an existing access-check pipeline; Windows extended the pipeline itself. The next section walks why Microsoft picked option four.
&lt;p&gt;The name &lt;em&gt;LowBox&lt;/em&gt; is itself a vestige of Microsoft&apos;s internal marketing process. The Win32 surface chose &lt;em&gt;AppContainer&lt;/em&gt; in time for Windows 8&apos;s general availability; the kernel surface had already shipped under the older name. Microsoft Learn confirms the lineage in plain prose [@ms-learn-legacy], and the syscall &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; preserves it.&lt;/p&gt;
&lt;p&gt;Microsoft had a choice: build a user-mode policy daemon like Apple, extend the user-keyed principal like Android, or keep everything in user mode like Chromium. The team chose the fourth option and extended the kernel access token. The next section walks how, year by year, that decision matured.&lt;/p&gt;
&lt;h2&gt;4. The Evolution, Generation by Generation&lt;/h2&gt;
&lt;p&gt;The Windows 8 ship on October 26, 2012 [@wiki-win8] [@win8-rtm] was not the end of the story. It was the start of a six-generation refinement that continues into 2026 Windows 11 25H2. The seven generations look like this on a timeline.&lt;/p&gt;

gantt
    title Per-application sandbox primitives in Windows
    dateFormat YYYY-MM
    axisFormat %Y
    section Gen 0
    Classic NT user-token model :done, g0, 1993-07, 33y
    section Gen 1
    MIC plus IE 7 Protected Mode :done, g1, 2007-01, 19y
    section Gen 2
    Chromium broker target sandbox :done, g2, 2008-12, 18y
    section Gen 3
    AppContainer LowBox token :done, g3, 2012-10, 14y
    section Gen 4
    Less Privileged AppContainer :done, g4, 2016-08, 10y
    section Gen 5
    MSIX TrustLevel appContainer :done, g5, 2018-11, 8y
    section Gen 6
    Windows Sandbox plus Hyper-V :done, g6, 2019-05, 7y
&lt;h3&gt;4.1 Generation 0: The classic NT user-token model (1993)&lt;/h3&gt;
&lt;p&gt;Dave Cutler&apos;s Windows NT 3.1 shipped in July 1993 with a security model that is still the foundation in 2026. Every process has an access token. The token carries &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Groups[]&lt;/code&gt;, &lt;code&gt;Privileges[]&lt;/code&gt;, and a &lt;code&gt;DefaultDacl&lt;/code&gt;. Every securable object has a security descriptor with a DACL. &lt;a href=&quot;https://paragmali.com/blog/windows-access-control-25-years-of-attacks/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;SeAccessCheck&lt;/code&gt;&lt;/a&gt; walks the DACL access control entry by access control entry against the caller&apos;s user and group SIDs. &lt;em&gt;Identity equals authority:&lt;/em&gt; if process A and process B both run as Alice, they are the same principal to the kernel. Whatever Alice can do, both of them can do. Chapter 7 of the seventh-edition &lt;em&gt;Windows Internals&lt;/em&gt; [@ms-press-wi7] gives the canonical post-LowBox reference for this data structure.&lt;/p&gt;
&lt;p&gt;This model has no answer to the single-user, multi-application threat. A user&apos;s text editor and a user&apos;s browser are indistinguishable principals. &lt;em&gt;Bridge: the next generation tried to fix that by adding an axis, not a principal.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.2 Generation 1: IE 7 Protected Mode plus MIC (Vista, 2007)&lt;/h3&gt;
&lt;p&gt;We already walked the mechanism in §2. Mandatory Integrity Control shipped with Vista&apos;s general availability on January 30, 2007 [@wiki-vista] [@wiki-mic]. Internet Explorer 7&apos;s Protected Mode launched its renderer at Low integrity with a Medium-IL &lt;code&gt;ieuser.exe&lt;/code&gt; broker [@wiki-uac] [@ie7-protected-mode-mirror]. The Wikipedia article on MIC names IE 7 as the marquee consumer: &quot;Internet Explorer 7 introduces a MIC-based &apos;Protected Mode&apos; setting&quot; [@wiki-mic]. &lt;em&gt;Bridge: integrity level is an axis, not a principal. Two Low-IL processes share identity.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.3 Generation 2: The Chromium broker / target sandbox (Chrome, 2008)&lt;/h3&gt;
&lt;p&gt;The parallel path Microsoft did not take. Chromium&apos;s design document calls the architecture explicitly: &quot;The Windows sandbox is a user-mode only sandbox&quot; [@chromium-sandbox]. The two-process structure is named: &quot;The minimal sandbox configuration has two processes: one that is a privileged controller known as the &lt;em&gt;broker&lt;/em&gt;, and one or more sandboxed processes known as the &lt;em&gt;target&lt;/em&gt;&quot; [@chromium-sandbox]. The FAQ adds the integrity-level note: the sandbox &quot;uses integrity levels&quot; on Vista and later, and the only Vista-era application the FAQ knows about that used them was Internet Explorer 7 [@chromium-sandbox-faq]. &lt;em&gt;Bridge: who is this app? remains unanswered in any user-mode-only design.&lt;/em&gt;&lt;/p&gt;

Do not re-invent the wheel: It is tempting to extend the OS kernel with a better security model. Don&apos;t. Let the operating system apply its security to the objects it controls. -- Chromium sandbox design document [@chromium-sandbox]
&lt;p&gt;The pull quote is the most interesting sentence in the article so far, because Microsoft -- which &lt;em&gt;was&lt;/em&gt; the operating system vendor -- did exactly the opposite four years later, and shipped AppContainer the same year (2012) Chrome 22 hit the desktop with its user-mode-only sandbox. The two browsers&apos; modern descendants now run &lt;em&gt;both&lt;/em&gt; designs simultaneously, but we are getting ahead of ourselves.&lt;/p&gt;
&lt;h3&gt;4.4 Generation 3: AppContainer / LowBox token (Windows 8, October 26, 2012)&lt;/h3&gt;
&lt;p&gt;This is the keystone. Microsoft made three structural extensions to the NT access-token model.&lt;/p&gt;
&lt;p&gt;First, two new SID alphabets. The &lt;em&gt;App Package Authority&lt;/em&gt; (identifier authority 15) carries Package SIDs at &lt;code&gt;S-1-15-2-*&lt;/code&gt; and Capability SIDs at &lt;code&gt;S-1-15-3-*&lt;/code&gt; [@ms-learn-well-known-sids]. A Package SID is a deterministic SHA-2-derived identifier for a specific package; a Capability SID names a permission the package declared in its manifest. Microsoft&apos;s &lt;code&gt;DeriveAppContainerSidFromAppContainerName&lt;/code&gt; API takes a moniker string and returns the corresponding Package SID [@ms-learn-derivesid], which means every Windows 11 machine derives the same &lt;code&gt;S-1-15-2-*&lt;/code&gt; for &lt;code&gt;Microsoft.WindowsCalculator_8wekyb3d8bbwe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Second, a per-package partition of the Object Manager namespace. Every LowBox process&apos;s references to global named objects under &lt;code&gt;\BaseNamedObjects&lt;/code&gt; are rewritten by the Object Manager to land in &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; and its &lt;code&gt;RPC Control&lt;/code&gt; sub-directory. The kernel captures handles to these directories at token-creation time and uses them at name-lookup time; the rewrite is invisible to the application code. We will name the exact field that holds the handles in §5.&lt;/p&gt;
&lt;p&gt;Third, an always-Low integrity level. Microsoft Learn states the rule plainly: &quot;if you &lt;em&gt;are&lt;/em&gt; in an app container, then the integrity level (IL) is always &lt;em&gt;low&lt;/em&gt;&quot; [@ms-learn-legacy]. The AppContainer flag is orthogonal to integrity level, but AppContainer membership forces the integrity level to Low. Two co-tenanted AppContainer processes can therefore both write to &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; (their per-package directory is owned by the package SID) but neither can write to a Medium-IL object outside.&lt;/p&gt;
&lt;p&gt;The kernel surface is &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; [@ms-learn-ntcreatelowboxtoken]. Its nine-parameter signature -- the existing token, the desired access, an &lt;code&gt;OBJECT_ATTRIBUTES&lt;/code&gt; parameter, the package SID, a capability count and array, a handle count and array -- is the literal data plane for everything we just described. The Win32 surface is &lt;code&gt;CreateAppContainerProfile&lt;/code&gt; [@ms-learn-createprofile], &lt;code&gt;DeriveAppContainerSidFromAppContainerName&lt;/code&gt; [@ms-learn-derivesid], the &lt;code&gt;SECURITY_CAPABILITIES&lt;/code&gt; struct [@ms-learn-security-capabilities], and the &lt;code&gt;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&lt;/code&gt; attribute key that &lt;code&gt;UpdateProcThreadAttribute&lt;/code&gt; accepts [@ms-learn-updateprocthread]. All five APIs share the same minimum supported client: Windows 8 desktop applications [@ms-learn-ntcreatelowboxtoken] [@ms-learn-createprofile] [@ms-learn-security-capabilities].&lt;/p&gt;
&lt;p&gt;The corresponding access-check rule is also explicit. Microsoft Learn formalises the dual-principal intersection: &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-implementing]. We will walk this five-step check field by field in §5.&lt;/p&gt;

sequenceDiagram
    participant App as Launcher (Medium IL)
    participant W32 as Win32 surface
    participant K as Kernel
    App-&amp;gt;&amp;gt;W32: CreateAppContainerProfile(name, caps)
    W32--&amp;gt;&amp;gt;App: package SID, profile root
    App-&amp;gt;&amp;gt;W32: InitializeProcThreadAttributeList
    App-&amp;gt;&amp;gt;W32: UpdateProcThreadAttribute (PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES)
    App-&amp;gt;&amp;gt;W32: CreateProcess(STARTUPINFOEX)
    W32-&amp;gt;&amp;gt;K: NtCreateLowBoxToken(parent, caps, handles)
    K--&amp;gt;&amp;gt;K: set _TOKEN.Flags.LowBoxToken, stamp PackageSid, Capabilities, assign AppContainerNumber
    K--&amp;gt;&amp;gt;W32: new token handle
    W32--&amp;gt;&amp;gt;App: PROCESS_INFORMATION
    Note over K: Every later SeAccessCheck consults the new flag and fields.
&lt;p&gt;Three motivations drive the next three generations. The &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group SID grants every AppContainer ambient access to a broad default-installed surface; tighter deny-by-default needs the LPAC variant. Win32 applications cannot opt in without source changes; MSIX brings them in via manifest. Kernel exploits bypass the boundary entirely; Windows Sandbox wraps the boundary in Hyper-V.&lt;/p&gt;
&lt;p&gt;The implementation correctness motivations are equally specific. James Forshaw&apos;s &lt;em&gt;Raising the Dead&lt;/em&gt; post on Project Zero in January 2016 walked an issue (P0 Issue 483) in the original &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; plumbing that, in his own words, looked &quot;on the surface&quot; like at best a local denial of service but turned out to be an elevation of privilege [@p0-raising-dead]. &quot;The root cause of the vulnerability is the NtCreateLowBoxToken system call introduced in Windows 8 to support the creation of locked down tokens for Immersive Applications (formerly known as Metro) as well as IE11&apos;s Enhanced Protected Mode&quot; [@p0-raising-dead]. Microsoft patched the issue in bulletin MS15-111 on October 13, 2015. NVD records the specific CVE within MS15-111 that mapped to Forshaw&apos;s writeup as CVE-2015-2554, &quot;Windows Object Reference Elevation of Privilege Vulnerability&quot; [@nvd-cve-2015-2554]. Three years later, Forshaw&apos;s &lt;em&gt;Windows Exploitation Tricks: Exploiting Arbitrary Object Directory Creation&lt;/em&gt; post (P0 Issue 1550, August 2018) found a related bug in the AppInfo service&apos;s helper API: &quot;The AppInfo service, which is responsible for creating the new application, calls the undocumented API CreateAppContainerToken to do some internal housekeeping. Unfortunately this API creates object directories under the user&apos;s AppContainerNamedObjects object directory to support redirecting BaseNamedObjects and RPC endpoints by the OS&quot; [@p0-1550]. The directory was created &quot;with an explicit security descriptor which allows the user full access&quot; [@p0-1550], and a user-controlled symbolic link in the target could redirect the directory creation almost anywhere in the namespace.&lt;/p&gt;

Forshaw&apos;s two P0 writeups in 2016 and 2018 are textbook cases of *correct primitive, incorrect implementation*. The LowBox flag, the package SID, and the namespace partition are sound. The bugs were in adjacent code -- the kernel&apos;s reference-counting on handles passed to `NtCreateLowBoxToken` (Issue 483, fixed in MS15-111 as CVE-2015-2554 [@nvd-cve-2015-2554]) and the AppInfo service&apos;s overly permissive directory creation under `\AppContainerNamedObjects` (Issue 1550 [@p0-1550]). The lesson is that being *in the kernel* is not the same as being *correct in the kernel*. Forshaw&apos;s parallel work on the Diagnostics Hub Standard Collector Service -- &quot;we&apos;ll call it DiagHub for short&quot; [@p0-arbitrary-write-2018] -- belongs to the same class: brokers and helpers near the AppContainer boundary have repeatedly been the exploitable seams.
&lt;p&gt;&lt;em&gt;Bridge: ambient access via &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; motivates the next generation.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.5 Generation 4: Less Privileged AppContainer (LPAC) (Windows 10 1607, August 2, 2016)&lt;/h3&gt;
&lt;p&gt;LPAC is one of the most parsimonious changes in the AppContainer chain. Microsoft introduced a synthetic capability with the SID prefix &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt;; when this capability is present in the new token&apos;s &lt;code&gt;Capabilities[]&lt;/code&gt; array, the kernel omits the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group SID from the new token. The Windows 10 1607 Anniversary Update was where the mechanism shipped [@wiki-win10-versions]. Microsoft Learn defines LPAC plainly: &quot;Less Privileged AppContainers (LPAC) are even more isolated than regular AppContainers and require further capabilities to gain access to resources that regular AppContainers already have access to such as the registry, files, and others. For example, LPAC cannot open any keys in the registry unless it has the &lt;em&gt;registryRead&lt;/em&gt; capability and cannot use COM unless it has the &lt;em&gt;lpacCom&lt;/em&gt; capability&quot; [@ms-learn-implementing].&lt;/p&gt;
&lt;p&gt;The Chromium repository carries the most explicit user of this surface today. &lt;code&gt;sandbox/policy/win/lpac_capability.h&lt;/code&gt; enumerates the LPAC capability constants Chromium passes to its renderer / utility / network-service / GPU process tokens: &lt;code&gt;kLpacChromeInstallFiles&lt;/code&gt;, &lt;code&gt;kLpacAppExperience&lt;/code&gt;, &lt;code&gt;kLpacCom&lt;/code&gt;, &lt;code&gt;kLpacCryptoServices&lt;/code&gt;, &lt;code&gt;kLpacEnterprisePolicyChangeNotifications&lt;/code&gt;, &lt;code&gt;kLpacIdentityServices&lt;/code&gt;, &lt;code&gt;kLpacInstrumentation&lt;/code&gt;, &lt;code&gt;kLpacMedia&lt;/code&gt;, &lt;code&gt;kLpacPnPNotifications&lt;/code&gt;, &lt;code&gt;kLpacPnpNotifications&lt;/code&gt;, &lt;code&gt;kLpacServicesManagement&lt;/code&gt;, &lt;code&gt;kLpacSessionManagement&lt;/code&gt;, &lt;code&gt;kRegistryRead&lt;/code&gt;, and several Media Foundation-specific constants [@chromium-lpac]. Microsoft Edge, the Chromium-based rebase Microsoft published on January 15, 2020 [@edge-chromium-ga], inherited all of them. The policy &lt;code&gt;RendererAppContainerEnabled&lt;/code&gt; documents the Edge consumer specifically: &quot;Launches Renderer processes into an App Container for more security benefits... If you enable this policy, Microsoft Edge launches the renderer process in an app container&quot;, available on Windows ≥ 96 [@ms-learn-edge-lpac].&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bridge: LPAC is still a user-mode boundary. A kernel exploit still bypasses it.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.6 Generation 5: MSIX TrustLevel = appContainer (Windows 10 1809, November 13, 2018)&lt;/h3&gt;
&lt;p&gt;The Windows 10 October 2018 Update [@wiki-win10-versions] shipped MSIX with an explicit manifest opt-in for AppContainer. The MSIX schema page documents the two values &lt;code&gt;uap10:TrustLevel&lt;/code&gt; may take: &quot;mediumIL&quot; and &quot;appContainer&quot; [@ms-learn-manifest-app]. There are exactly two trust levels, not three. The widely-stated &quot;third trust level&quot; of &lt;code&gt;runFullTrust&lt;/code&gt; is in fact a &lt;em&gt;restricted capability&lt;/em&gt; that a &lt;code&gt;mediumIL&lt;/code&gt; package may declare under its &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt; element to register out-of-process COM and similar legacy-Win32 mechanisms [@ms-learn-capabilities]. Microsoft Learn&apos;s MSIX container migration page walks the conversion explicitly, showing the manifest going from &lt;code&gt;&amp;lt;rescap:Capability Name=&quot;runFullTrust&quot; /&amp;gt;&lt;/code&gt; to &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; [@ms-learn-msix-container]. The Wikipedia article on App Installer (which MSIX redirects to) records the App Installer tool&apos;s introduction in the Windows 10 1607 Anniversary Update; the MSIX format itself debuted with Windows 10 1809 in 2018 [@wiki-msix].&lt;/p&gt;
&lt;p&gt;For developers, the win is that the MSIX deployment stack does all the LowBox plumbing on the application&apos;s behalf at install. The application binary itself does not change; the manifest changes, and the App Installer pipeline derives the package SID, creates the profile, and registers the per-package directories.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bridge: still a user-mode boundary; many packaged apps stay at &lt;code&gt;mediumIL&lt;/code&gt; plus &lt;code&gt;runFullTrust&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.7 Generation 6: Windows Sandbox = AppContainer + Hyper-V (Windows 10 1903, May 21, 2019)&lt;/h3&gt;
&lt;p&gt;Microsoft announced Windows Sandbox on the Tech Community blog as &quot;a new lightweight desktop environment tailored for safely running applications in isolation&quot; [@windows-sandbox-announce]. It shipped in the Windows 10 May 2019 Update [@win10-1903-ga] [@wiki-win10-versions]. Three architectural primitives carry the design.&lt;/p&gt;
&lt;p&gt;The first is the Dynamic Base Image, the pristine read-only file set the sandbox&apos;s guest kernel boots from. Microsoft Learn describes its character: &quot;Most OS files are immutable and can be freely shared with Windows Sandbox. A small subset of operating system files are mutable and can&apos;t be shared, so the sandbox base image contains pristine copies of them&quot; [@ms-learn-sandbox-arch]. Footprint figures from the same page: &quot;Before Windows Sandbox is installed, the dynamic base image package is stored as a compressed 30-MB package. Once installed, the dynamic base image occupies about 500 MB of disk space&quot; [@ms-learn-sandbox-arch].&lt;/p&gt;
&lt;p&gt;The second is direct-map memory sharing. &quot;when &lt;code&gt;ntdll.dll&lt;/code&gt; is loaded into memory in the sandbox, it uses the same physical pages as those pages of the binary when loaded on the host&quot; [@ms-learn-sandbox-arch]. Read-only host code does not get re-paged into guest memory; the guest&apos;s virtual address space simply maps to the same physical frames.&lt;/p&gt;
&lt;p&gt;The third is the host-side AppContainer wrap. The host-side container manager has been reported in security-research write-ups to itself run in a LowBox token, though Microsoft&apos;s published Windows Sandbox architecture documentation describes only the Hyper-V partition boundary. The actual containment boundary is the &lt;a href=&quot;https://paragmali.com/blog/above-ring-zero-how-the-windows-hypervisor-became-a-security/&quot; rel=&quot;noopener&quot;&gt;Hyper-V partition&lt;/a&gt;: Microsoft Learn&apos;s overview is explicit. The sandbox &quot;relies on the Microsoft hypervisor to run a separate kernel that isolates Windows Sandbox from the host&quot; [@ms-learn-sandbox-overview]. A kernel exploit inside the sandbox kernel does &lt;em&gt;not&lt;/em&gt; reach the host kernel, because the host kernel is on the other side of the Type-1 hypervisor partition.The 30 MB compressed / 500 MB installed Dynamic Base Image figures come from the Microsoft Learn architecture page [@ms-learn-sandbox-arch]; the &quot;few seconds to launch&quot; wall-clock figure and the &quot;data persists through restarts initiated within the sandbox&quot; Windows 11 22H2 persistence behaviour are from the overview page [@ms-learn-sandbox-overview]. The persistence behaviour explicitly excludes Windows Home edition.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bridge: WDAG (the per-tab Edge variant of Hyper-V isolation) was retired in Windows 11 24H2. Per-app Hyper-V isolation is no longer the Microsoft direction. Microsoft Defender Application Guard&apos;s overview page documents the retirement: &quot;Microsoft Defender Application Guard, including the Windows Isolated App Launcher APIs, is deprecated for Microsoft Edge for Business and will no longer be updated&quot;; &quot;Starting with Windows 11, version 24H2, Microsoft Defender Application Guard, including the Windows Isolated App Launcher APIs, is no longer available&quot; [@ms-learn-mdag].&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gen&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Key idea&lt;/th&gt;
&lt;th&gt;What it added&lt;/th&gt;
&lt;th&gt;Failure that motivated the next&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;Identity equals authority&lt;/td&gt;
&lt;td&gt;Classic NT access token&lt;/td&gt;
&lt;td&gt;Cannot distinguish two apps as same user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;Add a mandatory label&lt;/td&gt;
&lt;td&gt;Integrity levels, broker pattern&lt;/td&gt;
&lt;td&gt;NO_READ_UP missing; shared identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;Sandbox in user mode&lt;/td&gt;
&lt;td&gt;Broker / target, restricted token&lt;/td&gt;
&lt;td&gt;No per-app principal in the kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Per-app SID alphabets&lt;/td&gt;
&lt;td&gt;LowBox flag, package and capability SIDs&lt;/td&gt;
&lt;td&gt;ALL_APP_PACKAGES is broad; Win32 cannot opt in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Deny-by-default&lt;/td&gt;
&lt;td&gt;WIN://NOALLAPPPKG strips ALL_APP_PACKAGES&lt;/td&gt;
&lt;td&gt;Still user-mode; not auto-applied to Win32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Manifest opt-in for Win32&lt;/td&gt;
&lt;td&gt;MSIX TrustLevel=&quot;appContainer&quot;&lt;/td&gt;
&lt;td&gt;Still user-mode; kernel exploit bypasses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Hyper-V isolation wrap&lt;/td&gt;
&lt;td&gt;Dynamic Base Image + direct-map&lt;/td&gt;
&lt;td&gt;Per-app HV not the direction (WDAG retired)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;By 2026 a single Edge browser instance runs the host UI at Medium IL (Gen 0), uses MIC labels (Gen 1), runs a broker / target architecture (Gen 2), launches renderers as LowBox tokens (Gen 3) with &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; (Gen 4), is itself an MSIX-packaged app (Gen 5), and can be invoked inside Windows Sandbox (Gen 6). The chain is &lt;em&gt;layered&lt;/em&gt;, not &lt;em&gt;successive&lt;/em&gt;. The next section opens the LowBox token in WinDbg and walks the five token-level fields that produce these behaviours.&lt;/p&gt;
&lt;h2&gt;5. The Five Token-Level Fields&lt;/h2&gt;
&lt;p&gt;If you load WinDbg on a Windows 11 25H2 kernel debugging session and inspect the access token of a UWP &lt;code&gt;calc.exe&lt;/code&gt; process with &lt;code&gt;!token&lt;/code&gt;, then do the same for a plain &lt;code&gt;notepad.exe&lt;/code&gt;, the differences cluster in five fields. Four of them are pointer-valued; one of them is a single bit. Three of them are public in Microsoft&apos;s &lt;code&gt;winnt.h&lt;/code&gt;-adjacent surface. Two are internal field names that Alex Ionescu reverse-engineered in his Black Hat USA 2015 talk &lt;em&gt;Battle of SKM and IUM: How Windows 10 Rewrites OS Architecture&lt;/em&gt; [@ionescu-bh15], and that James Forshaw walks systematically in his 2024 No Starch Press book &lt;em&gt;Windows Security Internals&lt;/em&gt; [@nostarch-wsi].&lt;/p&gt;
&lt;h3&gt;5.1 &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt; -- the bit&lt;/h3&gt;
&lt;p&gt;The bit. The kernel&apos;s &lt;code&gt;_TOKEN.Flags&lt;/code&gt; word carries a &lt;code&gt;LowBoxToken&lt;/code&gt; bit that &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; sets and that no later operation modifies [@nostarch-wsi]. Every kernel access-check path consults this bit when deciding which evaluation rules to apply. If the bit is clear, the token is a classic NT user token and &lt;code&gt;SeAccessCheck&lt;/code&gt; proceeds in the 1993 way. If the bit is set, the kernel adds the package-SID claim check, the capability-claim check, and the namespace redirect that we will walk in §5.2 through §5.5.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The bit in &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt; is what tells the kernel to evaluate the per-app principal alongside the per-user principal. Everything else in the LowBox token is bookkeeping for that decision.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The other four fields are the bookkeeping.&lt;/p&gt;
&lt;h3&gt;5.2 &lt;code&gt;_TOKEN.PackageSid&lt;/code&gt; -- the package principal&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PackageSid&lt;/code&gt; is a &lt;code&gt;PSID&lt;/code&gt; field in the kernel token structure that carries the &lt;code&gt;S-1-15-2-*&lt;/code&gt; SID for the package. The Microsoft surface calls it the AppContainer SID. It is the per-application principal the kernel&apos;s access-check path checks DACL entries against. When you mount a per-package ACL like the one in &lt;code&gt;%LOCALAPPDATA%\Packages\Microsoft.WindowsCalculator_8wekyb3d8bbwe\&lt;/code&gt; and find an access control entry naming &lt;code&gt;S-1-15-2-466767348-3739614953-2700836392-...&lt;/code&gt;, that ACE is allowing or denying exactly the LowBox process whose &lt;code&gt;PackageSid&lt;/code&gt; matches.&lt;/p&gt;

A `PSID` value with the prefix `S-1-15-2-` derived deterministically by SHA-2 hash from the package&apos;s family name and used as the *per-application principal* in DACL evaluations against AppContainer processes [@ms-learn-derivesid]. The same package family name produces the same Package SID on every Windows 8 or later machine, which is why per-package directories on disk can be ACL&apos;d to a known SID at install time. The 32-bit sub-authorities are sequential 4-byte slices of the underlying hash.
&lt;p&gt;The signature of &lt;code&gt;DeriveAppContainerSidFromAppContainerName&lt;/code&gt; confirms the &lt;em&gt;derivation&lt;/em&gt; is intended to be deterministic and reproducible [@ms-learn-derivesid]: &quot;Gets the SID of the specified profile&quot;, with a Windows 8 minimum supported client. The reverse direction -- start from a Package SID and recover the moniker -- is not feasible without the inverse hash, which is by construction infeasible.&lt;/p&gt;
&lt;h3&gt;5.3 &lt;code&gt;_TOKEN.Capabilities[]&lt;/code&gt; -- the per-capability claim array&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Capabilities[]&lt;/code&gt; is a kernel array of &lt;code&gt;SID_AND_ATTRIBUTES&lt;/code&gt; structures, populated by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; from the &lt;code&gt;Capabilities&lt;/code&gt; parameter the caller passes [@ms-learn-ntcreatelowboxtoken]. Each capability SID lives in the &lt;code&gt;S-1-15-3-*&lt;/code&gt; alphabet [@ms-learn-well-known-sids] and represents a specific permission the package declared in its manifest [@ms-learn-capabilities]. Each capability is &lt;em&gt;checked&lt;/em&gt; by the relevant resource manager: the Windows Filtering Platform checks the &lt;code&gt;internetClient&lt;/code&gt; capability SID for outbound network packets, the Object Manager checks namespace capabilities for the per-package directories, the contacts WinRT broker checks the &lt;code&gt;contactsLibrary&lt;/code&gt; capability SID against the contacts ACL.&lt;/p&gt;

A `PSID` value with the prefix `S-1-15-3-` representing a per-package permission [@ms-learn-well-known-sids]. Capability SIDs are stamped into a LowBox token&apos;s `Capabilities[]` array at process creation and consulted by the kernel callout, broker, or kernel access-check path that gates the corresponding resource [@ms-learn-implementing]. The capability vocabulary is open-ended: standard Microsoft-defined capabilities such as `internetClient` coexist with developer-defined custom capabilities and Chromium&apos;s `Lpac*` synthetic constants.
&lt;p&gt;Microsoft Learn&apos;s AppContainer-implementation page formalises the dual-principal access rule for these fields with the same intersection wording quoted in §4.4: the granted access is the intersection of what the user / group SIDs allow and what the AppContainer SIDs allow [@ms-learn-implementing]. The capability-claim check fails closed: a capability the manifest did not declare is not in &lt;code&gt;Capabilities[]&lt;/code&gt;, the kernel callout does not find a matching claim, and the access fails.&lt;/p&gt;
&lt;p&gt;Selected capability SIDs and the resources they gate look like this.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;What it gates&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;internetClient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outbound TCP/UDP via WFP callout&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;internetClientServer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inbound + outbound network&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;picturesLibrary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read access to user&apos;s Pictures library&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;documentsLibrary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricted; access to user Documents&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enterpriseAuthentication&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricted; Kerberos authentication&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;microphone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audio input device&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;webcam&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Video input device&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Geolocation API&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;runFullTrust&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricted; opt-out of AppContainer for mediumIL packages&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip ALL_APP_PACKAGES from token (LPAC)&lt;/td&gt;
&lt;td&gt;[@ms-learn-implementing] [@chromium-lpac]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lpacCom&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LPAC needs this to use COM&lt;/td&gt;
&lt;td&gt;[@ms-learn-implementing]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;registryRead&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LPAC needs this to open registry keys&lt;/td&gt;
&lt;td&gt;[@ms-learn-implementing]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Several rows here have a &lt;em&gt;restricted capability&lt;/em&gt; designation in the manifest schema; Microsoft Learn&apos;s note on &lt;code&gt;runFullTrust&lt;/code&gt; is that &quot;a Medium IL app &lt;em&gt;needs&lt;/em&gt; to declare the &lt;strong&gt;runFullTrust&lt;/strong&gt; restricted capability&quot; before it can register out-of-process COM [@ms-learn-capabilities]. The same page gives the structural advice every developer should treat as default: &quot;be sure to declare only the capabilities that your app needs&quot; [@ms-learn-capabilities].&lt;/p&gt;
&lt;h3&gt;5.4 &lt;code&gt;_TOKEN.LowboxNumberEntry&lt;/code&gt; -- the per-instance number&lt;/h3&gt;
&lt;p&gt;Two simultaneous Microsoft Calculator processes both carry the same Package SID and the same Capabilities array. What makes them distinct principals to the kernel is the &lt;code&gt;AppContainerNumber&lt;/code&gt; -- a per-instance integer assigned by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; at token-creation time. The kernel maintains a session-scoped AVL tree keyed by this number. Ionescu named the corresponding kernel data structure in his 2015 reverse engineering [@ionescu-bh15]; the public-facing reference is Forshaw&apos;s 2024 book [@nostarch-wsi].&lt;/p&gt;

A per-instance integer assigned by `NtCreateLowBoxToken` to each LowBox token at creation time and used as the key into the kernel&apos;s session-scoped AVL trees that hold the per-instance named-object handles and the per-instance namespace state [@nostarch-wsi]. Two simultaneously running processes of the same package have the *same* Package SID but *different* `AppContainerNumber` values. This is the field that makes two co-tenanted calculator processes opaque to each other.
&lt;p&gt;The internal field names &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt;, the AVL tree at &lt;code&gt;nt!SepLowBoxNumberTable&lt;/code&gt;, and the &lt;code&gt;AppContainerNumber&lt;/code&gt; are not in any official Microsoft document. The canonical primary sources for them are Ionescu&apos;s Black Hat 2015 reverse engineering [@ionescu-bh15] and Forshaw&apos;s 2024 book &lt;em&gt;Windows Security Internals&lt;/em&gt; [@nostarch-wsi]. Microsoft&apos;s own &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; page documents the &lt;em&gt;behaviour&lt;/em&gt; but does not name the internal fields [@ms-learn-ntcreatelowboxtoken].&lt;/p&gt;
&lt;h3&gt;5.5 &lt;code&gt;_TOKEN.LowboxHandlesEntry&lt;/code&gt; -- the saved namespace handles&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;LowboxHandlesEntry&lt;/code&gt; points into a &lt;em&gt;second&lt;/em&gt; AVL tree, keyed by the same &lt;code&gt;AppContainerNumber&lt;/code&gt;. The entry carries kernel handles to the per-package directory at &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; and to its &lt;code&gt;RPC Control&lt;/code&gt; sub-directory. The handles are captured by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; (the syscall&apos;s last two parameters are &lt;code&gt;HandleCount&lt;/code&gt; and &lt;code&gt;Handles[]&lt;/code&gt; [@ms-learn-ntcreatelowboxtoken]) and stored in the AVL tree for later lookups.&lt;/p&gt;
&lt;p&gt;When a LowBox process calls &lt;code&gt;NtCreateMutant(&quot;\\Global\\Foo&quot;)&lt;/code&gt;, the Object Manager&apos;s path-walker (&lt;code&gt;ObpLookupObjectName&lt;/code&gt;) consults &lt;code&gt;LowboxHandlesEntry&lt;/code&gt; to substitute the rewrite. The lookup proceeds against the per-package directory rather than the global &lt;code&gt;\BaseNamedObjects&lt;/code&gt;. The application&apos;s request for &quot;Global\Foo&quot; silently resolves to &quot;\Sessions\1\AppContainerNamedObjects\S-1-15-2-...\Foo&quot; -- which is invisible to any other package, because every other package has a different per-instance handle pointing to a different directory.&lt;/p&gt;
&lt;p&gt;This is the mechanism Forshaw&apos;s Project Zero Issue 1550 attacked in 2018. Forshaw&apos;s writeup names the surface: &quot;this API creates object directories under the user&apos;s AppContainerNamedObjects object directory to support redirecting BaseNamedObjects and RPC endpoints by the OS&quot; [@p0-1550]. The implementation bug was in the &lt;em&gt;helper&lt;/em&gt; (the AppInfo service&apos;s &lt;code&gt;CreateAppContainerToken&lt;/code&gt;); the primitive itself is structurally sound.&lt;/p&gt;
&lt;h3&gt;The five-step LowBox access check&lt;/h3&gt;
&lt;p&gt;Putting the five fields together, every access decision from a LowBox process runs through this five-step sequence.&lt;/p&gt;

flowchart TD
    A[Access requested by LowBox process] --&amp;gt; B[&quot;Step 1: Classic DACL walk&lt;br /&gt;SeAccessCheck against user + groups&quot;]
    B --&amp;gt; C[&quot;Step 2: MIC label check&lt;br /&gt;NO_WRITE_UP (default)&lt;br /&gt;against IL=Low&quot;]
    C --&amp;gt; D[&quot;Step 3: Package SID claim&lt;br /&gt;does object&apos;s DACL name PackageSid?&quot;]
    D --&amp;gt; E[&quot;Step 4: Capability claim&lt;br /&gt;WFP / broker / kernel callout&lt;br /&gt;checks Capabilities array&quot;]
    E --&amp;gt; F[&quot;Step 5: Namespace redirect&lt;br /&gt;ObpLookupObjectName rewrites&lt;br /&gt;BaseNamedObjects path&quot;]
    F --&amp;gt; G[Allow]
    F --&amp;gt; H[Deny]
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Classic DACL check.&lt;/strong&gt; &lt;code&gt;SeAccessCheck&lt;/code&gt; walks the object&apos;s DACL exactly as if the caller were a normal user. The user SID and group SIDs in the token contribute to the granted-access mask.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MIC label check.&lt;/strong&gt; The SACL&apos;s mandatory-integrity ACE is compared against the token&apos;s &lt;code&gt;IntegrityLevel&lt;/code&gt;. AppContainer tokens are &lt;em&gt;always&lt;/em&gt; Low [@ms-learn-legacy]; if the object&apos;s mandatory ACE says &lt;code&gt;NO_WRITE_UP&lt;/code&gt; and the request is a write, the access is denied here. The default &lt;code&gt;NO_WRITE_UP&lt;/code&gt;-only policy from Vista [@ms-learn-mic] means a Low-IL LowBox process still cannot write to a default-ACL Medium object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Package SID claim check.&lt;/strong&gt; The object&apos;s DACL may grant access specifically to the Package SID. If matched, the package SID claim contributes to the granted mask. This is the channel by which per-package data directories are made accessible to the package and only the package.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capability claim check.&lt;/strong&gt; A WFP callout, a broker, or a kernel callout checks whether the requested resource maps onto a declared capability. The Windows Filtering Platform compares the outbound socket&apos;s destination against the firewall callout&apos;s allow list; if the package did not declare &lt;code&gt;internetClient&lt;/code&gt;, no &lt;code&gt;S-1-15-3-*&lt;/code&gt; capability matches and the connection fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Named-object namespace redirect.&lt;/strong&gt; References to objects under &lt;code&gt;\BaseNamedObjects&lt;/code&gt; (mutants, events, sections, RPC endpoints) are rewritten by the Object Manager to &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; before lookup. This is what makes &quot;Global\Foo&quot; mean a per-package object for the LowBox process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Microsoft Learn&apos;s intersection rule is the canonical articulation: &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-implementing]. A user / group ACE that grants &lt;code&gt;FILE_READ_DATA&lt;/code&gt; and a package SID ACE that grants &lt;code&gt;FILE_READ_DATA | FILE_WRITE_DATA&lt;/code&gt; together produce &lt;code&gt;FILE_READ_DATA&lt;/code&gt;, because the intersection is the smaller set.&lt;/p&gt;

flowchart LR
    T[&quot;_TOKEN structure&quot;] --&amp;gt; U[&quot;User&lt;br /&gt;S-1-5-21-...&quot;]
    T --&amp;gt; G[&quot;Groups[]&quot;]
    T --&amp;gt; P[&quot;Privileges[]&quot;]
    T --&amp;gt; I[&quot;IntegrityLevel&lt;br /&gt;(always Low for LowBox)&quot;]
    T --&amp;gt; F[&quot;Flags&lt;br /&gt;(LowBoxToken bit)&quot;]
    T --&amp;gt; PK[&quot;PackageSid&lt;br /&gt;S-1-15-2-...&quot;]
    T --&amp;gt; CA[&quot;Capabilities[]&lt;br /&gt;SID_AND_ATTRIBUTES&quot;]
    T --&amp;gt; LN[&quot;LowboxNumberEntry&quot;]
    T --&amp;gt; LH[&quot;LowboxHandlesEntry&quot;]
    LN -.-&amp;gt;|&quot;keyed lookup&quot;| AVL1[&quot;nt!SepLowBoxNumberTable&lt;br /&gt;AVL tree by AppContainerNumber&quot;]
    LH -.-&amp;gt;|&quot;keyed lookup&quot;| AVL2[&quot;nt!SepLowBoxHandlesTable&lt;br /&gt;AVL tree of saved directory handles&quot;]
&lt;h3&gt;The Win32 launch surface&lt;/h3&gt;
&lt;p&gt;The minimum production launch sequence has four moving parts. &lt;code&gt;CreateAppContainerProfile&lt;/code&gt; derives the Package SID, lays down the per-package profile on disk, and returns the SID and the profile root [@ms-learn-createprofile]. &lt;code&gt;InitializeProcThreadAttributeList&lt;/code&gt; allocates a process-attribute list. &lt;code&gt;UpdateProcThreadAttribute&lt;/code&gt; installs the &lt;code&gt;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&lt;/code&gt; attribute, whose payload is a &lt;code&gt;SECURITY_CAPABILITIES&lt;/code&gt; struct carrying the AppContainer SID, the capability array, and a count [@ms-learn-updateprocthread] [@ms-learn-security-capabilities]. &lt;code&gt;CreateProcess&lt;/code&gt; (or &lt;code&gt;CreateProcessAsUser&lt;/code&gt;) with &lt;code&gt;STARTUPINFOEX&lt;/code&gt; carrying the attribute list launches the new process. Inside the kernel, &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; does the work [@ms-learn-ntcreatelowboxtoken]: it sets &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt;, stamps &lt;code&gt;PackageSid&lt;/code&gt;, populates &lt;code&gt;Capabilities[]&lt;/code&gt;, assigns the &lt;code&gt;AppContainerNumber&lt;/code&gt;, and saves the directory handles into &lt;code&gt;LowboxHandlesEntry&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;{`
// This sketches the &lt;em&gt;shape&lt;/em&gt; of the Win32 launch surface, not real P/Invoke.
// Real callers go through Win32 from C/C++/Rust or via interop.&lt;/p&gt;
&lt;p&gt;type PSID = string; // S-1-15-2-... package SIDs
type SECURITY_CAPABILITIES = {
  AppContainerSid: PSID;
  Capabilities: { Sid: PSID; Attributes: number }[];
};&lt;/p&gt;
&lt;p&gt;function createSandboxedProcess(packageName: string, exe: string) {
  // Step 1: derive Package SID, create per-package profile on disk.
  const packageSid = createAppContainerProfile(packageName, {
    displayName: packageName,
    description: &quot;Sandboxed worker&quot;,
    capabilities: [&quot;internetClient&quot;],
  });&lt;/p&gt;
&lt;p&gt;  // Step 2: build the capabilities payload.
  const caps: SECURITY_CAPABILITIES = {
    AppContainerSid: packageSid,
    Capabilities: [
      { Sid: &quot;S-1-15-3-1&quot;, Attributes: 0 }, // internetClient capability SID
    ],
  };&lt;/p&gt;
&lt;p&gt;  // Step 3: install PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES.
  const attrList = initializeProcThreadAttributeList(1);
  updateProcThreadAttribute(
    attrList,
    &quot;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&quot;,
    caps,
  );&lt;/p&gt;
&lt;p&gt;  // Step 4: launch. Kernel calls NtCreateLowBoxToken under the hood.
  return createProcess(exe, { startupInfoEx: { attributeList: attrList } });
}&lt;/p&gt;
&lt;p&gt;// Demonstration only:
const info = createSandboxedProcess(&quot;contoso.WidgetApp_8wekyb3d8bbwe&quot;, &quot;widget.exe&quot;);
console.log(&quot;Launched LowBox PID&quot;, info.pid);
console.log(&quot;Package SID:&quot;, info.packageSid);
console.log(&quot;Capabilities granted:&quot;, info.caps);
`}&lt;/p&gt;
&lt;h3&gt;LPAC: one synthetic capability&lt;/h3&gt;
&lt;p&gt;A LowBox token whose &lt;code&gt;Capabilities[]&lt;/code&gt; includes the synthetic &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; capability causes the kernel to &lt;em&gt;omit&lt;/em&gt; the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group SID from the new token entirely. The token&apos;s groups become strictly the package&apos;s own derivations, plus the declared capabilities, plus the universal SIDs. Microsoft Learn&apos;s wording: &quot;Less Privileged AppContainers (LPAC) are even more isolated than regular AppContainers and require further capabilities to gain access to resources that regular AppContainers already have access to such as the registry, files, and others. For example, LPAC cannot open any keys in the registry unless it has the &lt;em&gt;registryRead&lt;/em&gt; capability and cannot use COM unless it has the &lt;em&gt;lpacCom&lt;/em&gt; capability&quot; [@ms-learn-implementing]. The Chromium source&apos;s &lt;code&gt;lpac_capability.h&lt;/code&gt; enumerates the full set Microsoft Edge inherits at renderer launch [@chromium-lpac]. Section 6.3 walks the practical consequences.&lt;/p&gt;
&lt;p&gt;These five fields and five steps are everything AppContainer is. The next section walks the four places they are exercised at production scale on a 2026 Windows 11 machine: every UWP app, every MSIX-packaged Win32 app, every Edge renderer, and every Windows Sandbox launch.&lt;/p&gt;
&lt;h2&gt;6. The Four 2026 Production Deployments&lt;/h2&gt;
&lt;p&gt;AppContainer is not a demonstration primitive. It is the principal mechanism behind every piece of trustworthy code execution on a modern Windows desktop. Four populations exercise it at production scale on a Windows 11 25H2 machine.&lt;/p&gt;
&lt;h3&gt;6.1 UWP apps&lt;/h3&gt;
&lt;p&gt;The Microsoft Calculator (&lt;code&gt;Microsoft.WindowsCalculator&lt;/code&gt;), the Mail and Calendar apps, Photos, Xbox, Settings, the modern Notepad, and the rest of the Windows-bundled Universal Windows Platform applications are MSIX packages whose manifest declares &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; [@ms-learn-manifest-app]. Capabilities are declared explicitly in each manifest&apos;s &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt; element [@ms-learn-capabilities]. A typical UWP capabilities profile is small: &lt;code&gt;internetClient&lt;/code&gt; for a mail client; &lt;code&gt;picturesLibrary&lt;/code&gt; for Photos; &lt;code&gt;userAccountInformation&lt;/code&gt; for an app that wants the user&apos;s display name. The LowBox token is the only principal that ACLs in the per-package data directory at &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt; name. The six AppContainer isolation axes -- credential, device, file, network, process, and window isolation -- are listed on Microsoft Learn&apos;s AppContainer Isolation page [@ms-learn-isolation], and every UWP app exercises all six. Process isolation is AppContainer-only; UWP apps do not run inside Hyper-V.&lt;/p&gt;
&lt;h3&gt;6.2 MSIX-packaged desktop apps&lt;/h3&gt;
&lt;p&gt;The new File Explorer (in 25H2), the new Outlook, Power Automate Desktop, the modernised Snipping Tool, and a growing catalogue of third-party MSIX packages live in a different population. They are packaged using MSIX, and their manifest declares either &lt;code&gt;uap10:TrustLevel=&quot;mediumIL&quot;&lt;/code&gt; (with &lt;code&gt;&amp;lt;rescap:Capability Name=&quot;runFullTrust&quot; /&amp;gt;&lt;/code&gt;) or &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; [@ms-learn-msix-container] [@ms-learn-manifest-app]. Many start at &lt;code&gt;mediumIL&lt;/code&gt; for legacy interop reasons and gradually migrate as the application drops dependencies on HKLM writes, on absolute file paths, and on registering arbitrary out-of-process COM. Microsoft Learn&apos;s MSIX page shows the migration shape: change the manifest from &lt;code&gt;&amp;lt;rescap:Capability Name=&quot;runFullTrust&quot; /&amp;gt;&lt;/code&gt; to &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt;, and the MSIX deployment stack does the LowBox plumbing on the application&apos;s behalf at install [@ms-learn-msix-container]. The &lt;code&gt;App Installer&lt;/code&gt; component is the surface that performs the install [@wiki-msix].&lt;/p&gt;
&lt;h3&gt;6.3 Microsoft Edge: LPAC renderers&lt;/h3&gt;
&lt;p&gt;Every Microsoft Edge renderer process on Windows 10 RS5 and later runs as a LowBox token with &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; set. The browser is Chromium-based -- &quot;A little over a year ago, we announced our intention to rebuild Microsoft Edge on the Chromium open source project&quot; said the Windows Experience Blog on January 15, 2020 [@edge-chromium-ga] -- and inherits Chromium&apos;s broker / target architecture and job-object lockdown [@chromium-sandbox]. The LPAC layer is the Windows-specific addition. Chromium&apos;s &lt;code&gt;lpac_capability.h&lt;/code&gt; defines the synthetic capabilities the renderer needs to function with &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; stripped: &lt;code&gt;kLpacChromeInstallFiles&lt;/code&gt;, &lt;code&gt;kLpacCom&lt;/code&gt;, &lt;code&gt;kLpacCryptoServices&lt;/code&gt;, &lt;code&gt;kLpacIdentityServices&lt;/code&gt;, &lt;code&gt;kLpacMedia&lt;/code&gt;, &lt;code&gt;kRegistryRead&lt;/code&gt;, and the rest [@chromium-lpac]. Microsoft&apos;s &lt;code&gt;RendererAppContainerEnabled&lt;/code&gt; policy documents the customer-facing surface: &quot;Launches Renderer processes into an App Container for more security benefits&quot;; &quot;This policy will only take effect on Windows 10 RS5 and above&quot;; &quot;Windows: ≥ 96&quot;; &quot;If you enable this policy, Microsoft Edge launches the renderer process in an app container&quot; [@ms-learn-edge-lpac]. The policy is rolled out broadly on current Edge channels; the documentation itself notes the unconfigured default will move to in-app-container in a future update [@ms-learn-edge-lpac].&lt;/p&gt;

A renderer process has no reason to access the registry, no reason to open arbitrary COM classes, no reason to read most of the per-package directories under `%LOCALAPPDATA%\Packages\`. LPAC strips the ambient access by removing `ALL_APP_PACKAGES` from the token, and the renderer adds back only the synthetic capabilities Chromium needs to function (font cache reads, media foundation handles, the install-files capability). The combination -- *Chromium broker / target* outside the kernel plus *LPAC* inside the kernel -- is the most defended browser configuration on any current desktop OS. The Chromium sandbox document says the design intent: &quot;Sandbox operates at process-level granularity. Anything that needs to be sandboxed needs to live on a separate process&quot; [@chromium-sandbox]. LPAC is the Windows-side answer to *how* a separate process gets the smallest possible authority.
&lt;p&gt;This is the second insight to keep. Modern Microsoft Edge runs &lt;em&gt;all six post-Gen-0 mechanisms simultaneously&lt;/em&gt;: Medium IL host UI (Gen 0 baseline), MIC labels on objects (Gen 1), Chromium broker / target architecture (Gen 2), LowBox token on renderers (Gen 3), &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; LPAC (Gen 4), MSIX-packaged Edge (Gen 5), and optionally Windows Sandbox (Gen 6). Microsoft and Google reached the same browser-sandbox design from opposite directions, and the production browser uses &lt;em&gt;both&lt;/em&gt;. The right question is not &quot;AppContainer or Chromium sandbox?&quot; but &quot;where in the stack does each layer belong?&quot;&lt;/p&gt;
&lt;h3&gt;6.4 Windows Sandbox&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;WindowsSandbox.exe&lt;/code&gt; -- available on Pro, Enterprise, and Education editions of Windows 10 1903 and later [@ms-learn-sandbox-overview] -- launches the most isolated of the four populations. Three layers carry the design. The Hyper-V partition isolates a separate kernel from the host: &quot;It relies on the Microsoft hypervisor to run a separate kernel that isolates Windows Sandbox from the host&quot; [@ms-learn-sandbox-overview]. The Dynamic Base Image is the read-only file set the guest kernel boots from, stored as a 30 MB compressed package and unpacked to 500 MB on disk [@ms-learn-sandbox-arch]. Direct-map memory sharing means the host&apos;s read-only binaries (such as &lt;code&gt;ntdll.dll&lt;/code&gt;) &quot;use the same physical pages as those pages of the binary when loaded on the host&quot; [@ms-learn-sandbox-arch]. Security-research write-ups report that the host-side container manager that orchestrates this runs in an AppContainer; Microsoft&apos;s published architecture page does not document the host-side container-manager token type directly, so the load-bearing isolation property remains the Hyper-V partition itself.&lt;/p&gt;
&lt;p&gt;Starting with Windows 11 22H2, in-sandbox restarts persist data: &quot;data persists through restarts initiated within the sandbox&quot; [@ms-learn-sandbox-overview]. The persistence is bounded by the sandbox lifecycle; closing the sandbox window discards the guest entirely.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On a running Windows 11 machine you can confirm the AppContainer status of any process two ways. Sysinternals Process Explorer&apos;s Security tab on a UWP process shows the package SID, the &lt;code&gt;AppContainer&lt;/code&gt; group, and the integrity level Low (&lt;code&gt;S-1-16-4096&lt;/code&gt;). James Forshaw&apos;s &lt;code&gt;NtObjectManager&lt;/code&gt; PowerShell module exposes &lt;code&gt;Get-NtToken&lt;/code&gt; and &lt;code&gt;Get-AccessibleObject -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; which enumerate the kernel-visible token fields and the objects the process can reach [@sandboxsecuritytools]. Microsoft&apos;s own &lt;code&gt;SandboxSecurityTools&lt;/code&gt; repository on GitHub publishes &lt;code&gt;LaunchAppContainer&lt;/code&gt;: &quot;The LaunchAppContainer tool can be used to run applications in AppContainer or Less Privileged AppContainer (LPAC) sandboxes&quot; [@sandboxsecuritytools].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A comparison table for the four populations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Year of ship&lt;/th&gt;
&lt;th&gt;Trust boundary&lt;/th&gt;
&lt;th&gt;Capabilities typical&lt;/th&gt;
&lt;th&gt;Kernel-exploit defence&lt;/th&gt;
&lt;th&gt;Hyper-V wrap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;UWP apps&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;AppContainer&lt;/td&gt;
&lt;td&gt;1-5, declared in manifest&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MSIX-packaged desktop apps&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;AppContainer or mediumIL&lt;/td&gt;
&lt;td&gt;Varies; many declare runFullTrust&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Edge LPAC renderers&lt;/td&gt;
&lt;td&gt;2020 (Edge Chromium GA)&lt;/td&gt;
&lt;td&gt;LPAC inside Chromium target&lt;/td&gt;
&lt;td&gt;Synthetic Lpac* set&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Optional via Windows Sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows Sandbox&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Hyper-V partition + AppContainer&lt;/td&gt;
&lt;td&gt;Implicit&lt;/td&gt;
&lt;td&gt;Yes (separate kernel)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Microsoft Defender Application Guard, the per-tab Hyper-V isolation for Edge that shipped in Windows 10 1709, was retired in Windows 11 24H2 [@ms-learn-mdag]. The replacement is Microsoft Defender SmartScreen plus the Edge Management Service; the per-tab Hyper-V boundary is gone. Per-application hypervisor isolation is no longer the Microsoft direction; Windows Sandbox is the surviving Hyper-V-wrapped variant.&lt;/p&gt;
&lt;p&gt;On the same machine, the same kernel, the same &lt;code&gt;SeAccessCheck&lt;/code&gt; runs the same five-step LowBox path millions of times a second across these four populations. Yet the LowBox token is not what every OS does. The next section asks: how do macOS, iOS, Android, and Linux solve the same problem -- and what do the differences teach us about &lt;em&gt;kernel-token-side&lt;/em&gt; enforcement versus &lt;em&gt;user-mode-policy-side&lt;/em&gt; enforcement?&lt;/p&gt;
&lt;h2&gt;7. Competing Approaches Across Operating Systems&lt;/h2&gt;
&lt;p&gt;Apple, Google, and the Linux distributions solved the same problem -- &lt;em&gt;how does the kernel give two co-tenanted applications different verdicts?&lt;/em&gt; -- with four structurally different answers. Each trade-off is a different bet on where in the system the per-app principal should live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iOS and iPadOS application sandbox.&lt;/strong&gt; Apple ships a mandatory per-app sandbox on every device since iPhone OS 1 in 2007 [@apple-platform-security-app]. From iPhone OS 2.0 / the 2008 App Store launch onward, the developer-facing principal is two-valued: a Bundle ID (developer-chosen string) and a Team ID (signer identity). Per-app entitlements declared in the signed bundle are the capability vocabulary. Enforcement is &lt;em&gt;mandatory&lt;/em&gt; -- there is no opt-out. The per-app filesystem container at &lt;code&gt;Containers/Data/Application/&amp;lt;bundle-uuid&amp;gt;/&lt;/code&gt; is the iOS analogue of &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. Where Windows extends the kernel token, iOS keeps the per-app profile in a TrustedBSD-derived MAC framework and consults it from kernel mode at every relevant resource manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS App Sandbox.&lt;/strong&gt; macOS uses the same entitlement primitive Apple developed for iOS, plus a user-mode policy daemon (&lt;code&gt;sandboxd&lt;/code&gt;) that holds the per-app profile [@apple-app-sandbox]. The trade-off is that more decisions are made in user mode than on iOS, with corresponding flexibility costs. App Sandbox is &lt;em&gt;optional&lt;/em&gt; on macOS for non-App-Store distribution, in contrast to iOS, where it is mandatory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Android per-app UID plus SELinux plus seccomp-bpf.&lt;/strong&gt; Android&apos;s &lt;em&gt;Application Sandbox&lt;/em&gt; page on the AOSP documents the layered model: every app receives a distinct UNIX UID; classical UNIX discretionary access control enforces per-app isolation; SELinux mandatory access control is layered on top from Android 5.0 onward; per-physical-user partitions arrived in Android 6.0; the seccomp-bpf system-call filter is required from Android 8.0; per-app SELinux sandboxes apply for &lt;code&gt;targetSdkVersion &amp;gt;= 28&lt;/code&gt; (Android 9); and path-based file restrictions tightened in Android 10 [@android-app-sandbox]. The kernel-side per-app principal is the UNIX UID. The MAC layers above add additional confinement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux unprivileged namespaces plus seccomp plus AppArmor / SELinux plus Flatpak / Snap.&lt;/strong&gt; Linux&apos;s per-application sandbox is &lt;em&gt;composable&lt;/em&gt;. No first-class per-app principal lives in the kernel. The application sandboxes are layered above the user-keyed DAC: Flatpak and Snap build on unprivileged user namespaces and seccomp filters; Chromium on Linux uses the same kit. The Chromium FAQ&apos;s kernel-bugs caveat from §3 applies in every cross-OS configuration: a user-mode sandbox cannot defend against bugs in the kernel it runs on [@chromium-sandbox-faq].&lt;/p&gt;
&lt;p&gt;The structural axis falls out cleanly. macOS keeps the per-app profile in user-mode &lt;em&gt;outside&lt;/em&gt; the kernel token; iOS keeps it in user-mode but enforces &lt;em&gt;mandatorily&lt;/em&gt;; Android allocates a real UNIX UID; Windows extends the kernel-token SID alphabet. &lt;strong&gt;Windows is the only one of the four that partitions the kernel data structure.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operating system&lt;/th&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Where the principal lives&lt;/th&gt;
&lt;th&gt;Per-call cost model&lt;/th&gt;
&lt;th&gt;Kernel-exploit containment&lt;/th&gt;
&lt;th&gt;Mandatory?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Windows 8+&lt;/td&gt;
&lt;td&gt;LowBox token (AppContainer)&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Inside the access token (kernel)&lt;/td&gt;
&lt;td&gt;Inline in &lt;code&gt;SeAccessCheck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Opt-in via manifest / SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;App Sandbox + entitlements&lt;/td&gt;
&lt;td&gt;2011 (Mac App Store)&lt;/td&gt;
&lt;td&gt;User-mode &lt;code&gt;sandboxd&lt;/code&gt; profile&lt;/td&gt;
&lt;td&gt;IPC for some decisions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Opt-in (Mac App Store: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS / iPadOS&lt;/td&gt;
&lt;td&gt;App Sandbox + entitlements&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;TrustedBSD MAC framework&lt;/td&gt;
&lt;td&gt;Inline kernel check&lt;/td&gt;
&lt;td&gt;No (Pegasus class proves)&lt;/td&gt;
&lt;td&gt;Yes, mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;UID + SELinux + seccomp&lt;/td&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;UID space (kernel) + MAC&lt;/td&gt;
&lt;td&gt;Four checks per syscall&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;Namespaces + seccomp + Flatpak / Snap&lt;/td&gt;
&lt;td&gt;2014 (Flatpak)&lt;/td&gt;
&lt;td&gt;Composable user-mode + kernel&lt;/td&gt;
&lt;td&gt;Variable per layer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Opt-in per app&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The sandbox is not a security silver bullet, but it is a strong last defense against nasty exploits. -- Chromium sandbox FAQ [@chromium-sandbox-faq]
&lt;p&gt;The Chromium project itself ports its broker / target sandbox across all four families. On Windows it sits &lt;em&gt;on top of&lt;/em&gt; AppContainer and LPAC. On macOS it uses seatbelt. On Linux it uses seccomp filters plus unprivileged user namespaces. On ChromeOS the same code talks to ChromeOS&apos;s own kernel-side enforcement. The broker / target pattern is operating-system-independent; what differs is the per-OS capability vocabulary the broker negotiates with [@chromium-sandbox].&lt;/p&gt;
&lt;p&gt;Putting the principal in the kernel access token is fast and elegant. The amortised cost of the LowBox path is one extra DACL pass plus the capability-claim check, all running inline in &lt;code&gt;SeAccessCheck&lt;/code&gt; with no IPC round-trip. But the kernel-side bet has a brittle edge: it is brittle exactly where putting it in the kernel is not enough. The next section asks what the LowBox token &lt;em&gt;cannot&lt;/em&gt; prove.&lt;/p&gt;
&lt;h2&gt;8. Theoretical Limits: What the LowBox Token Cannot Prove&lt;/h2&gt;
&lt;p&gt;Every security primitive has a perimeter beyond which it can no longer reason. The LowBox token has five.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 1: Kernel exploits.&lt;/strong&gt; Once the attacker is in kernel mode, they rewrite their own token. The LowBox bit becomes irrelevant. The Chromium sandbox FAQ states the principle plainly: &quot;the sandbox cannot provide any protection against bugs in system components such as the kernel it is running on&quot; [@chromium-sandbox-faq]. This is the load-bearing argument for Windows Sandbox&apos;s Hyper-V wrap (§4.7, §6.4). It is the third insight to keep.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The LowBox token is a runtime sandbox, not a privilege ceiling. To the rest of user mode a LowBox process is opaque and bounded; to the kernel it is a small extension of the access-check pipeline. A kernel-mode arbitrary write -- of which Windows has had a steady but small supply across its history -- gives the attacker the ability to set or clear &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt;, swap out &lt;code&gt;PackageSid&lt;/code&gt;, append entries to &lt;code&gt;Capabilities[]&lt;/code&gt;, or simply impersonate a different token. The structural answer is hypervisor isolation, not better in-token checks. Windows Sandbox is the production implementation [@ms-learn-sandbox-overview].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Limit 2: Confused-deputy brokers.&lt;/strong&gt; A broker process outside the AppContainer is, by design, the bridge back to user-scoped resources. If the broker honours requests it should not have honoured, the AppContainer&apos;s caller succeeds in doing something it could not have done directly. The canonical class on Windows is RuntimeBroker, AppInfo (the UAC consent broker), Print, WebAuthn, voice activation, and the &lt;em&gt;Microsoft (R) Diagnostics Hub Standard Collector Service&lt;/em&gt; that Forshaw called &quot;DiagHub for short&quot; in his 2018 arbitrary-file-write writeup [@p0-arbitrary-write-2018]. The 1988 &lt;em&gt;Confused Deputy&lt;/em&gt; paper from the capability-systems literature [@hardy-confused-deputy] is the literature anchor for this whole class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 3: Token-stealing.&lt;/strong&gt; An attacker who can duplicate a higher-privilege token via &lt;code&gt;NtDuplicateToken&lt;/code&gt;, &lt;code&gt;NtOpenProcessTokenEx&lt;/code&gt;, or any of the SeImpersonatePrivilege-stealing Potato-family attacks steps out of the AppContainer by impersonating a non-LowBox user. &lt;em&gt;Siloscape&lt;/em&gt; is the most-publicised Windows container escape: Daniel Prizmant&apos;s Unit 42 writeup characterises it as &quot;the first known malware targeting Windows containers&quot;, and records Microsoft&apos;s initial position that &quot;Windows Server containers are not a security boundary&quot; before later reclassifying that &quot;an escape from a Windows container to the host, when executed without administrator permissions inside the container, will in fact be considered a vulnerability&quot; [@siloscape]. Token-stealing is the same class one rung down: an AppContainer escape via impersonation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 4: Capability granularity.&lt;/strong&gt; &lt;code&gt;internetClient&lt;/code&gt; is &quot;the application can speak to the public internet&quot; with no per-host granularity. Each Windows capability is approximately as fine-grained as a Linux &lt;code&gt;CAP_*&lt;/code&gt;, not as fine-grained as an iOS entitlement key. There is no AppContainer-native equivalent of &quot;this app can connect to api.example.com:443 but nowhere else.&quot; The Microsoft Learn capabilities page enumerates the capability vocabulary [@ms-learn-capabilities]; the cardinality is in the dozens of standard capabilities plus custom ones, not in the millions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 5: In-process side channels.&lt;/strong&gt; Once data is in the AppContainer process&apos;s address space, the AppContainer model has nothing to say about it. Hardware side channels (Spectre, MDS, Downfall), explicit channels (clipboard, WebRTC, browser-tab side channels in Edge), and out-of-band exfiltration via broker calls are all out of model. AppContainer is a &lt;em&gt;boundary&lt;/em&gt; primitive; once you are inside the boundary, the primitive does not constrain in-process behaviour.&lt;/p&gt;

The Windows Sandbox architecture page is explicit about the chain of reasoning [@ms-learn-sandbox-arch]. The host&apos;s read-only OS files can be shared with the sandbox without compromise (immutable; &quot;Most OS files are immutable and can be freely shared&quot;). The host&apos;s writable state cannot be shared (mutable; &quot;A small subset of operating system files are mutable and can&apos;t be shared&quot;). The remaining isolation question -- can a kernel exploit inside the guest reach the host? -- is answered structurally: the guest runs on a separate Microsoft hypervisor partition with a separate kernel. A guest-side kernel exploit gives the attacker control of the guest&apos;s `_TOKEN.Flags.LowBoxToken` field, but the host&apos;s kernel is untouched on the other side of the partition.
&lt;p&gt;These five limits define the research surface post-2024. The next section catalogues what is still open.&lt;/p&gt;
&lt;h2&gt;9. Open Problems&lt;/h2&gt;
&lt;p&gt;Six open problem classes are actively investigated in 2024-2026 research.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; ambient-access surface.&lt;/strong&gt; Even with LPAC, many default-installed resources are ACL&apos;d to the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group; non-LPAC AppContainers inherit access to all of them. The research class is to enumerate the union of all &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt;-readable surfaces on a default Windows install and classify which leak cross-package data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Capability-SID collisions and over-grant.&lt;/strong&gt; Third-party custom capability SIDs are derived from a string in the manifest; the collision resistance of the hash construction under adversarial choice of input string is an open question. A malicious manifest that produces a capability SID colliding with a privileged Microsoft-defined capability would be a structural break.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LPAC capability-injection at process creation.&lt;/strong&gt; The capability list is supplied by the &lt;em&gt;creator&lt;/em&gt; of the AppContainer process; tightening which caller can ask for which capability has been a recurring servicing-bulletin theme. Forshaw&apos;s &lt;em&gt;Raising the Dead&lt;/em&gt; writeup (P0 Issue 483, fixed in MS15-111 as CVE-2015-2554 [@p0-raising-dead] [@nvd-cve-2015-2554]) and the 2018 &lt;em&gt;Arbitrary Object Directory Creation&lt;/em&gt; writeup (P0 Issue 1550 [@p0-1550]) are the canonical examples of bugs in this class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hyper-V-per-AppContainer (&quot;HVCI for AppContainers&quot;).&lt;/strong&gt; WDAG&apos;s 2024 retirement [@ms-learn-mdag] leaves a gap: there is no production direction for per-application hypervisor isolation. Windows Sandbox is the surviving Hyper-V-wrapped sandbox; per-tab or per-app HV isolation in Edge is not coming back. The research question is whether per-app Hyper-V can be reintroduced in a form that does not have WDAG&apos;s adoption problems.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-package AppContainer-to-AppContainer attacks.&lt;/strong&gt; As third-party MSIX adoption grows, the cross-package attack surface (one third-party package abusing another&apos;s &lt;code&gt;\AppContainerNamedObjects\&amp;lt;other-package-sid&amp;gt;\&lt;/code&gt; ACL) grows. The 2018 P0 1550 writeup [@p0-1550] is the canonical example of a kernel-side variant; user-mode variants in third-party MSIX brokers are the next class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-CrowdStrike user-mode EDR runtime.&lt;/strong&gt; After the July 2024 incident, Microsoft convened a multi-vendor endpoint-security summit and committed to a more isolated EDR / AV runtime in user mode. AppContainer or LPAC is one of the candidate runtimes for the new platform; architectural details are still being published.For Problem 1 specifically, the standard tool for enumerating the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt;-readable surface is Forshaw&apos;s &lt;code&gt;NtObjectManager&lt;/code&gt; PowerShell module&apos;s &lt;code&gt;Get-AccessibleObject -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; cmdlet -- it enumerates every kernel object a given process can reach, which is the right primitive for asking &quot;what does an &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; member actually see?&quot; The companion Microsoft tool is &lt;code&gt;LaunchAppContainer&lt;/code&gt; from the official &lt;code&gt;SandboxSecurityTools&lt;/code&gt; repository on GitHub [@sandboxsecuritytools].&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft&apos;s decision to retire WDAG in Windows 11 24H2 [@ms-learn-mdag] is the proximate signal that per-application hypervisor isolation is &lt;em&gt;not&lt;/em&gt; the Microsoft direction. The 2024+ answer is &quot;harden the in-token AppContainer surface&quot; and &quot;reduce the broker attack surface&quot; rather than &quot;wrap every app in Hyper-V.&quot; The single shipping Hyper-V-wrapped sandbox is Windows Sandbox, and it is a user-launched primitive, not an OS-default for browsers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The unresolved problems share a structural property: they sit &lt;em&gt;between&lt;/em&gt; the token-level guarantee AppContainer makes and the resource managers that implement the consequences. The next section gives practical guidance for developers and practitioners working within these limits today.&lt;/p&gt;
&lt;h2&gt;10. Practical Guide&lt;/h2&gt;
&lt;p&gt;Two audiences need different answers. Developers need to know how to opt their code in. Practitioners need to know how to audit the deployment.&lt;/p&gt;
&lt;h3&gt;For developers&lt;/h3&gt;
&lt;p&gt;Package your Win32 app with MSIX and set &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; in the manifest&apos;s &lt;code&gt;&amp;lt;Application&amp;gt;&lt;/code&gt; element [@ms-learn-manifest-app]. Stop adding &lt;code&gt;runFullTrust&lt;/code&gt; as a restricted capability by default; the manifest is a &lt;em&gt;budget&lt;/em&gt;, and every restricted capability is a lifetime ACL on your package SID [@ms-learn-capabilities] [@ms-learn-msix-container].&lt;/p&gt;
&lt;p&gt;Declare &lt;em&gt;only&lt;/em&gt; the capabilities your app needs in &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt;. Microsoft Learn&apos;s wording is exactly this: &quot;be sure to declare only the capabilities that your app needs&quot; [@ms-learn-capabilities]. Each extra &lt;code&gt;internetClient&lt;/code&gt; or &lt;code&gt;picturesLibrary&lt;/code&gt; is a lifetime allow ACE on the corresponding Capability SID.&lt;/p&gt;
&lt;p&gt;Use &lt;strong&gt;LPAC&lt;/strong&gt; for any process that does not need cross-package or cross-user resource access. The mechanism is the synthetic &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; capability in &lt;code&gt;SECURITY_CAPABILITIES.Capabilities[]&lt;/code&gt; at process creation [@ms-learn-implementing] [@chromium-lpac]. The Chromium tree&apos;s &lt;code&gt;lpac_capability.h&lt;/code&gt; is the canonical reference for which LPAC capabilities each kind of worker process needs [@chromium-lpac].&lt;/p&gt;
&lt;p&gt;Test your sandbox with Forshaw&apos;s &lt;code&gt;NtObjectManager&lt;/code&gt; PowerShell module&apos;s &lt;code&gt;Get-AccessibleObject -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; cmdlet, which enumerates every kernel object your process can actually reach [@sandboxsecuritytools]. Tighten capabilities until the enumerated list matches your expectations. Microsoft&apos;s own &lt;code&gt;SandboxSecurityTools&lt;/code&gt; repository ships &lt;code&gt;LaunchAppContainer&lt;/code&gt; for the same kind of testing in the Insider bounty programme [@sandboxsecuritytools].&lt;/p&gt;
&lt;h3&gt;For practitioners&lt;/h3&gt;
&lt;p&gt;Identify which high-risk applications on your managed fleet can be MSIX-packaged and migrated to &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt;. Browsers, PDF viewers, and email clients are highest payoff because hostile content reaches them most often.&lt;/p&gt;
&lt;p&gt;Audit the per-package directory permissions in &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. The &lt;code&gt;LocalState&lt;/code&gt;, &lt;code&gt;RoamingState&lt;/code&gt;, and &lt;code&gt;AC&lt;/code&gt; subdirectories are the load-bearing entries; each should grant access to the package SID, not to broad groups.&lt;/p&gt;
&lt;p&gt;Use Windows Sandbox (&lt;code&gt;WindowsSandbox.exe&lt;/code&gt;) for Hyper-V-isolated execution of untrusted programs on Pro, Enterprise, or Education editions [@ms-learn-sandbox-overview]. Configurations are described in a &lt;code&gt;.wsb&lt;/code&gt; file: a 30 MB Dynamic Base Image launches in a few seconds, and Windows 11 22H2 onward preserves data through in-sandbox restarts.&lt;/p&gt;
&lt;p&gt;Monitor the &lt;code&gt;Microsoft-Windows-AppxPackaging/Operational&lt;/code&gt; and &lt;code&gt;Microsoft-Windows-AppXDeploymentServer/Operational&lt;/code&gt; event logs for unexpected &lt;code&gt;CreateAppContainerToken&lt;/code&gt; failures and capability mismatches. Microsoft Learn&apos;s AppX troubleshooting page names these two channels verbatim and walks the diagnostic surface they expose [@ms-learn-appx-troubleshooting].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Five commands a defender can run today to inventory AppContainer adoption on a managed endpoint: 1. &lt;code&gt;Get-AppxPackage -AllUsers | Select-Object Name, PackageFullName, PackageFamilyName&lt;/code&gt; -- enumerate installed MSIX packages. 2. &lt;code&gt;(Get-AppxPackage -Name Microsoft.MicrosoftEdge.Stable).Manifest&lt;/code&gt; -- inspect a specific package&apos;s manifest. 3. &lt;code&gt;Get-Process | Where-Object &amp;amp;#123; $_.Path -like &quot;*WindowsApps*&quot; &amp;amp;#125; | Select-Object Id, ProcessName, Path&lt;/code&gt; -- find running UWP / packaged processes. 4. From an elevated PowerShell with &lt;code&gt;NtObjectManager&lt;/code&gt; installed: &lt;code&gt;Get-NtToken -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; to see the &lt;code&gt;LowBoxToken&lt;/code&gt; flag, Package SID, and capability list. 5. &lt;code&gt;Get-AppxLog | Where-Object &amp;amp;#123; $_.LevelDisplayName -eq &quot;Error&quot; &amp;amp;#125;&lt;/code&gt; -- review AppX deployment errors for clues to broken AppContainer launches.&lt;/p&gt;
&lt;/blockquote&gt;

Once `NtObjectManager` is installed (from PowerShell Gallery), a defender can dump the *complete* reachable kernel-object set for a running LowBox process:&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Install-Module NtObjectManager -Scope CurrentUser
$pid = (Get-Process -Name calc).Id
Get-AccessibleObject -ProcessId $pid -TypeFilter Mutant,Section,Event,Directory |
  Sort-Object Path | Format-Table -Property TypeName, Path, GrantedAccess
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output enumerates every named kernel object the LowBox token can open, with the access mask the kernel would grant on each request. Any path that resolves outside &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; is a candidate for cross-package leakage or an over-permissive capability. This is the same surface §9 problem 1 catalogues [@sandboxsecuritytools].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

Converting a `mediumIL` packaged Win32 app to `appContainer` is structurally easier than it sounds and operationally harder than it sounds. Structurally, the manifest change is one line: drop `` and set `uap10:TrustLevel=&quot;appContainer&quot;` [@ms-learn-msix-container]. Operationally, you must remove every HKLM write the app does, every assumption that absolute file paths outside the per-package directory will succeed, and every dependency on registering out-of-process COM. Legacy desktop apps tend to violate all three in plumbing that nobody documented. This is why `runFullTrust` is so widespread in third-party MSIX packages despite Microsoft&apos;s clear guidance to stop adding it.
&lt;p&gt;The practical guidance only matters if the deployment is correctly understood. The next section addresses the most common misconceptions.&lt;/p&gt;
&lt;h2&gt;11. Frequently Asked Questions&lt;/h2&gt;

No. AppContainer and integrity level are orthogonal axes. AppContainer is a per-application principal in the access token; integrity level is a mandatory label. Microsoft Learn states the relationship plainly: &quot;if you _are_ in an app container, then the integrity level (IL) is always _low_&quot; -- but the converse is *not* true. Plenty of Low-IL tokens are not AppContainer tokens (the IE 7 Protected Mode renderer was the historical example; the LocalLow workspace and the Low-IL Chromium target are modern examples) [@ms-learn-legacy].

Because the LowBox process&apos;s Object Manager namespace is partitioned at `\Sessions\\AppContainerNamedObjects\\`, and the WinRT API the UWP app uses for `Windows.Storage.TempFolder` resolves a per-package temporary directory under `%LOCALAPPDATA%\Packages\\AC\Temp\` instead of the global `\BaseNamedObjects`-anchored `C:\Windows\Temp`. The kernel handles for the rewrite live in the `LowboxHandlesEntry` AVL-tree slot keyed by your process&apos;s `AppContainerNumber`.

*LowBox* is the kernel-internal name preserved in `NtCreateLowBoxToken` and the `_TOKEN.Flags.LowBoxToken` bit [@ms-learn-legacy] [@ms-learn-ntcreatelowboxtoken]. *AppContainer* is the public Win32 / WinRT API name for the same kernel object. *LPAC* (*Less Privileged AppContainer*) is the variant where the synthetic `WIN://NOALLAPPPKG` capability strips the implicit `ALL_APP_PACKAGES` group SID from the new token, requiring the package to declare each access (`registryRead`, `lpacCom`, and others) explicitly [@ms-learn-implementing].

Because the Package SID is a deterministic hash of the Package Family Name (which is itself a hash of the publisher distinguished name plus the package moniker). Each of the eight 32-bit sub-authorities is a 4-byte slice of the underlying SHA-256 hash (8 × 4 bytes = 32 bytes = 256 bits). The point of the construction is that every Windows 8 or later machine derives the *same* Package SID for the same Package Family Name, so per-package directory DACLs can be ACL&apos;d at install time to a known SID [@ms-learn-derivesid] [@nostarch-wsi].

No. The kernel&apos;s `AppContainerNumber` per-instance counter keys each instance into its own slot in `nt!SepLowBoxHandlesTable`, and the Object Manager rewrites `\BaseNamedObjects` references against the per-instance saved-handle directory before name lookup [@nostarch-wsi]. Two Calculator processes share a Package SID and a Capabilities array but have *different* AppContainerNumbers; the named objects each one creates land in different directories.

Yes -- unless the AppContainer has `WIN://NOALLAPPPKG`, in which case it is LPAC and `ALL_APP_PACKAGES` is omitted from its token [@ms-learn-implementing] [@chromium-lpac]. The `ALL_APP_PACKAGES` ambient-access surface is the §9 problem-1 research class. The standard tool for enumerating what an `ALL_APP_PACKAGES` member can reach on a given system is the `Get-AccessibleObject` cmdlet in Forshaw&apos;s `NtObjectManager` PowerShell module, and Microsoft publishes its own `LaunchAppContainer` testing tool in `SandboxSecurityTools` [@sandboxsecuritytools].

No. Windows Sandbox combines a *separate* Hyper-V partition running a pristine Windows kernel from the Dynamic Base Image [@ms-learn-sandbox-arch] with -- according to security-research reverse-engineering write-ups -- an AppContainer wrap on the host-side container manager. The Hyper-V layer is the only documented structural defence against kernel exploits inside the sandbox; pure AppContainer cannot defend against a kernel exploit. Inside the sandbox you still see the LowBox plumbing (it is a Windows guest, after all), but the meaningful trust boundary is the hypervisor partition.
&lt;h2&gt;12. Where This Plane Connects to the Others&lt;/h2&gt;
&lt;p&gt;AppContainer is the &lt;em&gt;principal layer&lt;/em&gt; of the modern Windows sandbox stack. It is not the whole stack. A correct modern Windows app launch crosses four single-purpose layers in the kernel and in the deployment pipeline, and each layer carries a separate guarantee.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Code identity.&lt;/strong&gt; Who is this code? &lt;a href=&quot;https://paragmali.com/blog/authenticode-and-catalog-files-the-crypto-foundation-under-w/&quot; rel=&quot;noopener&quot;&gt;Authenticode&lt;/a&gt; signatures, kernel-mode code signing, the MSIX signing pipeline, and the Package Family Name derivation answer that question [@ms-learn-derivesid].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AppContainer / LowBox token.&lt;/strong&gt; What principal is the running instance? &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; sets the LowBox flag, stamps the Package SID, and populates &lt;code&gt;Capabilities[]&lt;/code&gt; [@ms-learn-ntcreatelowboxtoken].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Object Manager namespace partition.&lt;/strong&gt; What names can the principal resolve? &lt;code&gt;LowboxHandlesEntry&lt;/code&gt; directs the path-walker to the per-package &lt;code&gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; directory [@nostarch-wsi].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/process-mitigation-policies-cfg-acg-cig-and-the-layer-betwee/&quot; rel=&quot;noopener&quot;&gt;Process mitigation policies&lt;/a&gt;.&lt;/strong&gt; What is the principal allowed to do inside its own address space? Arbitrary Code Guard, Code Integrity Guard, Control Flow Guard, Extended Flow Guard, Hardware-enforced Stack Protection (CET), and ImageLoadPolicy are the in-process mitigations Windows applies on top of the per-app principal, exposed via the &lt;code&gt;PROCESS_MITIGATION_POLICY&lt;/code&gt; enumeration that &lt;code&gt;SetProcessMitigationPolicy&lt;/code&gt; accepts [@ms-learn-process-mitigation-policy].&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Walk a modern Windows app boot. Authenticode and the MSIX signing pipeline verify the binaries. The MSIX deployment stack derives the Package SID from the Package Family Name and writes the per-package profile under &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. The launcher calls &lt;code&gt;CreateProcess&lt;/code&gt; with a &lt;code&gt;SECURITY_CAPABILITIES&lt;/code&gt; payload, which calls &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; in the kernel. &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; sets the LowBox flag and the per-instance fields. From that point every later &lt;code&gt;SeAccessCheck&lt;/code&gt; against any object the process opens consults the new token&apos;s per-app fields alongside the classic user / groups walk. The Object Manager namespace gets partitioned in the kernel&apos;s hash table by the &lt;code&gt;AppContainerNumber&lt;/code&gt;. The in-process mitigation policies decide what code may execute and which DLLs may load.&lt;/p&gt;

flowchart LR
    A[Authenticode / KMCS&lt;br /&gt;code identity] --&amp;gt; B[NtCreateLowBoxToken&lt;br /&gt;per-app principal]
    B --&amp;gt; C[Object Manager&lt;br /&gt;namespace partition]
    C --&amp;gt; D[Mitigation policies&lt;br /&gt;ACG / CIG / CFG / XFG / CET]
    D --&amp;gt; E[Running LowBox process]
&lt;p&gt;Each layer is single-purpose. Together they cover the four-axis system the classic NT token model could not.&lt;/p&gt;

The LowBox bit is the answer to the question Windows NT 3.1 couldn&apos;t ask. Everything else is bookkeeping.
&lt;p&gt;Two calculators on the same Windows 11 machine give the kernel a single question with two verdicts. The bit in &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt; is the one-line answer Windows took fourteen years to ship -- and the work Microsoft did in the years between January 30, 2007 and October 26, 2012 is what made it possible to ask the question at all.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;appcontainer-and-lowbox-tokens-windowss-capability-sandbox&quot; keyTerms={[
  { term: &quot;LowBox token&quot;, definition: &quot;An access token whose _TOKEN.Flags.LowBoxToken bit is set, marking the process as an AppContainer process whose access checks consult the package SID, capability SIDs, and per-instance namespace fields.&quot; },
  { term: &quot;Package SID&quot;, definition: &quot;A deterministic SHA-2-derived SID with prefix S-1-15-2-* that names a specific MSIX/UWP package.&quot; },
  { term: &quot;Capability SID&quot;, definition: &quot;A SID with prefix S-1-15-3-* representing a per-package permission declared in the application manifest.&quot; },
  { term: &quot;AppContainerNumber&quot;, definition: &quot;A per-instance integer assigned by NtCreateLowBoxToken that keys per-instance state in the kernel&apos;s session-scoped AVL trees.&quot; },
  { term: &quot;LPAC&quot;, definition: &quot;Less Privileged AppContainer -- a LowBox token whose WIN://NOALLAPPPKG capability strips the ALL_APP_PACKAGES group SID for stricter deny-by-default.&quot; },
  { term: &quot;Mandatory Integrity Control&quot;, definition: &quot;Vista-era mechanism adding an integrity level (Low/Medium/High/System) to every process and securable object; AppContainer tokens are always Low IL.&quot; },
  { term: &quot;Dynamic Base Image&quot;, definition: &quot;Windows Sandbox&apos;s read-only file set, stored as a 30 MB compressed package and unpacked to 500 MB, that a fresh Hyper-V partition boots from.&quot; }
]} questions={[
  { q: &quot;Which kernel data-structure field distinguishes a UWP calc.exe from a legacy win32calc.exe even when every classic NT token field is identical?&quot;, a: &quot;_TOKEN.Flags.LowBoxToken, the bit set by NtCreateLowBoxToken and consulted by every AppContainer-aware access-check path.&quot; },
  { q: &quot;Why are two co-tenanted instances of the same package different principals to the kernel?&quot;, a: &quot;Because NtCreateLowBoxToken assigns a fresh AppContainerNumber per instance and the Object Manager keys the per-instance namespace handles on that number.&quot; },
  { q: &quot;Name the synthetic capability that turns an AppContainer into an LPAC.&quot;, a: &quot;WIN://NOALLAPPPKG -- its presence in Capabilities[] causes NtCreateLowBoxToken to omit the ALL_APP_PACKAGES group SID from the new token.&quot; },
  { q: &quot;What is the only structural defence against kernel exploits inside an AppContainer?&quot;, a: &quot;Hypervisor isolation. Windows Sandbox wraps an AppContainer-managed guest in a separate Hyper-V partition with its own kernel.&quot; },
  { q: &quot;Cite the Microsoft Learn intersection rule for AppContainer access checks.&quot;, a: &quot;&apos;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&apos; -- the dual-principal rule that runs on every LowBox access decision.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>appcontainer</category><category>lowbox-token</category><category>sandbox</category><category>capability-security</category><category>msix</category><category>uwp</category><category>windows-sandbox</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>The Object Manager Namespace: The Hierarchical Filesystem Underneath Every Windows Security Boundary</title><link>https://paragmali.com/blog/the-object-manager-namespace/</link><guid isPermaLink="true">https://paragmali.com/blog/the-object-manager-namespace/</guid><description>A bottom-up tour of the Windows Object Manager namespace, the 1993 Cutler-era kernel data structure that every Windows security boundary quietly assumes.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>
**The Windows Object Manager namespace is the kernel-resident, filesystem-shaped tree that every Windows security boundary quietly assumes.** Every named kernel object -- processes, threads, sections, files, registry keys, tokens, mutants, semaphores, ALPC ports, devices, drivers, jobs, silos -- lives somewhere under `\`. Six generations of isolation primitives (Session 0 isolation, AppContainer lowbox, integrity levels, VBS trustlets, Server Silos, and the `ObRegisterCallbacks` EDR sensor surface) are all path rewrites, per-directory ACLs, or kernel callbacks layered on the same 1993 Cutler-era four-piece structure. This article builds the namespace bottom-up -- `OBJECT_HEADER`, `OBJECT_TYPE`, `ParseProcedure`, `OBJECT_DIRECTORY` -- walks the 2026 top-level directory atlas on Windows 11 25H2, surveys the exploit tradition (symbolic-link redirection, namespace squatting, bait-and-switch on `\??` and `\Device`, arbitrary directory creation), and closes on the EDR pivot in `ObRegisterCallbacks`.
&lt;h2&gt;1. The path that isn&apos;t a path&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;WinObj.exe&lt;/code&gt; as administrator on any Windows 11 25H2 machine (&lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_11_version_history&quot; rel=&quot;noopener&quot;&gt;Windows 11 version history&lt;/a&gt;). For about ten seconds the screen looks like a filesystem. The root is named &lt;code&gt;\&lt;/code&gt;. Below it sit folders called &lt;code&gt;\Device&lt;/code&gt;, &lt;code&gt;\BaseNamedObjects&lt;/code&gt;, &lt;code&gt;\Sessions&lt;/code&gt;, &lt;code&gt;\RPC Control&lt;/code&gt;, &lt;code&gt;\KnownDlls&lt;/code&gt;, and &lt;code&gt;\ObjectTypes&lt;/code&gt;. Double-click any of them and you see children. Right-click any node and you can read a security descriptor. This is essentially the same UI a 1996 SysAdmin would have recognised; the tool first shipped that year as part of Mark Russinovich and Bryce Cogswell&apos;s Winternals [@en-wikipedia-mark-russinovich], and the current build is a Microsoft-signed Sysinternals binary whose navigation surface has not been redesigned in three decades [@ms-winobj].&lt;/p&gt;
&lt;p&gt;Navigate to &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; and the picture starts to fracture. Inside that directory you will find one subdirectory per running AppContainer-sandboxed app, each named after a long Security Identifier of the form &lt;code&gt;S-1-15-2-...&lt;/code&gt;. Pick the one belonging to the Microsoft Edge renderer process you are reading this article in. Every named mutant, event, section, semaphore, and ALPC port the renderer can ever name lives inside that one subdirectory. The renderer cannot escape it. Not because of a permission check that comes second, but because the kernel rewrites every name the renderer asks for, transparently, before path resolution begins. Microsoft&apos;s AppContainer Isolation documentation [@ms-appcontainer-isolation] calls this &quot;sandboxing the application kernel objects.&quot;&lt;/p&gt;
&lt;p&gt;This tree is not a filesystem. There is no disk persistence; nothing under &lt;code&gt;\&lt;/code&gt; survives a reboot. It is not the Windows registry either; the registry is a separate subsystem with its own hive format that hangs off the namespace only through a parse procedure on the &lt;code&gt;Key&lt;/code&gt; object type. What this tree is, instead, is the Object Manager namespace: the in-memory, kernel-resident, hierarchical name service that the Windows kernel uses to locate every nameable kernel object [@ms-managing-kernel-objects]. Its top-level directories are catalogued in the driver kit&apos;s Object Directories reference [@ms-object-directories].&lt;/p&gt;

The Windows Object Manager, internally called `Ob`, is a kernel-mode subsystem of the Windows Executive that manages the lifetime, naming, security, and accounting of every resource the kernel exposes to user mode as a named object. Wikipedia summarises it as a &quot;subsystem implemented as part of the Windows Executive which manages Windows resources... each [resource] reside[s] in a namespace for categorization&quot; [@en-wikipedia-object-manager].
&lt;p&gt;Here is the thesis the rest of this article spends nine thousand words unpacking. Every Windows security boundary you have read about -- Session 0 isolation, Mandatory Integrity Control, AppContainer, the Virtualization-Based Security trustlets, Server Silos and Windows containers, the EDR sensor surface that fires when something opens a handle to &lt;code&gt;lsass.exe&lt;/code&gt; -- is &lt;em&gt;physically realised&lt;/em&gt; in this tree. Each boundary is either a path rewrite at lookup time, a per-directory ACL, a token-keyed name substitution, or a kernel callback registered against an &lt;code&gt;OBJECT_TYPE&lt;/code&gt;. The boundaries you read about elsewhere are the &lt;em&gt;policies&lt;/em&gt;; this tree is the &lt;em&gt;mechanism&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The Object Manager has shipped without architectural change for thirty-three years. Whose decision was that? And why did a 1993 data structure survive untouched while the GUI, the driver model, the security subsystem, and the boot path around it were rewritten more than once?&lt;/p&gt;
&lt;h2&gt;2. Where the namespace came from&lt;/h2&gt;
&lt;p&gt;The decision belongs to Dave Cutler. In 1988 Microsoft hired Cutler away from Digital Equipment Corporation. The Wikipedia biography records the line of operating systems Cutler had developed at DEC: &quot;RSX-11M, VAXELN, VMS, and MICA&quot; [@en-wikipedia-dave-cutler]. Three of those shipped commercially; the fourth, MICA, was cancelled with the Prism RISC program. Cutler walked out, and Microsoft signed him with a charter from Bill Gates to build a portable next-generation kernel that could host the existing Windows API on top of a 32-bit, multi-architecture base [@en-wikipedia-architecture-of-windows-nt]. Cutler brought a small team of DEC veterans with him.&lt;/p&gt;
&lt;p&gt;The Object Manager is one of that team&apos;s earliest design decisions. The architectural bet was to &lt;em&gt;unify every named kernel object&lt;/em&gt; under one filesystem-shaped tree, with each type carrying a parse procedure so a single family of syscalls (&lt;code&gt;NtCreateFile&lt;/code&gt;, &lt;code&gt;NtOpenSection&lt;/code&gt;, &lt;code&gt;NtOpenProcess&lt;/code&gt;, and so on) could address files, registry keys, processes, ports, sections, drivers, devices, jobs, and synchronization primitives using the same path-walk algorithm. That was an unusual choice in 1989. VMS had a more typed, less unified resource broker. Mach treated kernel objects as capability-style port rights and never gave them a hierarchical name. Cutler&apos;s choice was, at heart, a Plan-9-style &quot;every named resource is a filesystem path&quot; idea, imported into a Windows shell.Plan 9 from Bell Labs (Pike, Thompson, et al.) was the academic articulation of the &quot;everything is a path&quot; property: every kernel-named resource, including processes and network connections, surfaced as a file under a 9P-served namespace. Plan 9 never reached commercial scale, but its design idea reached production through NT, and through Linux&apos;s /proc, /sys, and FUSE.&lt;/p&gt;
&lt;p&gt;Windows NT 3.1 shipped on July 27, 1993. It was &quot;Microsoft&apos;s first 32-bit operating system,&quot; supported on IA-32, DEC Alpha, and MIPS [@en-wikipedia-windows-nt-3-1]. The Object Manager was already one of its executive subsystems, sitting alongside the I/O Manager, the Memory Manager, the Process Manager, the Security Reference Monitor, and the Local Procedure Call subsystem [@en-wikipedia-architecture-of-windows-nt]. The four pieces this article will rebuild from scratch -- the &lt;code&gt;OBJECT_HEADER&lt;/code&gt; that prefixes every object in memory, the &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singleton that owns each type&apos;s method table, the &lt;code&gt;ParseProcedure&lt;/code&gt; that delegates path resolution to the owning subsystem, and the &lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt; hash table that maps names to objects -- were all in the NT 3.1 kernel. None of them has been rearchitected since.&lt;/p&gt;
&lt;p&gt;That same year, Microsoft Press published &lt;em&gt;Inside Windows NT&lt;/em&gt;, written by technical writer Helen Custer with a Foreword by Cutler himself. The book&apos;s Object Manager chapter is the canonical pre-2000 description of the namespace, cited on the Sysinternals WinObj page [@ms-winobj] as &quot;Helen Custer&apos;s &lt;em&gt;Inside Windows NT&lt;/em&gt; provides a good overview of the Object Manager namespace.&quot; Custer&apos;s book has been out of print for two decades, but the citation chain through Russinovich&apos;s tool is durable.&lt;/p&gt;
&lt;p&gt;Three years later, in 1996, Russinovich and Cogswell co-founded Winternals and released WinObj 1.0 [@en-wikipedia-mark-russinovich]. WinObj was the first publicly distributed tool to walk &lt;code&gt;\&lt;/code&gt; from user mode, using the native &lt;code&gt;NtOpenDirectoryObject&lt;/code&gt; and &lt;code&gt;NtQueryDirectoryObject&lt;/code&gt; syscalls that the Object Manager exposed through NTDLL [@ms-winobj]. The following year, Russinovich&apos;s October 1997 &lt;em&gt;Windows IT Pro&lt;/em&gt; column &quot;Inside the Object Manager&quot; gave the namespace its first treatment in the trade press. The original URL did not survive changes to TechTarget&apos;s web property portfolio in 2025 (TechTarget was acquired by Informa PLC in 2025), but the WinObj page still cites the column by name as &quot;Mark&apos;s October 1997 [WindowsITPro Magazine] column, &apos;Inside the Object Manager&apos;.&quot;The Russinovich 1997 column has no surviving direct URL because the URL did not survive changes to TechTarget&apos;s web property portfolio in 2025. The most accessible surviving citation is through the WinObj page itself. The same archive failure also explains why Helen Custer&apos;s 1993 biography returns HTTP 404 on Wikipedia in 2026; the book (ISBN 1-55615-481-X) survives in used-book channels only.&lt;/p&gt;
&lt;p&gt;The line of book-length internals references that began with Custer continued through &lt;em&gt;Inside Windows 2000&lt;/em&gt; (third edition) and the &lt;em&gt;Windows Internals&lt;/em&gt; series that succeeded it. The 7th edition Part 1 was published by Microsoft Press in May 2017, authored by Russinovich, Alex Ionescu, and David A. Solomon [@microsoftpressstore-wininternals7-part1]; its Chapter 8 is the current canonical reference for the Object Manager. James Forshaw&apos;s April 2024 &lt;em&gt;Windows Security Internals&lt;/em&gt; [@nostarch-windows-security-internals] is the contemporary companion that ties the namespace into the access-check pipeline.&lt;/p&gt;
&lt;p&gt;The 1993 design assumed a single global namespace. One process tree, one &lt;code&gt;\BaseNamedObjects&lt;/code&gt;, one &lt;code&gt;\Windows\WindowStations\WinSta0&lt;/code&gt;, one &lt;code&gt;\??&lt;/code&gt; view of DOS device letters. Everyone shared everything. Did that assumption survive the Internet?&lt;/p&gt;
&lt;h2&gt;3. The pre-Vista namespace and how it broke&lt;/h2&gt;
&lt;p&gt;It did not. By the late 1990s every interactive Windows user was sharing a name service with every running service. The single-global-namespace assumption produced three distinct exploit classes, each rediscovered repeatedly between 1996 and 2007, and each ultimately closed only by architectural change.&lt;/p&gt;
&lt;p&gt;The most public failure was the &lt;em&gt;shatter attack&lt;/em&gt;. In August 2002 a researcher named Chris Paget published a paper titled &quot;Exploiting design flaws in the Win32 API for privilege escalation.&quot; Wikipedia&apos;s article on the disclosure preserves the chronology: &quot;Shatter attacks became a topic of intense conversation in the security community in August 2002 after the publication of Chris Paget&apos;s paper&quot; [@en-wikipedia-shatter-attack]. The proof-of-concept was about thirty lines. As an unprivileged interactive user, Paget sent a &lt;code&gt;WM_TIMER&lt;/code&gt; window message to a service&apos;s hidden window in the same &lt;code&gt;\Windows\WindowStations\WinSta0&lt;/code&gt; (which all services and all interactive users shared in pre-Vista Windows), with a callback parameter pointing to attacker-placed shellcode. The shellcode ran as SYSTEM.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s initial response, preserved in the Wikipedia article, was that &quot;the flaw lies in the specific, highly privileged service&quot;: a per-service bug, patch the services. That stance did not survive the structural-class argument. The exploit was not a bug in one service. It was a &lt;em&gt;property of the namespace&lt;/em&gt;: as long as services and users shared a window station and a &lt;code&gt;\BaseNamedObjects&lt;/code&gt;, any service that ever called a Windows API processing a message from its message queue was reachable from any logged-in user.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A second class of pre-Vista failure was &lt;em&gt;named-object squatting&lt;/em&gt;. A low-privilege user pre-creates &lt;code&gt;\BaseNamedObjects\Some_Global_Event&lt;/code&gt; with a permissive DACL. A privileged service later calls &lt;code&gt;CreateEvent(&quot;Some_Global_Event&quot;)&lt;/code&gt; with default open-or-create semantics and ends up inheriting the squatter&apos;s object, security descriptor and all. This is not one service-author&apos;s bug; it is the consequence of every service-author trusting that names in a shared namespace would resolve to objects they themselves created. The pattern has been rediscovered approximately once a year for two decades. James Forshaw documents the contemporary named-pipe analog in his 2017 &quot;Named Pipe Secure Prefixes&quot; post [@tiraniddo-named-pipe-secure-prefixes], where the SMSS-created prefixes &lt;code&gt;\Device\NamedPipe\ProtectedPrefix\Administrators&lt;/code&gt;, &lt;code&gt;\Device\NamedPipe\ProtectedPrefix\LocalService&lt;/code&gt;, and &lt;code&gt;\Device\NamedPipe\ProtectedPrefix\NetworkService&lt;/code&gt; are TCB-privilege-gated -- only &lt;code&gt;smss.exe&lt;/code&gt; can create sibling protected prefixes, so a service that publishes its pipe below one of these prefixes inherits a DACL that low-privilege squatters cannot reach.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The third class was &lt;em&gt;symbolic-link redirection&lt;/em&gt;. The pre-Vista Object Manager exposed two kinds of user-creatable symbolic link: object-manager symbolic links inside &lt;code&gt;\??&lt;/code&gt; (the per-session DOS-devices view) and NTFS mount points on disk. The attack pattern was the same in both. A privileged process is asked to open a path the user controls part of. The user has pre-planted a symbolic link partway through the path that redirects the residual walk into a target the user could not otherwise write. The privileged process opens the redirected file and treats it as if it were the original.&lt;/p&gt;
&lt;p&gt;Forshaw&apos;s 2015 Project Zero post on the symbolic-link hardening generation is the canonical taxonomy: &quot;There are three types of symbolic links you can access from a low privileged user, Object Manager Symbolic Links, Registry Key Symbolic Links and NTFS Mount Points&quot; [@p0-symlink-mitigations]. His worked example for the Internet Explorer 11 EPM sandbox is CVE-2015-0055 [@nvd-cve-2015-0055], described in the post as &quot;an information disclosure issue in the IE EPM sandbox which abused symbolic links to bypass a security check.&quot;&lt;/p&gt;
&lt;p&gt;The aha moment from this section is the one Microsoft eventually conceded. The pre-Vista failure mode was not three independent bug families. It was &lt;em&gt;one&lt;/em&gt; structural problem -- a single global namespace shared by every principal -- with three faces. No amount of per-service patching could close it. The fix had to be architectural: the namespace itself had to be partitioned.The Interactive Services Detection Service (ISDS) was Vista&apos;s backward-compatibility hack for legacy services that drew GUIs into Session 0. ISDS displayed a &quot;An interactive service has requested attention&quot; prompt that let the user switch to Session 0 long enough to dismiss the dialog. It was deprecated in Windows 10 1803 and is the historical artifact of just how much pre-Vista code assumed services and users would share a window station.&lt;/p&gt;
&lt;p&gt;That fix took five years to ship. Windows Vista RTM was released on November 8, 2006 and General Availability arrived on January 30, 2007 [@en-wikipedia-windows-vista]. Vista did not ship one fix; it shipped three independent partition mechanisms in the same release window, because the structural failure had three faces and each face needed its own mechanism. The next section catalogues those mechanisms and the four additional generations of additive isolation that have built on them since.&lt;/p&gt;
&lt;h2&gt;4. Six generations of namespace isolation&lt;/h2&gt;
&lt;p&gt;The namespace itself has not been rearchitected since 1993. What has evolved, in six discrete generations between 1993 and 2026, is the set of &lt;em&gt;partition primitives&lt;/em&gt; layered on top: the mechanisms that let the kernel hide subtrees from particular callers, rewrite paths transparently for particular tokens, or invoke a registered watcher when a particular handle is created. Each generation closes a structural class. None has rendered its predecessor obsolete. On 2026 Windows 11 25H2 all six are simultaneously load-bearing.&lt;/p&gt;

flowchart LR
    G1[&quot;Gen 1&lt;br /&gt;NT 3.1, Jul 1993&lt;br /&gt;Single global namespace&quot;] --&amp;gt; G2
    G2[&quot;Gen 2&lt;br /&gt;Vista, Jan 2007 / SP1, Feb 2008&lt;br /&gt;Session 0 + MIC + ObRegisterCallbacks&quot;] --&amp;gt; G3
    G3[&quot;Gen 3&lt;br /&gt;Windows 8, Oct 2012&lt;br /&gt;AppContainer / Lowbox / per-package directory&quot;] --&amp;gt; G4
    G4[&quot;Gen 4&lt;br /&gt;Windows 10 RTM, Jul 2015&lt;br /&gt;VBS / IUM secure-kernel namespace&quot;] --&amp;gt; G5
    G5[&quot;Gen 5&lt;br /&gt;Windows Server 2016, Oct 2016&lt;br /&gt;Server Silos / silo-scoped views&quot;] --&amp;gt; G6
    G6[&quot;Gen 6&lt;br /&gt;MS15-090, Aug 2015 -&amp;gt;&lt;br /&gt;symbolic-link class hardening&quot;]
&lt;p&gt;Generation numbering is thematic (by isolation capability introduced) rather than strictly chronological. Gen 6 (MS15-090, August 11, 2015) predates Gen 5 (Windows Server 2016, October 12, 2016) by 14 months; the numbering reflects the logical layering of isolation mechanisms, not their calendar sequence.&lt;/p&gt;
&lt;h3&gt;4.1 Generation 2 -- Session 0 isolation, integrity levels, ObRegisterCallbacks&lt;/h3&gt;
&lt;p&gt;Vista shipped three mechanisms in one release window because the structural failure had three faces.&lt;/p&gt;
&lt;p&gt;The first was &lt;em&gt;Session 0 isolation&lt;/em&gt;. From Vista forward, services run in Session 0 alone; the first interactive logon starts at Session 1. Each session gets its own subtree at &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\BaseNamedObjects&lt;/code&gt;, &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\Windows\WindowStations&lt;/code&gt;, and &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\DosDevices&lt;/code&gt;. The Win32 &lt;code&gt;Local\&lt;/code&gt; prefix routes through &lt;code&gt;kernel32!BaseGetNamedObjectDirectory&lt;/code&gt; into the per-session BNO; &lt;code&gt;Global\&lt;/code&gt; routes into the shared &lt;code&gt;\BaseNamedObjects&lt;/code&gt; [@ms-termserv-kernel-object-namespaces]. The Wikipedia Shatter article preserves the architectural fix verbatim: &quot;Local user logins were moved from Session 0 to Session 1, thus separating the user&apos;s processes from system services that could be vulnerable&quot; [@en-wikipedia-shatter-attack]. After Vista an interactive user could no longer &lt;code&gt;SendMessage(WM_TIMER)&lt;/code&gt; into a service&apos;s hidden window because the user and the service no longer shared a window station.&lt;/p&gt;
&lt;p&gt;The second mechanism was &lt;em&gt;Mandatory Integrity Control&lt;/em&gt;. Vista introduced a new ACE type, &lt;code&gt;SYSTEM_MANDATORY_LABEL_ACE&lt;/code&gt;, attached to every object&apos;s security descriptor. Each token carries one of four integrity levels (Low S-1-16-4096, Medium S-1-16-8192, High S-1-16-12288, or System S-1-16-16384), and the Security Reference Monitor compares the requester&apos;s level against the object&apos;s level &lt;em&gt;after&lt;/em&gt; path resolution succeeds [@en-wikipedia-mandatory-integrity-control]. MIC is not a namespace partition. A Low-IL process and a Medium-IL process resolve the same &lt;code&gt;\BaseNamedObjects&lt;/code&gt; directory; only the open is denied at the leaf. The structural property MIC adds is that the leaf check is &lt;em&gt;unbypassable from user mode&lt;/em&gt;; the check fires regardless of which DACL the object carries.&lt;/p&gt;
&lt;p&gt;The third mechanism was &lt;code&gt;ObRegisterCallbacks&lt;/code&gt;. Microsoft&apos;s wdm.h documentation records the API&apos;s first ship date verbatim: &quot;Available starting with Windows Vista with Service Pack 1 (SP1) and Windows Server 2008&quot; [@ms-obregistercallbacks]. The API lets a KMCS-signed driver intercept handle creation and handle duplication on &lt;code&gt;PsProcessType&lt;/code&gt;, &lt;code&gt;PsThreadType&lt;/code&gt;, and the desktop object type. The registration carries an Altitude (a FltMgr-style collision key) and an array of &lt;code&gt;OB_OPERATION_REGISTRATION&lt;/code&gt; records [@ms-ob-callback-registration]. Pre-operation callbacks can strip access-mask bits before the handle is granted; post-operation callbacks fire for logging. The parallel API &lt;code&gt;PsSetCreateProcessNotifyRoutineEx&lt;/code&gt; [@ms-pssetcreateprocessnotifyroutineex] covers process creation. Together, these are the kernel-mode primitives every modern EDR product depends on; they ship inside the Object Manager itself and they are the reason an EDR knows when something opens a handle to &lt;code&gt;lsass.exe&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;4.2 Generation 3 -- AppContainer and the lowbox token&lt;/h3&gt;
&lt;p&gt;Windows 8 shipped on October 26, 2012 [@en-wikipedia-windows-8]. Modern / UWP apps downloaded from the Microsoft Store needed a sandbox finer-grained than per-session BNO. The Vista path rewriting in &lt;code&gt;kernel32!BaseGetNamedObjectDirectory&lt;/code&gt; happened in user mode, which made it the wrong layer for a sandbox: a hostile renderer could in principle bypass the user-mode rewrite. The new layer moved into the kernel.&lt;/p&gt;
&lt;p&gt;Each UWP / MSIX process runs under a special token type, the &lt;em&gt;AppContainer / LowBox token&lt;/em&gt; (referred to in kernel code as the &lt;em&gt;lowbox token&lt;/em&gt;), created by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt;. The token carries a &lt;code&gt;TOKEN_APPCONTAINER_INFORMATION&lt;/code&gt; block that names the process&apos;s package SID (&lt;code&gt;S-1-15-2-...&lt;/code&gt;) and an &lt;code&gt;AppContainerNumber&lt;/code&gt;. Inside &lt;code&gt;ObpLookupObjectName&lt;/code&gt;, &lt;em&gt;before&lt;/em&gt; the path is walked, the kernel checks whether the caller&apos;s token is a lowbox token; if it is, lookups of &lt;code&gt;\BaseNamedObjects\X&lt;/code&gt;, &lt;code&gt;\RPC Control\X&lt;/code&gt;, and other rewriteable paths get redirected into &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\X&lt;/code&gt;. The user-mode caller never sees the rewrite. The package-SID directory is created by SYSTEM at process-creation time with a security descriptor that grants the package SID, and only the package SID, full access. Microsoft&apos;s wording is precise: AppContainer works by &quot;sandboxing the application kernel objects, the AppContainer environment prevents the application from influencing, or being influenced by, other application processes&quot; [@ms-appcontainer-isolation].&lt;/p&gt;

The AppInfo service, which is responsible for creating the new application, calls the undocumented API CreateAppContainerToken to do some internal housekeeping. Unfortunately this API creates object directories under the user&apos;s AppContainerNamedObjects object directory to support redirecting BaseNamedObjects and RPC endpoints by the OS. -- James Forshaw, Project Zero Issue 1550 [@p0-issue1550]
&lt;p&gt;The residual class the AppContainer model has not closed is the one Forshaw&apos;s August 30, 2018 Project Zero post [@p0-issue1550] documents: because the SYSTEM-side AppInfo service has to write into the user&apos;s AppContainerNamedObjects subtree to set up redirection, an unprivileged caller can race the directory creation and end up planting a symbolic link the SYSTEM service then follows. The class -- &quot;SYSTEM-privileged directory creation in user-controllable territory&quot; -- is the worked example of why &quot;the kernel rewrites the name&quot; is an isolation property only when the SYSTEM helpers also use the rewrite.&lt;/p&gt;
&lt;h3&gt;4.3 Generation 4 -- VBS trustlets and the IUM secure-kernel namespace&lt;/h3&gt;
&lt;p&gt;Windows 10 RTM shipped on July 29, 2015 [@en-wikipedia-windows-10-version-history]. The Virtualization-Based Security (VBS) feature set introduced a parallel object-manager-shaped namespace that lives in Virtual Trust Level 1 (VTL1) and is inaccessible to the VTL0 NT kernel. Inside VTL1 the Secure Kernel (&lt;code&gt;securekernel.exe&lt;/code&gt;) maintains its own root, its own type registry, and its own handle-table machinery. The VTL0 NT kernel can see &lt;em&gt;trustlet processes&lt;/em&gt; -- the per-trustlet user-mode containers running in Isolated User Mode (IUM) -- but it cannot reach into their secure-side state.&lt;/p&gt;
&lt;p&gt;Alex Ionescu&apos;s Black Hat USA 2015 talk Battle of SKM and IUM [@ionescu-bh2015-pdf] is the canonical inventory of the inbox Trustlet IDs at ship: Trustlet 0 is the Secure Kernel Process hosting Device Guard; Trustlet 1 is LSAISO.EXE for Credential Guard; Trustlet 2 is VMSP.EXE hosting the virtual TPM; Trustlet 3 is the vTPM provisioning trustlet. Each is identified by a Trustlet ID and reachable only through narrow Secure Kernel ALPC ports. The VBS Trustlets piece in this series unpacks the threat model.&lt;/p&gt;
&lt;h3&gt;4.4 Generation 5 -- Server Silos and the silo-scoped namespace&lt;/h3&gt;
&lt;p&gt;Windows Server 2016 shipped on October 12, 2016 [@en-wikipedia-windows-server-2016]. Microsoft needed a Linux-namespaces equivalent so that container runtimes -- Docker, containerd, and the Azure Kubernetes Service Windows-node pods that followed -- could host adjacent workloads on one kernel. The answer was &lt;em&gt;Server Silo&lt;/em&gt;: a new &lt;code&gt;OBJECT_TYPE&lt;/code&gt; registered alongside &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Process&lt;/code&gt;, and &lt;code&gt;Thread&lt;/code&gt;, that carries its own &lt;code&gt;RootDirectory&lt;/code&gt;, &lt;code&gt;DosDevicesDirectory&lt;/code&gt;, and &lt;code&gt;ServerSiloGlobals&lt;/code&gt;. A process attached to a silo via &lt;code&gt;PsAttachSiloToCurrentThread&lt;/code&gt; sees the silo&apos;s namespace as its root; the silo&apos;s &lt;code&gt;\GLOBAL??\C:&lt;/code&gt; resolves to the silo&apos;s &lt;code&gt;\Device\HarddiskVolume*&lt;/code&gt;, which is a different &lt;code&gt;Device&lt;/code&gt; object from the host&apos;s. Job objects [@ms-job-objects] provide the cgroups-equivalent resource-accounting dimension; the Silo type builds on top.&lt;/p&gt;
&lt;p&gt;The canonical reverse-engineering reference is Daniel Prizmant&apos;s July 2020 Unit 42 writeup, which spells out the architecture: &quot;job objects are used in a similar way control groups (cgroups) are used in Linux, and... server silo objects were used as a replacement for namespaces support in the kernel&quot; [@unit42-rev-eng-windows-containers].&lt;/p&gt;
&lt;p&gt;The companion piece, Prizmant&apos;s June 2021 &lt;em&gt;Siloscape&lt;/em&gt; [@unit42-siloscape], is the first known malware family that escapes the silo boundary: Prizmant named the malware &quot;Siloscape (sounds like silo escape) because its primary goal is to escape the container, and in Windows this is implemented mainly by a server silo.&quot; James Forshaw&apos;s April 2021 Project Zero post &lt;em&gt;Who Contains the Containers?&lt;/em&gt; [@p0-who-contains-containers] is the four-LPE companion disclosure. Microsoft&apos;s standing position is that Server Silo is not a security boundary; the Hyper-V Container, which adds a Hyper-V VM around the container&apos;s silo, is the security-boundary product.&lt;/p&gt;
&lt;h3&gt;4.5 Generation 6 -- the symbolic-link hardening continuum&lt;/h3&gt;
&lt;p&gt;The cross-cutting hardening generation closes the symlink subclass that recurred in Generations 1, 3, and 5. MS15-090 shipped on August 11, 2015 [@ms-ms15-090] and &quot;corrects how Windows Object Manager handles object symbolic links created by a sandbox process, by preventing improper interaction with the registry by sandboxed applications, and by preventing improper interaction with the filesystem by sandboxed applications.&quot; The bulletin&apos;s canonical Object Manager CVE is CVE-2015-2428 [@nvd-cve-2015-2428], described verbatim as the case where the &quot;Object Manager in Microsoft Windows... does not properly constrain impersonation levels during interaction with object symbolic links that originated in a sandboxed process.&quot; Subsequent Windows 10 builds added &lt;code&gt;OBJ_DONT_REPARSE&lt;/code&gt;, an open-time flag that disables symbolic-link substitution for callers willing to opt in, and post-Siloscape patches in 2021 closed &lt;code&gt;NtSetInformationSymbolicLink&lt;/code&gt; retargeting from inside a silo.&lt;/p&gt;
&lt;p&gt;The scope document for this article originally attributed MS15-090 to CVE-2015-2528 and CVE-2015-1463. Independent NVD verification confirmed neither is correct: CVE-2015-2528 [@nvd-cve-2015-2528] is the MS15-102 Task Management EoP, and CVE-2015-1463 [@nvd-cve-2015-1463] is a ClamAV denial-of-service crash. The canonical MS15-090 OM-symlink CVE is CVE-2015-2428. Separately, CVE-2018-0824 [@nvd-cve-2018-0824] is a CWE-502 COM deserialization issue that joined the CISA KEV catalog on 2024-08-05, not a namespace-squatting CVE.&lt;/p&gt;
&lt;p&gt;The residual subclass MS15-090 did not close was the per-session &lt;code&gt;\??&lt;/code&gt; DosDevices remapping path under impersonation. A low-privileged process whose token is impersonated by a SYSTEM service can plant a &lt;code&gt;DefineDosDevice&lt;/code&gt; remapping that survives into the impersonation-time &lt;code&gt;\??&lt;/code&gt; view, and the SYSTEM-side activation-context resolver then opens the redirected path while running with elevated privileges. The canonical 2023 worked example is HackSys&apos;s &lt;em&gt;Activation Context Hell -- DosDevices Remapping Attack under Impersonation&lt;/em&gt; [@hacksys-activation-context-hell], which targets the CSRSS / SxS activation-context resolver and shipped as CVE-2023-35359 [@nvd-cve-2023-35359], with the closely-related CVE-2022-22047 [@nvd-cve-2022-22047] covering the underlying CSRSS surface. The mitigation has to live inside the impersonation-aware &lt;code&gt;\??&lt;/code&gt; resolver in the SYSTEM caller, not at the symlink-creation gate.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every generation since Generation 1 has &lt;em&gt;layered&lt;/em&gt; a new isolation primitive on top of the prior generation. None has rendered its predecessor obsolete. On 2026 Windows 11 25H2 all six generations coexist simultaneously: a UWP / MSIX app inside a Server Silo on a VBS-enabled host is session-partitioned, lowbox-rewritten, silo-scoped, VTL0-confined, integrity-gated, and watched by every loaded EDR&apos;s &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; filter. Each layer adds an independent enforcement point at &lt;code&gt;ObpLookupObjectName&lt;/code&gt; time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Six generations of isolation primitives is a tidy story, but it has glossed the most important question. What is the actual kernel data structure all six generations parameterize? What does the path-walk algorithm look like, what is the type registry, and where does the hash table live?&lt;/p&gt;
&lt;h2&gt;5. The four load-bearing primitives&lt;/h2&gt;
&lt;p&gt;If you remember one paragraph from this article, make it this one. The Object Manager namespace is built out of four kernel data structures: an &lt;code&gt;OBJECT_HEADER&lt;/code&gt; that prefixes every named object in memory, an &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singleton that owns each type&apos;s method table, a &lt;code&gt;ParseProcedure&lt;/code&gt; that delegates path resolution to the owning subsystem when needed, and an &lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt; hash table that maps names to objects. Every Windows security boundary you have read about is a parameter to one of these four pieces. The next eight subsections rebuild them one at a time.&lt;/p&gt;

flowchart TB
    OD[&quot;OBJECT_DIRECTORY&lt;br /&gt;(37-bucket hash table)&quot;] --&amp;gt;|&quot;hash(name) % 37&quot;| OH
    OH[&quot;OBJECT_HEADER&lt;br /&gt;(PointerCount, HandleCount,&lt;br /&gt;TypeIndex, InfoMask,&lt;br /&gt;SecurityDescriptor, Body offset)&quot;] --&amp;gt;|&quot;TypeIndex XOR&lt;br /&gt;ObHeaderCookie&quot;| OT
    OT[&quot;OBJECT_TYPE singleton&lt;br /&gt;(in nt!ObTypeIndexTable)&quot;] --&amp;gt;|&quot;TypeInfo&quot;| TI
    TI[&quot;TYPE_INFO method table&lt;br /&gt;(Dump, Open, Close, Delete,&lt;br /&gt;ParseProcedure,&lt;br /&gt;Security, QueryName, ...)&quot;]
    OH --&amp;gt;|&quot;Body[]&quot;| BODY[&quot;Type-specific body&lt;br /&gt;(EPROCESS, FILE_OBJECT,&lt;br /&gt;SECTION_OBJECT, ...)&quot;]
&lt;h3&gt;5.1 OBJECT_HEADER&lt;/h3&gt;
&lt;p&gt;Every named kernel object lives in non-paged pool. Immediately &lt;em&gt;before&lt;/em&gt; each object&apos;s typed body sits an &lt;code&gt;OBJECT_HEADER&lt;/code&gt;, a 0x30-byte (48-byte on x64) structure that the Object Manager owns. &lt;code&gt;PointerCount&lt;/code&gt; and &lt;code&gt;HandleCount&lt;/code&gt; are the two reference counts: the former tracks raw kernel-mode pointer references, the latter tracks user-mode handles. &lt;code&gt;TypeIndex&lt;/code&gt; is a single byte that indexes into the &lt;code&gt;nt!ObTypeIndexTable&lt;/code&gt; to find the object&apos;s type singleton; since Windows 10 1709, the byte is XOR-obfuscated against the per-boot &lt;code&gt;nt!ObHeaderCookie&lt;/code&gt; so that simple type confusion is non-trivial.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;InfoMask&lt;/code&gt; is a bitmap of optional sub-headers that may precede the main header: &lt;code&gt;OBJECT_HEADER_NAME_INFO&lt;/code&gt; for named objects, &lt;code&gt;OBJECT_HEADER_QUOTA_INFO&lt;/code&gt; for objects that charge a quota block, &lt;code&gt;OBJECT_HEADER_HANDLE_INFO&lt;/code&gt; for objects that need per-process handle accounting. &lt;code&gt;SecurityDescriptor&lt;/code&gt; is a tagged pointer to the object&apos;s DACL/SACL. &lt;code&gt;Body[]&lt;/code&gt; is the offset at which the type-specific payload begins; for a process object that payload is an &lt;code&gt;EPROCESS&lt;/code&gt;, for a file it is a &lt;code&gt;FILE_OBJECT&lt;/code&gt;, and so on. The canonical reference is Chapter 8 of &lt;em&gt;Windows Internals 7th Edition Part 1&lt;/em&gt; [@microsoftpressstore-wininternals7-part1].&lt;/p&gt;

The per-object header (`nt!_OBJECT_HEADER`) that precedes every named kernel object in non-paged pool. Carries reference counts (`PointerCount`, `HandleCount`), a `TypeIndex` byte that points into `nt!ObTypeIndexTable` (XOR-obfuscated against `nt!ObHeaderCookie` since Windows 10 1709), an `InfoMask` describing optional sub-headers, a `SecurityDescriptor` pointer, and the offset to the typed `Body[]`.
&lt;p&gt;The &lt;code&gt;TypeIndex&lt;/code&gt; XOR-with-cookie is one of the smallest kernel hardening changes Microsoft has shipped: a single byte that prevents a poisoned &lt;code&gt;OBJECT_HEADER&lt;/code&gt; from naming an arbitrary type after a heap-corruption primitive. The cookie is per-boot and lives in &lt;code&gt;nt!ObHeaderCookie&lt;/code&gt;. The hardening is documented in &lt;em&gt;Windows Internals 7th Edition&lt;/em&gt; Chapter 8 [@microsoftpressstore-wininternals7-part1] and in Geoff Chappell&apos;s reverse-engineering studies; Microsoft has not, as of 2026, published a Learn-hosted reference for the cookie itself.&lt;/p&gt;
&lt;h3&gt;5.2 OBJECT_TYPE&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;OBJECT_TYPE&lt;/code&gt; is the per-type singleton. There is exactly one &lt;code&gt;OBJECT_TYPE&lt;/code&gt; per registered kernel type, and they live in &lt;code&gt;\ObjectTypes&lt;/code&gt;. On Windows 11 25H2 the count sits at roughly seventy-five: &lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;Directory&lt;/code&gt;, &lt;code&gt;SymbolicLink&lt;/code&gt;, &lt;code&gt;Token&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Process&lt;/code&gt;, &lt;code&gt;Thread&lt;/code&gt;, &lt;code&gt;Section&lt;/code&gt;, &lt;code&gt;Key&lt;/code&gt;, &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;Event&lt;/code&gt;, &lt;code&gt;Mutant&lt;/code&gt;, &lt;code&gt;Semaphore&lt;/code&gt;, &lt;code&gt;Timer&lt;/code&gt;, &lt;code&gt;WindowStation&lt;/code&gt;, &lt;code&gt;Desktop&lt;/code&gt;, &lt;code&gt;Device&lt;/code&gt;, &lt;code&gt;Driver&lt;/code&gt;, &lt;code&gt;IoCompletion&lt;/code&gt;, &lt;code&gt;ALPC Port&lt;/code&gt;, &lt;code&gt;EtwRegistration&lt;/code&gt;, &lt;code&gt;Silo&lt;/code&gt;, and dozens more.&lt;/p&gt;

The per-type singleton (`nt!_OBJECT_TYPE`) that owns each kernel type&apos;s method table. The `TypeInfo` field carries eight procedure pointers and one offset field (WaitObjectFlagOffset): `DumpProcedure`, `OpenProcedure`, `CloseProcedure`, `DeleteProcedure`, `ParseProcedure` (the path-resolution callback), `SecurityProcedure`, `QueryNameProcedure`, `OkayToCloseProcedure`, and a `WaitObjectFlagOffset` offset for waitable types. Every `OBJECT_TYPE` instance is reachable through `\ObjectTypes`.
&lt;p&gt;The &lt;code&gt;TypeInfo&lt;/code&gt; field on each &lt;code&gt;OBJECT_TYPE&lt;/code&gt; carries eight procedure pointers and one offset field (WaitObjectFlagOffset). The most consequential is the &lt;code&gt;ParseProcedure&lt;/code&gt;. When &lt;code&gt;ObpLookupObjectName&lt;/code&gt; is walking a path component-by-component, and a step lands on an object whose &lt;code&gt;OBJECT_TYPE&lt;/code&gt; defines a &lt;code&gt;ParseProcedure&lt;/code&gt;, the OM hands the &lt;em&gt;residual&lt;/em&gt; path and the desired access to that procedure, which becomes the namespace authority below that point. That is how the registry&apos;s &lt;code&gt;Key&lt;/code&gt; type, the I/O Manager&apos;s &lt;code&gt;Device&lt;/code&gt; type, and the various WMI / Volume-Manager subsystems insert themselves into the namespace without the Object Manager having to know any of their internal structure [@en-wikipedia-object-manager].&lt;/p&gt;
&lt;h3&gt;5.3 The parse procedure&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ObpLookupObjectName&lt;/code&gt; walks &lt;code&gt;\Foo\Bar\Baz\...\Leaf&lt;/code&gt; left-to-right. At each component the walker does one of three things. The common case is a hash-table lookup in the current &lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt;&apos;s 37 buckets to find the child object by name. The second case is &lt;code&gt;SymbolicLink&lt;/code&gt; substitution: if the child object&apos;s type is &lt;code&gt;SymbolicLink&lt;/code&gt;, the walker substitutes the link target and re-enters the walk at the substitution. The third and most consequential case is &lt;em&gt;parse-procedure handoff&lt;/em&gt;. If the child object&apos;s &lt;code&gt;OBJECT_TYPE&lt;/code&gt; has a non-null &lt;code&gt;ParseProcedure&lt;/code&gt;, the walker stops, hands the residual path string to that procedure, and lets it decide what to do.&lt;/p&gt;

The load-bearing method pointer on each `OBJECT_TYPE`&apos;s `TypeInfo` field. When `ObpLookupObjectName` encounters an object whose type defines a `ParseProcedure`, the residual path is handed to that procedure for resolution. The two canonical parse procedures are `IopParseDevice` (for the `Device` type, which delegates further resolution to the device&apos;s owning driver via `IRP_MJ_CREATE`) and `CmpParseKey` (for the `Key` type, which walks the registry hive).
&lt;p&gt;&lt;code&gt;IopParseDevice&lt;/code&gt; is the parse procedure for the &lt;code&gt;Device&lt;/code&gt; type. When the walker reaches &lt;code&gt;\Device\HarddiskVolume1&lt;/code&gt; and is asked to continue with &lt;code&gt;\Users\me\file.txt&lt;/code&gt;, the I/O Manager builds an &lt;code&gt;IRP_MJ_CREATE&lt;/code&gt; packet, dispatches it to the filesystem driver that owns the volume (NTFS, ReFS, ExFAT, FAT32, or one of several others), and lets that driver walk the rest of the path inside its own on-disk structures. The driver returns a &lt;code&gt;FILE_OBJECT&lt;/code&gt;, which the Object Manager packages into a handle.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CmpParseKey&lt;/code&gt; is the parse procedure for the &lt;code&gt;Key&lt;/code&gt; type. When the walker reaches &lt;code&gt;\REGISTRY&lt;/code&gt; and is asked to continue with &lt;code&gt;\MACHINE\Software\Microsoft\Windows&lt;/code&gt;, the Configuration Manager takes over and walks the in-memory hive structures.&lt;/p&gt;
&lt;p&gt;The structural consequence is profound. Every named file in Windows is, technically, a leaf in the Object Manager namespace. NTFS, ReFS, ExFAT, and the registry are not separate naming systems; they are parse-procedure callbacks that hand &lt;code&gt;FILE_OBJECT&lt;/code&gt; or &lt;code&gt;KEY&lt;/code&gt; bodies back to the OM.&lt;/p&gt;

sequenceDiagram
    participant User as User Process
    participant OM as ObpLookupObjectName
    participant Dir as \GLOBAL?? OBJECT_DIRECTORY
    participant Dev as \Device\HarddiskVolume1 (Device type)
    participant Drv as NTFS Driver
    User-&amp;gt;&amp;gt;OM: NtCreateFile(&quot;\??\C:\Users\me\file.txt&quot;)
    OM-&amp;gt;&amp;gt;OM: rewrite \??\ -&amp;gt; \Sessions\\DosDevices\
    OM-&amp;gt;&amp;gt;Dir: lookup &quot;C:&quot;
    Dir--&amp;gt;&amp;gt;OM: SymbolicLink -&amp;gt; \Device\HarddiskVolume1
    OM-&amp;gt;&amp;gt;OM: substitute, re-enter walk
    OM-&amp;gt;&amp;gt;Dev: lookup \Device\HarddiskVolume1
    Dev--&amp;gt;&amp;gt;OM: type=Device, has ParseProcedure
    OM-&amp;gt;&amp;gt;Drv: IopParseDevice with &quot;\Users\me\file.txt&quot;
    Drv-&amp;gt;&amp;gt;Drv: IRP_MJ_CREATE: walk MFT, find file
    Drv--&amp;gt;&amp;gt;OM: FILE_OBJECT
    OM--&amp;gt;&amp;gt;User: HANDLE
&lt;h3&gt;5.4 The 37-bucket directory hash&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt; is a 37-bucket open-hash table. The hash function is &lt;code&gt;RtlHashUnicodeString&lt;/code&gt;, applied to each component name. Thirty-seven was the prime Cutler picked in 1993; the constant has not changed in thirty-three years. The folk-knowledge corroboration is in Chapter 8 of &lt;em&gt;Windows Internals 7th Edition Part 1&lt;/em&gt; and in Forshaw&apos;s &lt;em&gt;Windows Security Internals&lt;/em&gt; Chapter 8; Microsoft has never published a Learn-hosted spec for the constant [@nostarch-windows-security-internals].&lt;/p&gt;

The 37-bucket open-hash table (`nt!_OBJECT_DIRECTORY`) that lives at every interior node of the Object Manager tree. Keys are `UNICODE_STRING` component names; the hash is `RtlHashUnicodeString` modulo 37. Each bucket is a linked list of `OBJECT_DIRECTORY_ENTRY` records that point at the next-level `OBJECT_HEADER`. Reading the tree requires `Directory`-`TRAVERSE` rights on the parent.
&lt;p&gt;The 37-bucket constant from 1993 has not changed in thirty-three years. On a 2026 Windows 11 25H2 box with several hundred MSIX packages each owning an &lt;code&gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; subtree, average bucket chains run several entries deep. Collision pressure on the constant is the open problem returned to in Section 9.&lt;/p&gt;
&lt;h3&gt;5.5 The lowbox redirect inside ObpLookupObjectName&lt;/h3&gt;
&lt;p&gt;This is the subsection that earns the second aha moment of the article.&lt;/p&gt;
&lt;p&gt;When the calling thread&apos;s primary token is a lowbox token, &lt;code&gt;ObpLookupObjectName&lt;/code&gt; consults the token&apos;s &lt;code&gt;AppContainerNumber&lt;/code&gt; and package SID &lt;em&gt;before&lt;/em&gt; it begins the walk. Lookups that would otherwise resolve into &lt;code&gt;\BaseNamedObjects&lt;/code&gt; or &lt;code&gt;\RPC Control&lt;/code&gt; are rewritten into &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt;. The rewrite happens transparently to the user-mode Win32 caller, which still thinks it asked for &lt;code&gt;\BaseNamedObjects\X&lt;/code&gt;.&lt;/p&gt;

A specialised token type produced by `NtCreateLowBoxToken` that carries a `TOKEN_APPCONTAINER_INFORMATION` block (with a package SID `S-1-15-2-...` and an `AppContainerNumber`). When a process runs under a lowbox token, `ObpLookupObjectName` rewrites every named-object lookup into the per-package directory `\Sessions\\AppContainerNamedObjects\\` before path walking begins.

The user-facing brand for the lowbox-token mechanism. Every UWP / MSIX / Windows Store app runs in an AppContainer. The Windows API surface is unchanged for the app; the Object Manager rewrites every named-object name into a per-package subtree, gating cross-package coordination at the namespace layer. The Microsoft Learn page describes this as &quot;Sandboxing the application kernel objects, the AppContainer environment prevents the application from influencing, or being influenced by, other application processes&quot; [@ms-appcontainer-isolation].
&lt;p&gt;The aha moment is structural. AppContainer is not a &lt;em&gt;containment&lt;/em&gt; mechanism the way you might first picture it. It is a &lt;em&gt;name-translation&lt;/em&gt; mechanism. The lowbox token tells the kernel which directory to rewrite every name into; the sandbox is, at root, a hash-table indirection inside the kernel&apos;s path-walk function. The Edge renderer process cannot name &lt;code&gt;\BaseNamedObjects\GlobalEvent_Foo&lt;/code&gt; because the kernel rewrites that name into &lt;code&gt;\Sessions\1\AppContainerNamedObjects\S-1-15-2-...\Global\GlobalEvent_Foo&lt;/code&gt; before lookup even begins. The &quot;sandbox&quot; is a hash-table redirect.&lt;/p&gt;
&lt;h3&gt;5.6 The Silo OBJECT_TYPE and silo-scoped views&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Silo&lt;/code&gt; is itself a registered &lt;code&gt;OBJECT_TYPE&lt;/code&gt;. Each silo instance carries a silo-scoped &lt;code&gt;RootDirectory&lt;/code&gt;, &lt;code&gt;DosDevicesDirectory&lt;/code&gt;, and &lt;code&gt;ServerSiloGlobals&lt;/code&gt; (with the silo&apos;s own registry-hive root and per-silo &lt;code&gt;BaseNamedObjects&lt;/code&gt; root). &lt;code&gt;PsAttachSiloToCurrentThread&lt;/code&gt; switches the thread&apos;s namespace view; once attached, every Object Manager lookup runs through the silo&apos;s roots instead of the host&apos;s. Job objects, which provide the cgroups-equivalent resource-accounting substrate, are the underlying primitive the Silo type extends [@ms-job-objects]. The structural design history is in Prizmant&apos;s reverse-engineering writeup [@unit42-rev-eng-windows-containers].&lt;/p&gt;

A specialised `Job`-derived kernel object (`OBJECT_TYPE` Silo) introduced in Windows Server 2016 that carries silo-scoped `RootDirectory`, `DosDevicesDirectory`, and `ServerSiloGlobals` fields. A thread attached to a silo via `PsAttachSiloToCurrentThread` sees the silo&apos;s namespace as its root; the silo&apos;s `\GLOBAL??\C:` resolves to the silo&apos;s `\Device\HarddiskVolume*`, which is a different `Device` object from the host&apos;s. Server Silo is the substrate underneath Windows Server Containers and WSL1.
&lt;h3&gt;5.7 The Secure Kernel&apos;s parallel namespace&lt;/h3&gt;
&lt;p&gt;Inside VTL1, the Secure Kernel maintains a separate Object Manager tree with its own root, its own type registry, and its own handle-table machinery. The VTL0 NT kernel cannot enumerate this tree; the only cross-VTL traffic is the narrow ALPC interface each trustlet publishes. Ionescu&apos;s BH2015 inventory (Trustlet IDs 0 through 3 at ship, growing in subsequent releases) is the canonical primary [@ionescu-bh2015-pdf].&lt;/p&gt;

A user-mode process running in Isolated User Mode under the VTL1 Secure Kernel. Each trustlet is signed with both the Windows System Component Verification EKU (1.3.6.1.4.1.311.10.3.6) and the IUM EKU (1.3.6.1.4.1.311.10.3.37), runs at Signature Level 12, and is reachable from VTL0 only through narrow ALPC ports. LSAISO.EXE (Credential Guard), VMSP.EXE (virtual TPM host), and the vTPM provisioning trustlet are the inbox examples.
&lt;h3&gt;5.8 The handle table&lt;/h3&gt;
&lt;p&gt;The namespace is the &lt;em&gt;name&lt;/em&gt; side; the per-process &lt;code&gt;HANDLE_TABLE&lt;/code&gt; is the &lt;em&gt;access&lt;/em&gt; side. Once a handle exists in a process, no name lookup happens on subsequent use; the kernel dereferences the handle through a three-level radix tree indexed by the 32-bit handle value, lands on an &lt;code&gt;OBJECT_HEADER&lt;/code&gt;, and operates on the body. This is why &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; fires on handle &lt;em&gt;creation&lt;/em&gt; and &lt;em&gt;duplication&lt;/em&gt; rather than on every use, and why an inherited handle bypasses the callback entirely. The structural consequence -- that the Object Manager is the gate at name resolution but not at every operation -- comes back in Section 8.&lt;/p&gt;
&lt;p&gt;Now you know the data structure. But what does the actual tree look like in 2026? What does &lt;code&gt;\&lt;/code&gt; contain on a Windows 11 25H2 box, and which security boundary lives in each top-level directory?&lt;/p&gt;
&lt;h2&gt;6. The 2026 top-level directory atlas&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;WinObj.exe&lt;/code&gt; as administrator on a Windows 11 25H2 machine and the root directory at &lt;code&gt;\&lt;/code&gt; carries roughly twenty entries. The table below catalogues the load-bearing ones. Each row names the directory, the security boundary it physically realises, and a representative exploit class that has been thrown at it. The driver kit&apos;s Object Directories reference [@ms-object-directories] is Microsoft&apos;s canonical inventory.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Top-level directory&lt;/th&gt;
&lt;th&gt;What it contains&lt;/th&gt;
&lt;th&gt;Which boundary it enforces&lt;/th&gt;
&lt;th&gt;Exploit class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\ObjectTypes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ~75 &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singletons (&lt;code&gt;Process&lt;/code&gt;, &lt;code&gt;Thread&lt;/code&gt;, &lt;code&gt;Section&lt;/code&gt;, &lt;code&gt;Key&lt;/code&gt;, &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;Token&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Silo&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;Meta -- the type registry the rest of the namespace depends on&lt;/td&gt;
&lt;td&gt;Type confusion (mitigated by &lt;code&gt;ObHeaderCookie&lt;/code&gt; since Windows 10 1709)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Device&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Driver-published device objects (&lt;code&gt;\Device\HarddiskVolume*&lt;/code&gt;, &lt;code&gt;\Device\Tcp&lt;/code&gt;, &lt;code&gt;\Device\Tpm&lt;/code&gt;, &lt;code&gt;\Device\NamedPipe&lt;/code&gt;, &lt;code&gt;\Device\Mailslot&lt;/code&gt;, &lt;code&gt;\Device\Vmbus&lt;/code&gt;, &lt;code&gt;\Device\KsecDD&lt;/code&gt;, &lt;code&gt;\Device\CNG&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The I/O Manager&apos;s surface; each driver&apos;s parse procedure consumes residual paths&lt;/td&gt;
&lt;td&gt;Bait-and-switch on &lt;code&gt;\Device&lt;/code&gt; (a low-privilege user redirects a privileged opener through a planted symbolic link)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Driver&lt;/code&gt;, &lt;code&gt;\FileSystem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Loaded &lt;code&gt;DRIVER_OBJECT&lt;/code&gt; registries&lt;/td&gt;
&lt;td&gt;KMCS / HVCI driver-load gate&lt;/td&gt;
&lt;td&gt;Vulnerable signed-driver class (BYOVD)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\GLOBAL??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The machine-wide DosDevices view -- where &lt;code&gt;C:&lt;/code&gt; and &lt;code&gt;D:&lt;/code&gt; are symlinks to &lt;code&gt;\Device\HarddiskVolume*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross-session drive-letter map&lt;/td&gt;
&lt;td&gt;Symlink redirect across session boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The per-session DosDevices alias, falling through to &lt;code&gt;\GLOBAL??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session-scoped drive-letter map&lt;/td&gt;
&lt;td&gt;The HackSys / CVE-2023-35359 worked example: a low-privilege caller plants a &lt;code&gt;DefineDosDevice&lt;/code&gt; remapping that survives into the impersonation-time &lt;code&gt;\??&lt;/code&gt; view, and the SYSTEM-side activation-context resolver opens the redirected path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\BaseNamedObjects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The global / &lt;code&gt;Global\&lt;/code&gt;-prefixed-only BNO&lt;/td&gt;
&lt;td&gt;Cross-session named-object visibility&lt;/td&gt;
&lt;td&gt;Pre-Vista squatting class (closed by Generation 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-session subtrees (BNO, DosDevices, WindowStations, AppContainerNamedObjects)&lt;/td&gt;
&lt;td&gt;Session boundary (Generation 2)&lt;/td&gt;
&lt;td&gt;Shatter attacks (closed by Generation 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-package UWP / MSIX lowbox namespace&lt;/td&gt;
&lt;td&gt;AppContainer / lowbox boundary (Generation 3)&lt;/td&gt;
&lt;td&gt;Forshaw P0 Issue 1550 arbitrary-directory creation race&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\RPC Control&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every named LRPC ALPC port (every COM call lands here)&lt;/td&gt;
&lt;td&gt;RPC endpoint visibility&lt;/td&gt;
&lt;td&gt;Endpoint squatting against named LRPC ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\KnownDlls&lt;/code&gt;, &lt;code&gt;\KnownDlls32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pre-mapped &lt;code&gt;Section&lt;/code&gt; objects for system DLLs&lt;/td&gt;
&lt;td&gt;Loader supply-chain&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DefineDosDevice&lt;/code&gt; + &lt;code&gt;\??&lt;/code&gt; symlink-plant trick (closed in NTDLL July 2022, build 19044.1826)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\KernelObjects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-defined events (&lt;code&gt;LowMemoryCondition&lt;/code&gt;, &lt;code&gt;HighMemoryCondition&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;Kernel-internal visibility&lt;/td&gt;
&lt;td&gt;None public&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Callback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-defined &lt;code&gt;Callback&lt;/code&gt; objects (&lt;code&gt;ExCallback&lt;/code&gt; slots drivers register against)&lt;/td&gt;
&lt;td&gt;Kernel API extension surface&lt;/td&gt;
&lt;td&gt;Driver-callback abuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Security&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LSA-private endpoints&lt;/td&gt;
&lt;td&gt;LSA / authentication isolation&lt;/td&gt;
&lt;td&gt;Credential-theft (the LSAISO trustlet via Generation 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Windows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;BNO-redirect surface and &lt;code&gt;SharedSection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Win32 subsystem shared state&lt;/td&gt;
&lt;td&gt;Cross-session Win32 state leakage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Silos\&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-container silo subroots on Server SKUs&lt;/td&gt;
&lt;td&gt;Server Silo boundary (Generation 5)&lt;/td&gt;
&lt;td&gt;Siloscape -- symlink retarget out of the silo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\BNOLINKS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The boundary-keyed private-namespace index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CreatePrivateNamespace&lt;/code&gt; cross-session/cross-package IPC&lt;/td&gt;
&lt;td&gt;None public; the directory itself is RE-derived&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart LR
    subgraph EdgeRenderer[&quot;Microsoft Edge Renderer (lowbox token)&quot;]
        K32[&quot;CreateMutexW(L&apos;Global\\Foo&apos;)&quot;]
    end
    K32 --&amp;gt;|&quot;NtCreateMutant, OBJECT_ATTRIBUTES&quot;| OB
    subgraph KernelOb[&quot;ObpLookupObjectName&quot;]
        OB[&quot;Read caller token&lt;br /&gt;token.AppContainerNumber&lt;br /&gt;token.PackageSid&quot;]
        OB --&amp;gt;|&quot;rewrite name&quot;| RW[&quot;Rewrite &apos;\\BaseNamedObjects\\Global\\Foo&apos;&lt;br /&gt;to&lt;br /&gt;&apos;\\Sessions\\1\\AppContainerNamedObjects\\&lt;br /&gt;S-1-15-2-...\\Global\\Foo&apos;&quot;]
        RW --&amp;gt; WALK[&quot;walk the rewritten path&quot;]
    end
    WALK --&amp;gt; Dir[&quot;\\Sessions\\1\\AppContainerNamedObjects\\&lt;br /&gt;S-1-15-2-...\\Global\\&lt;br /&gt;(per-package OBJECT_DIRECTORY,&lt;br /&gt;DACL allows only package SID)&quot;]
&lt;p&gt;The &lt;code&gt;\BNOLINKS&lt;/code&gt; directory deserves a separate paragraph because it is not on Microsoft Learn. &lt;code&gt;NtCreatePrivateNamespace&lt;/code&gt; is the kernel-side syscall behind the Win32 &lt;code&gt;CreatePrivateNamespace&lt;/code&gt; API [@ms-createprivatenamespacew]; the caller passes a boundary descriptor built by &lt;code&gt;CreateBoundaryDescriptor&lt;/code&gt; [@ms-createboundarydescriptorw] plus one or more SIDs added via &lt;code&gt;AddSIDToBoundaryDescriptor&lt;/code&gt; [@ms-addsidtoboundarydescriptor]. The kernel materialises one &lt;code&gt;\BNOLINKS&lt;/code&gt; entry per &lt;code&gt;(alias_prefix, boundary_descriptor_hash)&lt;/code&gt; tuple; two callers that pass the same &lt;code&gt;lpAliasPrefix&lt;/code&gt; but different boundary descriptors land on different directories. The native signature is documented in the PHNT-derived NtDoc mirror [@ntdoc-ntcreateprivatenamespace], and the &lt;code&gt;OBJECT_BOUNDARY_DESCRIPTOR&lt;/code&gt; structure layout is at ntdoc.m417z.com/object_boundary_descriptor [@ntdoc-object-boundary-descriptor]. The Win32 Object Namespaces overview [@ms-object-namespaces] is Microsoft&apos;s only published user-mode reference; the &lt;code&gt;\BNOLINKS&lt;/code&gt; directory name itself is reverse-engineering-derived.The &lt;code&gt;\BNOLINKS&lt;/code&gt; directory is documented only through reverse engineering of &lt;code&gt;ntoskrnl.exe&lt;/code&gt; -- via Forshaw&apos;s NtObjectManager and System Informer&apos;s PHNT headers -- not on Microsoft Learn. The user-mode API surface (&lt;code&gt;CreatePrivateNamespace&lt;/code&gt;, &lt;code&gt;CreateBoundaryDescriptor&lt;/code&gt;, &lt;code&gt;AddSIDToBoundaryDescriptor&lt;/code&gt;) is fully documented. The provenance gap is worth flagging when you cite the directory by name.The &lt;code&gt;\KnownDlls&lt;/code&gt; LPE class was, for a decade, the canonical example of how a DACL plus loader-side validation could lock down a supply-chain anchor. Forshaw&apos;s August 2018 P0 post first sketched a &lt;code&gt;DefineDosDevice&lt;/code&gt; + &lt;code&gt;\??&lt;/code&gt; symlink-plant chain that could land a forged &lt;code&gt;Section&lt;/code&gt; object into &lt;code&gt;\KnownDlls&lt;/code&gt;; Clement Labro (itm4n) implemented the attack as the PPLdump tool and wrote companion posts on both itm4n.github.io [@itm4n-lsass-runasppl] and the SCRT team blog [@blog-scrt-bypassing-lsa-protection-in-userland]. The class was closed in NTDLL by Windows 10 21H2 build 19044.1826; itm4n confirms the patch in &lt;em&gt;The End of PPLdump&lt;/em&gt; [@itm4n-the-end-of-ppldump]: &quot;A patch in NTDLL now prevents PPLs from loading Known DLLs.&quot;&lt;/p&gt;
&lt;p&gt;{`
const MAX_DIRECTORY_BUCKETS = 37;&lt;/p&gt;
&lt;p&gt;function rtlHashUnicodeString(name) {
  let h = 0;
  for (const ch of name.toUpperCase()) {
    h = (h * 31 + ch.charCodeAt(0)) &amp;gt;&amp;gt;&amp;gt; 0;
  }
  return h % MAX_DIRECTORY_BUCKETS;
}&lt;/p&gt;
&lt;p&gt;function makeDir() {
  return { buckets: Array(MAX_DIRECTORY_BUCKETS).fill(null).map(() =&amp;gt; []) };
}&lt;/p&gt;
&lt;p&gt;function addChild(dir, name, child) {
  dir.buckets[rtlHashUnicodeString(name)].push({ name, child });
}&lt;/p&gt;
&lt;p&gt;function lookupObjectName(path, root) {
  const components = path.split(&apos;\\&apos;).filter(Boolean);
  let cursor = root;
  for (const comp of components) {
    const bucket = rtlHashUnicodeString(comp);
    const chain = cursor.buckets[bucket];
    const hit = chain.find(e =&amp;gt; e.name.toUpperCase() === comp.toUpperCase());
    console.log(`lookup &apos;${comp}&apos; -&amp;gt; bucket ${bucket}, chain length ${chain.length}, ${hit ? &apos;HIT&apos; : &apos;MISS&apos;}`);
    if (!hit) return null;
    if (hit.child.parseProcedure) {
      const rest = &apos;\\&apos; + components.slice(components.indexOf(comp) + 1).join(&apos;\\&apos;);
      console.log(`  parse-procedure handoff for type &apos;${hit.child.type}&apos;, residual=&apos;${rest}&apos;`);
      return { handedOff: hit.child, residual: rest };
    }
    cursor = hit.child;
  }
  return cursor;
}&lt;/p&gt;
&lt;p&gt;const root = makeDir();
const device = makeDir();
device.parseProcedure = true; device.type = &apos;Device&apos;;
const sessions = makeDir();
addChild(root, &apos;Device&apos;, device);
addChild(root, &apos;Sessions&apos;, sessions);
addChild(root, &apos;BaseNamedObjects&apos;, makeDir());&lt;/p&gt;
&lt;p&gt;lookupObjectName(&apos;\\Device\\HarddiskVolume1\\Users\\me\\file.txt&apos;, root);
`}&lt;/p&gt;
&lt;p&gt;The walk is the algorithm. The 37 is the bucket count Cutler picked in 1993. The parse-procedure handoff is where the I/O Manager and the Configuration Manager and dozens of other subsystems insert themselves into the tree. Now turn the question around: Windows bet on one tree. What did the kernels that did not bet on one tree do, and why?&lt;/p&gt;
&lt;h2&gt;7. How other kernels name kernel objects&lt;/h2&gt;
&lt;p&gt;Three kernels, three different bets. Linux took the namespace and &lt;em&gt;split it into per-resource-class clones&lt;/em&gt; -- one for mounts, one for PIDs, one for IPC, one for the network stack, one for users, one for hostnames, one for cgroups, one for time -- and never built a unified tree. macOS / Darwin gave each task its own &lt;em&gt;Mach port-right namespace&lt;/em&gt; and let &lt;code&gt;launchd&lt;/code&gt; broker named-service lookups. Plan 9 from Bell Labs was the academic ancestor of &quot;every named OS resource is a filesystem path,&quot; and the design Cutler imported into NT.&lt;/p&gt;
&lt;h3&gt;7.1 Linux: per-resource namespaces&lt;/h3&gt;
&lt;p&gt;Linux ships eight namespace types, each governed by a &lt;code&gt;CLONE_NEW*&lt;/code&gt; flag passed to &lt;code&gt;clone()&lt;/code&gt;, &lt;code&gt;unshare()&lt;/code&gt;, or &lt;code&gt;setns()&lt;/code&gt;: mount, PID, network, IPC, user, UTS, cgroup, and time. The &lt;code&gt;namespaces(7)&lt;/code&gt; man page is precise: &quot;A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource&quot; [@man7-namespaces]. Docker, containerd, runc, Kubernetes pods, LXC, and systemd-nspawn all compose these eight flags into a Linux container.&lt;/p&gt;
&lt;p&gt;The strength of the Linux design is per-class composability. A process can be in a fresh mount namespace, a fresh PID namespace, and the host&apos;s network namespace, all at once. The weakness is the absence of a unified type registry: Linux has no equivalent of &lt;code&gt;\ObjectTypes&lt;/code&gt;, no equivalent of the &lt;code&gt;OBJECT_HEADER&lt;/code&gt; reference counting that the kernel applies uniformly to every named object. Each resource class has its own lookup function, its own permission model, and its own ownership story. A bug in any one of them is bounded to that one resource class but is also not shared mitigation across the others.&lt;/p&gt;
&lt;h3&gt;7.2 macOS / Darwin: Mach ports and the bootstrap server&lt;/h3&gt;
&lt;p&gt;Darwin&apos;s kernel-object naming is capability-style. Apple&apos;s archive documentation describes the model directly: &quot;each task consists of a virtual address space, a port right namespace, and one or more threads&quot; [@apple-mach-kernel]. Tasks send messages by holding a &lt;em&gt;port right&lt;/em&gt; -- a per-task index into a kernel-managed table of Mach ports. There is no single hierarchical namespace; ports are sent over Mach messages, and &lt;code&gt;launchd&lt;/code&gt; operates as the bootstrap-server name broker for services that need a stable rendezvous. A separate I/O Registry tree carries device objects.&lt;/p&gt;
&lt;p&gt;The strength of the Mach design is that capabilities cannot be forged; you cannot synthesise a port right out of a string the way you can synthesise a path string under Windows. The weakness is the split namespace: device objects live in the I/O Registry, services live behind &lt;code&gt;launchd&lt;/code&gt;, and the kernel itself has no equivalent of &lt;code&gt;\BaseNamedObjects&lt;/code&gt; as a one-stop shop.&lt;/p&gt;
&lt;h3&gt;7.3 Plan 9 from Bell Labs&lt;/h3&gt;
&lt;p&gt;Plan 9 is the design lineage Cutler imported. In Plan 9, every named operating-system resource -- including processes, network connections, devices, and the window system -- surfaces as a path served over 9P. The single hierarchical namespace was the central claim. Plan 9 never reached commercial scale, but its design idea reached production in three places: NT (1993, via Cutler), Linux&apos;s /proc, /sys, and FUSE (the 1990s onward), and the various capability-OS research projects (KeyKOS, EROS, seL4) that took the lessons in a different direction.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Granularity&lt;/th&gt;
&lt;th&gt;Enforcement point&lt;/th&gt;
&lt;th&gt;Structural / opt-in&lt;/th&gt;
&lt;th&gt;Bypass by privilege&lt;/th&gt;
&lt;th&gt;Inheritance gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Per-Session (NT)&lt;/td&gt;
&lt;td&gt;Logon session&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ObpLookupObjectName&lt;/code&gt; + DACL&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeDebugPrivilege&lt;/code&gt; short-circuit&lt;/td&gt;
&lt;td&gt;Inherited handles cross sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppContainer Lowbox (NT)&lt;/td&gt;
&lt;td&gt;Package SID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ObpLookupObjectName&lt;/code&gt; rewrite&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;TCB privileges only&lt;/td&gt;
&lt;td&gt;Brokered handles enter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server Silo (NT)&lt;/td&gt;
&lt;td&gt;Container&lt;/td&gt;
&lt;td&gt;Process-&amp;gt;Silo indirection&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;KMCS-signed driver&lt;/td&gt;
&lt;td&gt;Host handles cross silos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VBS / IUM Trustlet (NT)&lt;/td&gt;
&lt;td&gt;Trust level (VTL)&lt;/td&gt;
&lt;td&gt;Hypervisor&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Hypervisor compromise&lt;/td&gt;
&lt;td&gt;Cross-VTL ALPC only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mandatory Integrity Control (NT)&lt;/td&gt;
&lt;td&gt;IL band&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeAccessCheckByType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opt-in (per-object SACL)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeRelabelPrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inherited handles bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ObRegisterCallbacks&lt;/code&gt; (NT)&lt;/td&gt;
&lt;td&gt;Per-type, per-driver&lt;/td&gt;
&lt;td&gt;Object Manager pre-op callback&lt;/td&gt;
&lt;td&gt;Mediation, not partition&lt;/td&gt;
&lt;td&gt;KMCS-signed driver&lt;/td&gt;
&lt;td&gt;Inheritance bypasses callback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Namespace (NT)&lt;/td&gt;
&lt;td&gt;Boundary SID-list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NtCreatePrivateNamespace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;All SIDs in caller&apos;s token&lt;/td&gt;
&lt;td&gt;Boundary-keyed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux Namespace&lt;/td&gt;
&lt;td&gt;Per-resource clone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setns&lt;/code&gt;/&lt;code&gt;unshare&lt;/code&gt;/&lt;code&gt;clone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CAP_SYS_ADMIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fork inherits namespace set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mach Port Right&lt;/td&gt;
&lt;td&gt;Per-task&lt;/td&gt;
&lt;td&gt;Capability check on send&lt;/td&gt;
&lt;td&gt;Structural (capabilities)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;host_priv&lt;/code&gt; / kernel&lt;/td&gt;
&lt;td&gt;Inherited rights on fork&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The Object Manager namespace is not a filesystem. There is no disk persistence, no journal, no FAT or MFT, no inode allocator, no per-file DACL in the filesystem sense. Nothing under `\` survives a reboot. Files-on-disk, registry-keys-in-a-hive, and named pipes are leaves in the OM tree, but the actual filesystem implementation lives in NTFS / ReFS / ExFAT drivers reached through the `Device` type&apos;s parse procedure.&lt;p&gt;What the OM namespace &lt;em&gt;shares&lt;/em&gt; with filesystems is exactly three things: the path-walk algorithm (left-to-right, component-by-component, with one hash-table lookup per component), the per-directory hash table (analogous to the directory-entry hash filesystems use), and the per-object security descriptor (which the SRM enforces at the same point a filesystem would enforce its DACL).&lt;/p&gt;
&lt;p&gt;When you read or write the phrase &quot;Object Manager namespace,&quot; the metaphor that is doing real work is &quot;in-memory directory tree the kernel uses to find named objects,&quot; not &quot;filesystem in the disk-format sense.&quot;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

The Windows 2000-era `CreateRestrictedToken` primitive was the wrong layer in 2000 as a standalone sandboxing mechanism -- it could not partition the namespace; it only filtered the caller&apos;s SID set against per-object DACLs. Chromium revived it in 2008 as one of four cooperating layers, and that pattern is the canonical 2026 production sandbox shape. The Chromium design document captures the constraints: &quot;The Windows sandbox is a user-mode only sandbox. There are no special kernel mode drivers... The sandbox is provided as a static library that must be linked to both the broker and the target executables&quot; (Chromium Sandbox Design [@chromium-sandbox-md], FAQ [@chromium-sandbox-faq]).&lt;p&gt;The four layers compose pairwise-orthogonally. The token gates &lt;em&gt;which DACLs&lt;/em&gt; the renderer can satisfy at &lt;code&gt;SeAccessCheck&lt;/code&gt; time; the job object gates &lt;em&gt;which kernel API surface&lt;/em&gt; the renderer can call (UI exceptions, process creation, etc.); the integrity level gates &lt;em&gt;which writes&lt;/em&gt; the renderer can perform across MIC label boundaries; the AppContainer lowbox-rewrites &lt;em&gt;every named-object lookup&lt;/em&gt; into the per-package directory inside &lt;code&gt;ObpLookupObjectName&lt;/code&gt;. A handle that survives all four checks is the only object the renderer can usefully touch. The load-bearing header is &lt;code&gt;sandbox_policy.h&lt;/code&gt;, which declares &lt;code&gt;TargetConfig::SetTokenLevel(TokenLevel initial, TokenLevel lockdown)&lt;/code&gt;, &lt;code&gt;SetJobLevel&lt;/code&gt;, &lt;code&gt;SetIntegrityLevel&lt;/code&gt;, &lt;code&gt;SetDelayedIntegrityLevel&lt;/code&gt;, and &lt;code&gt;SetAppContainerSid&lt;/code&gt;, with one verbatim mutual-exclusion note: &quot;Using an initial token is not compatible with AppContainer&quot; [@chromium-sandbox-policy-h].&lt;/p&gt;
&lt;p&gt;This is the 2026 production sandbox shape every Chromium-based browser inherits (Edge, Chrome, Brave, Vivaldi, Opera), as do Electron-based apps like Visual Studio Code&apos;s renderer processes.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

The cross-VTL ALPC ports through which a VTL0 process talks to a VTL1 trustlet are still located in VTL0&apos;s `\RPC Control`. An attacker who controls VTL0 can *send* messages to LsaIso even though they cannot *read* LsaIso&apos;s internal state. Oliver Lyak&apos;s December 2022 *Pass-the-Challenge* result is the canonical worked example ([GitHub: ly4k/PassTheChallenge](https://github.com/ly4k/PassTheChallenge)): the trustlet&apos;s pages are never read, but the trustlet&apos;s RPC output exfiltrates the secret. The lesson is that VTL1 isolation is a *page-level* read barrier, not a *protocol-level* containment property. The VBS Trustlets piece in this corpus carries the deeper walkthrough.
&lt;p&gt;Windows bet on one tree; Linux bet on eight clone-flag dimensions; Darwin bet on capability-style port-right tables. Each bet has theoretical limits. What are they?&lt;/p&gt;
&lt;h2&gt;8. What the namespace cannot do&lt;/h2&gt;
&lt;p&gt;The frame for this section comes from James P. Anderson&apos;s 1972 USAF technical report &lt;em&gt;Computer Security Technology Planning Study&lt;/em&gt; (ESD-TR-73-51), Section 4.1.1. Anderson is the named originator of the reference-monitor concept and of the four properties such a monitor must satisfy. Wikipedia preserves the modern acronym verbatim: the reference-validation mechanism must be &quot;&lt;strong&gt;N&lt;/strong&gt;on-bypassable... &lt;strong&gt;E&lt;/strong&gt;valuable... &lt;strong&gt;A&lt;/strong&gt;lways invoked... &lt;strong&gt;T&lt;/strong&gt;amper-proof,&quot; and &quot;according to Ross Anderson, the reference monitor concept was introduced by James Anderson in an influential 1972 paper&quot; [@wikipedia-reference-monitor]. The NIST CSRC mirror hosts the original PDF [@csrc-nist-ande72].&lt;/p&gt;
&lt;p&gt;Saltzer and Schroeder&apos;s 1975 paper &lt;em&gt;The Protection of Information in Computer Systems&lt;/em&gt; [@cs-virginia-saltzer-schroeder] added the &lt;em&gt;complete-mediation principle&lt;/em&gt; -- &quot;every access to every object must be checked for authority&quot; -- and seven other design principles the reference-validation mechanism must satisfy (economy of mechanism, fail-safe defaults, open design, separation of privilege, least privilege, least common mechanism, psychological acceptability).&lt;/p&gt;
&lt;p&gt;Map the Windows Object Manager against the four NEAT properties and the answer is uncomfortable. The namespace partially achieves two (Always-invoked and Tamper-proof), fails Non-bypassable outright, and falls one to two orders of magnitude short of Evaluable.&lt;/p&gt;
&lt;h3&gt;8.1 Always-invoked: provably gapped&lt;/h3&gt;
&lt;p&gt;The namespace achieves always-invoked for &lt;em&gt;name-based opens&lt;/em&gt;. Every &lt;code&gt;Nt*OpenObject*&lt;/code&gt; syscall walks &lt;code&gt;ObpLookupObjectName&lt;/code&gt;; there is no path that returns a handle to a named object without going through the lookup. But the namespace cannot achieve always-invoked for &lt;em&gt;handle inheritance&lt;/em&gt;. A child process inherits handles from &lt;code&gt;CreateProcess(bInheritHandles=TRUE)&lt;/code&gt; without going through the OM at all. The handles already exist in the parent&apos;s &lt;code&gt;HANDLE_TABLE&lt;/code&gt;; the kernel walks the parent&apos;s table, duplicates the entries into the child&apos;s table, and the child has live access. No name-lookup, no &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; callback, no SRM check. As long as the OS API exposes handle inheritance -- and it is too deeply embedded in 33 years of shipping Windows code to remove -- the Object Manager cannot be the &lt;em&gt;sole&lt;/em&gt; reference monitor.&lt;/p&gt;
&lt;h3&gt;8.2 Tamper-proof: bounded, not absolute&lt;/h3&gt;
&lt;p&gt;The Object Manager runs in ring 0, under Kernel-Mode Code Signing (KMCS), and -- on machines with Virtualization-Based Security and Hypervisor-protected Code Integrity (HVCI) enabled -- inside a Hyper-V-enforced code-integrity policy. Any kernel-mode adversary who can load a driver bypasses the OM. KMCS and HVCI raise the cost; they do not eliminate the surface. The Bring-Your-Own-Vulnerable-Driver class of attacks (signed but exploitable drivers) is the running residual class, and the historical pattern is that one or two new vulnerable signed drivers surface every quarter.&lt;/p&gt;
&lt;h3&gt;8.3 Evaluable: provably above threshold&lt;/h3&gt;
&lt;p&gt;A small enough TCB can be machine-verified. The seL4 microkernel is the canonical demonstration: roughly 9,000 lines of C verified end-to-end against a formal specification (~11 person-years for initial functional correctness per Klein et al. SOSP 2009, and approximately 25 person-years for the full suite of subsequent proofs including information-flow and binary verification) [@sel4-project]. The Object Manager subsystem, the Security Reference Monitor, and the parse procedures the Object Manager delegates to (file-system drivers via &lt;code&gt;IopParseDevice&lt;/code&gt;; the registry via &lt;code&gt;CmpParseKey&lt;/code&gt;; ALPC; the I/O manager itself) collectively comprise tens of thousands of lines of C, putting the TCB for &quot;open a named object&quot; at one to two orders of magnitude above the verification threshold any current proof system can handle. The Object Manager is &lt;em&gt;not&lt;/em&gt; evaluable in the formal sense Anderson required.&lt;/p&gt;
&lt;h3&gt;8.4 Non-bypassable: the privilege short-circuit&lt;/h3&gt;
&lt;p&gt;A process holding &lt;code&gt;SeDebugPrivilege&lt;/code&gt; (or any privilege that grants &lt;code&gt;PROCESS_VM_*&lt;/code&gt; rights) can short-circuit per-directory ACLs. The privilege evaluation happens at &lt;code&gt;SeAccessCheck&lt;/code&gt; time, &lt;em&gt;after&lt;/em&gt; &lt;code&gt;ObpLookupObjectName&lt;/code&gt; has resolved the name. The Object Manager will resolve any path the privileged caller asks for; the gate fires, but it lets the call through. The namespace cannot defend against the holder of &lt;code&gt;SeDebugPrivilege&lt;/code&gt;. This is by design -- you want a debugger to be able to attach to anything -- but it is also the structural reason why &quot;lock down the namespace&quot; is not by itself a containment story.&lt;/p&gt;
&lt;h3&gt;8.5 What else the namespace cannot do&lt;/h3&gt;
&lt;p&gt;It cannot prevent in-process memory disclosure -- the Pass-the-Challenge limit covered in the Section 7 aside. It cannot defend against a malicious driver -- KMCS, HVCI, and WDAC gate driver load; the namespace itself trusts already-loaded drivers. It cannot eliminate time-of-check / time-of-use racing during a path walk; the walker walks components one at a time, and any reentrant call into the walker is a TOCTOU surface. The mitigation is per-call -- callers pass &lt;code&gt;OBJ_DONT_REPARSE&lt;/code&gt; on object-attributes, &lt;code&gt;FILE_FLAG_OPEN_REPARSE_POINT&lt;/code&gt; on file opens, or otherwise instruct the path-walker to refuse symbolic-link substitution -- not a structural property of the namespace.&lt;/p&gt;
&lt;h3&gt;8.6 The honest accounting&lt;/h3&gt;
&lt;p&gt;The Object Manager namespace is a &lt;em&gt;coordination&lt;/em&gt; mechanism, not a &lt;em&gt;containment&lt;/em&gt; mechanism. Containment is in the layers above: the session ID, the package SID, the integrity level, the silo ID, the VTL split. The namespace&apos;s job is to make those layers &lt;em&gt;enforceable&lt;/em&gt; by partitioning the path space so the bad open &lt;em&gt;cannot resolve to the privileged object&apos;s name&lt;/em&gt;. The layers above decide which partition the caller is in; the namespace&apos;s only job is &quot;given a path and a caller, find the object.&quot; Anderson 1972 names the &lt;em&gt;kernel mechanism&lt;/em&gt; (the reference-validation mechanism with NEAT properties); Saltzer-Schroeder 1975 names the &lt;em&gt;design principles&lt;/em&gt; the mechanism must satisfy. The Object Manager is the Windows realisation; it inherits both the strengths and the limits.&lt;/p&gt;

The namespace is a coordination mechanism, not a containment mechanism. The containment is in the layers above.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The Object Manager is the coordination layer; the containment is in the partition primitives stacked on top (session ID, package SID, integrity level, silo ID, VTL). The namespace&apos;s only job is &quot;given a path and a caller, find the object.&quot; Every Windows security boundary is a parameter to that one job: a per-directory ACL, a token-keyed name rewrite, or a kernel callback registered against an &lt;code&gt;OBJECT_TYPE&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The provable gaps are real. What is the active research direction in 2026 -- where do attackers and defenders actually meet inside the namespace today?&lt;/p&gt;
&lt;h2&gt;9. Open problems in 2026&lt;/h2&gt;
&lt;p&gt;Five open problems sit in active research as of 2026.&lt;/p&gt;
&lt;h3&gt;9.1 Hash-bucket collision pressure&lt;/h3&gt;
&lt;p&gt;The 37-bucket constant has not changed since 1993. On a 2026 Windows 11 25H2 machine with several hundred MSIX packages, each owning an &lt;code&gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; subtree, average chain lengths inside &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; exceed two and routinely run higher under load. The structural impact is small per-lookup (O(chain length) at each component), but it compounds across deep path walks and across the per-VM hot loops in &lt;code&gt;ObpLookupObjectName&lt;/code&gt;. Microsoft has not committed to a larger table or a different structure; the constant remains.&lt;/p&gt;
&lt;h3&gt;9.2 Cross-AppContainer object-directory privacy&lt;/h3&gt;
&lt;p&gt;Per-AppContainer isolation is the AppContainer model&apos;s promise; residual cross-package reads erode it. Forshaw&apos;s Project Zero work between 2017 and 2020 documents specific classes; Windows 11 25H2 DACLs are tighter than Windows 10 RTM, but the impersonation-mediated cases survive. The HackSys / CVE-2023-35359 family covered in Section 4.5 is the current realisation of the cross-AppContainer-plus-impersonation surface, and the same broader resource-planting taxonomy Forshaw described in the 2017 Named Pipe Secure Prefixes post [@tiraniddo-named-pipe-secure-prefixes] is still rediscovered every year.&lt;/p&gt;
&lt;h3&gt;9.3 Silo-escape via routines that ignore silo attachment&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Siloscape&lt;/em&gt; (June 7, 2021) showed that &lt;code&gt;NtSetInformationSymbolicLink&lt;/code&gt; could retarget a silo-scoped symbolic link at a host-scoped path. Microsoft patched the specific function; the &lt;em&gt;class&lt;/em&gt; -- kernel routines whose path resolution does not honour &lt;code&gt;Process-&amp;gt;Silo-&amp;gt;RootDirectory&lt;/code&gt; -- remains open. Microsoft&apos;s long-standing position is that Server Silo is not a security boundary; Hyper-V Container is the security-boundary product. Container runtimes that depend on Server Silo for tenant isolation are knowingly running outside the supported boundary.&lt;/p&gt;
&lt;h3&gt;9.4 ObRegisterCallbacks erosion under HVCI&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ObRegisterCallbacks&lt;/code&gt; requires a KMCS-signed driver, and on HVCI-enabled machines the binary must additionally be HVCI-compatible. Microsoft has progressively raised the compatibility bar -- preventing unsigned drivers, banning common runtime-patching idioms, and tightening the W^X policy. EDR vendors depend on the surface staying open; if HVCI&apos;s compatibility bar ever excludes the EDR kernel driver pattern, the in-kernel callback layer is at risk. The CrowdStrike Falcon Sensor outage of July 2024 made the brittleness of in-kernel EDR a public conversation. Microsoft&apos;s &lt;em&gt;Defender for Endpoint&lt;/em&gt; and &lt;em&gt;EDR-on-Linux eBPF&lt;/em&gt; projects point at alternative-mediation futures, but in-kernel &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; is still the primary credential-theft sensor.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As attackers ship Hell&apos;s Gate / Halo&apos;s Gate / direct-syscall stubs to bypass userland EDR hooks, the kernel callback fires regardless. The arms race accordingly shifts to the &lt;em&gt;access-mask-strip vs. impersonate-trusted-parent-PID&lt;/em&gt; layer inside the kernel callback itself, with both sides racing to define the right pre-operation policy for &lt;code&gt;lsass.exe&lt;/code&gt; handle opens. Watch the Microsoft Security Response Center advisories and the EDR-vendor incident postmortems for the bleeding edge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9.5 Public benchmark vacuum&lt;/h3&gt;
&lt;p&gt;No peer-reviewed benchmark compares per-call namespace-lookup cost across the Windows Object Manager, Linux namespaces, and Mach ports. Choice of namespace design at the OS level is a multi-decade commitment; the absence of an empirical comparison forces architecture decisions on theoretical-only grounds. The Linux Kernel Test Robot, the Phoronix Test Suite, and various academic systems-conference benchmarks measure adjacent properties (filesystem-call latency, system-call vector cost), but none publishes head-to-head numbers on the named-object-lookup hot path. This is an open invitation to systems researchers.&lt;/p&gt;
&lt;p&gt;Five open problems is a research agenda, not a how-to. How do you actually look at this thing on your own machine?&lt;/p&gt;
&lt;h2&gt;10. Reading the namespace from a live system&lt;/h2&gt;
&lt;p&gt;Three tools cover the operational practice: Sysinternals WinObj, Forshaw&apos;s NtObjectManager PowerShell module, and WinDbg in kernel mode.&lt;/p&gt;
&lt;h3&gt;10.1 WinObj on a live system&lt;/h3&gt;
&lt;p&gt;Download &lt;code&gt;winobj.exe&lt;/code&gt; from Sysinternals [@ms-winobj] and run it as administrator. The left pane is the directory tree; the right pane shows the children of the selected directory with their object types. Navigate to &lt;code&gt;\Sessions\1\BaseNamedObjects&lt;/code&gt; and read off the named events and mutants every Win32 app in your interactive session has created. Navigate to &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; and pick an &lt;code&gt;S-1-15-2-...&lt;/code&gt; directory; right-click, choose Properties, and read the security descriptor. You will see a single allow-ACE granting full access only to the package SID itself. That ACE is the entire AppContainer sandbox at the namespace layer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; WinObj cannot traverse &lt;code&gt;\ObjectTypes&lt;/code&gt;, &lt;code&gt;\Security&lt;/code&gt;, or &lt;code&gt;\Sessions\0\&lt;/code&gt; without administrator rights. Without traversal, the enumerate fails silently and the tree looks empty. Always run elevated, and accept that the tool will show the kernel view, not a per-process view.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;10.2 NtObjectManager PowerShell&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;NtObjectManager&lt;/code&gt; is Forshaw&apos;s PowerShell module that exposes the Object Manager namespace through cmdlets (PowerShell Gallery [@powershellgallery-ntobjectmanager]; GitHub [@p0-sandbox-attacksurface-analysis-tools]). Install with &lt;code&gt;Install-Module NtObjectManager&lt;/code&gt;. Useful commands: &lt;code&gt;Get-ChildItem NtObject:\&lt;/code&gt; walks the root; &lt;code&gt;Get-NtType&lt;/code&gt; lists the registered &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singletons; &lt;code&gt;Get-NtObject \BaseNamedObjects&lt;/code&gt; enumerates the global BNO; &lt;code&gt;Get-NtAlpcPort &apos;\RPC Control&apos;&lt;/code&gt; lists every LRPC endpoint on the machine. The module wraps the same NTDLL syscalls WinObj uses, but in a scripting surface that composes into automation.&lt;/p&gt;
&lt;h3&gt;10.3 WinDbg kernel session&lt;/h3&gt;
&lt;p&gt;In a kernel-mode WinDbg session attached to a target machine (or to a live local kernel via Microsoft&apos;s local-kernel debug mode), &lt;code&gt;!object \&lt;/code&gt; dumps the root directory and its children. &lt;code&gt;dt nt!_OBJECT_HEADER &amp;lt;addr&amp;gt;-30&lt;/code&gt; reads the header preceding any object&apos;s body (the offset 0x30 is the size of &lt;code&gt;OBJECT_HEADER&lt;/code&gt; on x64; subtract that from the body pointer to land on the header -- the field layout is documented in &lt;em&gt;Windows Internals 7th Edition* Chapter 8, Microsoft Press Store [@microsoftpressstore-wininternals7-part1]). `dx -r1 ((nt!_OBJECT_TYPE&lt;/em&gt;)nt!PsProcessType[0]).TypeInfo` walks the Process type&apos;s method table and lists all eight procedure pointers and the WaitObjectFlagOffset, including the parse procedure.&lt;/p&gt;
&lt;h3&gt;10.4 The EDR primitive: an ObRegisterCallbacks driver template&lt;/h3&gt;
&lt;p&gt;The minimal sketch of an in-kernel EDR sensor is four steps. Register an &lt;code&gt;OB_CALLBACK_REGISTRATION&lt;/code&gt; for &lt;code&gt;PsProcessType&lt;/code&gt; with &lt;code&gt;OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE&lt;/code&gt; [@ms-obregistercallbacks]. In the pre-operation callback, examine &lt;code&gt;OperationInformation-&amp;gt;Object&lt;/code&gt;, derive the target process&apos;s PID, and compare it against &lt;code&gt;lsass.exe&lt;/code&gt;. If it matches, strip credential-relevant access bits from &lt;code&gt;OperationInformation-&amp;gt;Parameters-&amp;gt;CreateHandleInformation.DesiredAccess&lt;/code&gt; (or duplicate-handle equivalent). The kernel grants the handle with the reduced rights, the attacker&apos;s &lt;code&gt;PROCESS_VM_READ&lt;/code&gt; is gone before the call returns, and the post-operation callback logs the attempt. The parallel API &lt;code&gt;PsSetCreateProcessNotifyRoutineEx&lt;/code&gt; [@ms-pssetcreateprocessnotifyroutineex] covers process creation, which is the other half of the EDR sensor surface.&lt;/p&gt;

sequenceDiagram
    participant A as Attacker process
    participant NT as nt!NtOpenProcess
    participant OM as Object Manager
    participant EDR as EDR Pre-Op Callback
    participant LSASS as lsass.exe (target)
    A-&amp;gt;&amp;gt;NT: NtOpenProcess(lsass PID, PROCESS_VM_READ | PROCESS_QUERY_INFORMATION)
    NT-&amp;gt;&amp;gt;OM: lookup PsProcessType, target by PID
    OM-&amp;gt;&amp;gt;EDR: fire pre-op callback (handle create)
    EDR-&amp;gt;&amp;gt;EDR: target == lsass.exe?
    EDR-&amp;gt;&amp;gt;EDR: strip PROCESS_VM_READ from DesiredAccess
    EDR--&amp;gt;&amp;gt;OM: granted = PROCESS_QUERY_LIMITED_INFORMATION
    OM--&amp;gt;&amp;gt;NT: HANDLE with reduced access
    NT--&amp;gt;&amp;gt;A: open succeeded (but useless rights)
&lt;p&gt;{`
const PROCESS_VM_READ                   = 0x0010;
const PROCESS_VM_WRITE                  = 0x0020;
const PROCESS_VM_OPERATION              = 0x0008;
const PROCESS_QUERY_INFORMATION         = 0x0400;
const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
const PROCESS_CREATE_THREAD             = 0x0002;
const PROCESS_DUP_HANDLE                = 0x0040;&lt;/p&gt;
&lt;p&gt;function stripForLsass(desired) {
  const STRIPPED =
      PROCESS_VM_READ |
      PROCESS_VM_WRITE |
      PROCESS_VM_OPERATION |
      PROCESS_CREATE_THREAD |
      PROCESS_DUP_HANDLE |
      PROCESS_QUERY_INFORMATION;
  return desired &amp;amp; ~STRIPPED;
}&lt;/p&gt;
&lt;p&gt;const desired = PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE;
console.log(&apos;attacker asked for:&apos;, &apos;0x&apos; + desired.toString(16));
const granted = stripForLsass(desired) | PROCESS_QUERY_LIMITED_INFORMATION;
console.log(&apos;EDR pre-op granted:&apos;, &apos;0x&apos; + granted.toString(16));
`}&lt;/p&gt;

```c
OB_OPERATION_REGISTRATION op = {
    .ObjectType = PsProcessType,
    .Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
    .PreOperation = MyPreOp,
    .PostOperation = MyPostOp,
};
OB_CALLBACK_REGISTRATION reg = {
    .Version = OB_FLT_REGISTRATION_VERSION,
    .OperationRegistrationCount = 1,
    .Altitude = RTL_CONSTANT_STRING(L&quot;123456&quot;),
    .OperationRegistration = &amp;amp;op,
};
ObRegisterCallbacks(®, &amp;amp;g_handle);
```
The driver must be KMCS-signed (`IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY`) per the wdm.h documentation; an unsigned image returns `STATUS_ACCESS_DENIED` from `ObRegisterCallbacks`. Two drivers cannot pick the same Altitude; collisions return `STATUS_FLT_INSTANCE_ALTITUDE_COLLISION`.
&lt;p&gt;You can now read the namespace, register an EDR-style callback, and dump the type registry. What are the questions readers ask after they finish reading?&lt;/p&gt;
&lt;h2&gt;11. Frequently asked questions&lt;/h2&gt;


No. The registry is a separate Windows Executive subsystem implemented in `nt!Cm*`, with its own hive on-disk format and its own in-memory hive structures. It hooks into the Object Manager namespace through one and only one mechanism: the `Key` `OBJECT_TYPE` registers a `ParseProcedure` (`CmpParseKey`) that takes over path walking when the namespace walker reaches `\REGISTRY`. The registry is therefore a *consumer* of the Object Manager, but not part of the Object Manager.

Because `\BaseNamedObjects` is the *global* / `Global\`-prefixed-only view, distinct from the per-session BNO at `\Sessions\\BaseNamedObjects`. The Win32 `Local\` prefix routes through `kernel32!BaseGetNamedObjectDirectory` into the per-session BNO; `Global\` routes into the global one [@ms-termserv-kernel-object-namespaces]. Cross-session named-object coordination still needs the global view; per-session isolation lives in the per-session subtree.

Because the lowbox token attached to the UWP app&apos;s process tells `ObpLookupObjectName` to rewrite the path to `\Sessions\\AppContainerNamedObjects\\Global\Foo` before path walking. Two different UWP apps have two different package SIDs and therefore land on two different directories. The Win32 names look the same; the kernel resolves them to different objects.

`\??\C:` is the per-session DosDevices alias; if `C:` is not defined in the current session&apos;s `\??`, the walker falls through to `\GLOBAL??\C:`. `\GLOBAL??\C:` is the machine-wide DosDevices symbolic link to `\Device\HarddiskVolume*` -- the real on-disk volume object. The split matters because the per-session `\??` is where per-session drive-letter remappings (`net use X: \\server\share`, `subst Z: C:\foo`, `DefineDosDevice`) live, and the activation-context resolver class covered in Section 4.5 is the exploit family that lives at this boundary.

Several top-level directories have `Directory`-`TRAVERSE` ACLs that restrict to SYSTEM and the local Administrators group. Without traversal, the directory enumeration silently fails. `\ObjectTypes`, `\Security`, and `\Sessions\0\` are the directories users most often notice as &quot;missing&quot; when running unelevated.

By DACL plus loader-side validation. The directory grants `Directory`-`READ` to everyone but `Directory`-`WRITE` only to SYSTEM and TrustedInstaller. The `Section` objects inside are Authenticode-signed by Microsoft and validated at boot by `smss.exe`. The historical `DefineDosDevice` + `\??` symlink-plant bypass class survived until Windows 10 21H2 build 19044.1826 (July 2022), when an NTDLL patch closed it [@itm4n-the-end-of-ppldump].

`ObRegisterCallbacks` [@ms-obregistercallbacks] and `PsSetCreateProcessNotifyRoutineEx` [@ms-pssetcreateprocessnotifyroutineex] are both fully documented. The HVCI compatibility requirements, the KMCS attestation flow, and the exact policy interactions with Defender for Endpoint&apos;s tamper-protection layer are partly implementation-defined; EDR vendor engineering teams maintain private regression suites against successive Windows feature updates.

When two or more processes that don&apos;t share a session or package must coordinate over a securable directory keyed by a SID-list they agree on at design time. The boundary descriptor is the *agreement primitive*: the kernel requires every SID in the boundary to be in the caller&apos;s token. The namespace&apos;s `OBJECT_DIRECTORY` lives in `\BNOLINKS`, keyed by the alias-prefix string plus a hash of the boundary descriptor&apos;s SID-list (CreatePrivateNamespaceW [@ms-createprivatenamespacew]; Object Namespaces overview [@ms-object-namespaces]; native NtCreatePrivateNamespace [@ntdoc-ntcreateprivatenamespace] and OBJECT_BOUNDARY_DESCRIPTOR [@ntdoc-object-boundary-descriptor] signatures). From inside an AppContainer process the lookup is rewritten into the per-package subtree, so private namespaces are not a substitute for the `windows.applicationModel.*` brokered APIs when cross-package coordination is the goal.


A user-mode structure produced by `CreateBoundaryDescriptor` and populated with `AddSIDToBoundaryDescriptor` (plus the optional `CREATE_BOUNDARY_DESCRIPTOR_ADD_APPCONTAINER_SID` flag). Conceptually the descriptor is a SID-list that the caller and every other participant must share via their tokens. Kernel-side the structure is `OBJECT_BOUNDARY_DESCRIPTOR` (Version, Items, TotalSize, Flags). `NtCreatePrivateNamespace` materialises a directory in `\BNOLINKS` keyed by the `lpAliasPrefix` plus a hash of the boundary descriptor&apos;s SIDs.
&lt;h2&gt;12. Coming back to the WinObj screen&lt;/h2&gt;
&lt;p&gt;Open WinObj one more time. Navigate back to &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; and pick the Edge renderer&apos;s &lt;code&gt;S-1-15-2-...&lt;/code&gt; directory. You can now name everything you are looking at. The directory is an &lt;code&gt;_OBJECT_DIRECTORY&lt;/code&gt; instance with 37 hash buckets. You reach it through a token-keyed rewrite that the kernel applies inside &lt;code&gt;ObpLookupObjectName&lt;/code&gt; before path walking begins. Its security descriptor grants &lt;code&gt;GenericAll&lt;/code&gt; only to the package SID. Every EDR loaded on this machine has registered an &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; filter on &lt;code&gt;PsProcessType&lt;/code&gt;, watching for handle creations against &lt;code&gt;lsass.exe&lt;/code&gt;. If you are running on a Server SKU with Windows Server Containers, the directory might also be silo-scoped, with &lt;code&gt;Process-&amp;gt;Silo-&amp;gt;RootDirectory&lt;/code&gt; indirecting your view of the rest of &lt;code&gt;\&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The four pieces of the 1993 Cutler design have shipped without architectural change for thirty-three years. The six generations of partition primitives stacked on top are all simultaneously load-bearing on Windows 11 25H2. The namespace itself is a coordination mechanism, in Anderson 1972&apos;s sense of the reference-validation mechanism, with Saltzer-Schroeder 1975&apos;s complete-mediation principle as the design constraint it must satisfy. Containment lives in the partition layers above it: the session, the package, the integrity level, the silo, and the VTL split. Every other article in this corpus -- the Credential Guard piece, the AppContainer piece, the VBS Trustlets piece, the Hyper-V piece, the App Identity piece, the TPM piece -- quietly assumes this tree underneath them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every Windows security boundary is a path rewrite, a per-directory ACL, a token-keyed name substitution, or a kernel callback against an &lt;code&gt;OBJECT_TYPE&lt;/code&gt;. The Object Manager is the data structure underneath them all.&lt;/p&gt;
&lt;/blockquote&gt;

**Key terms.** Object Manager (`Ob`), `OBJECT_HEADER`, `OBJECT_TYPE`, `ParseProcedure`, `OBJECT_DIRECTORY`, Lowbox token, AppContainer, Server Silo, Trustlet / IUM, Boundary descriptor, Session 0 isolation, Mandatory Integrity Control, `ObRegisterCallbacks`, KMCS, HVCI, `\BaseNamedObjects`, `\Sessions\\AppContainerNamedObjects`, `\RPC Control`, `\KnownDlls`, `\BNOLINKS`, `\GLOBAL??`, `\??`.&lt;p&gt;&lt;strong&gt;Review questions.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why does AppContainer isolation work even when the calling UWP app explicitly asks for &lt;code&gt;Global\X&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is the relationship between &lt;code&gt;IopParseDevice&lt;/code&gt;, &lt;code&gt;\Device\HarddiskVolume1&lt;/code&gt;, and &lt;code&gt;IRP_MJ_CREATE&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Which of Anderson 1972&apos;s four NEAT properties does the Object Manager achieve cleanly, and which does it provably fail?&lt;/li&gt;
&lt;li&gt;Why is &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; an enforcement gate only against handle creation and duplication, not against handle use?&lt;/li&gt;
&lt;li&gt;Why does the canonical MS15-090 OM-symlink CVE point at CVE-2015-2428 [@nvd-cve-2015-2428] rather than CVE-2015-2528 or CVE-2015-1463?&lt;/li&gt;
&lt;li&gt;What is the structural difference between &lt;code&gt;\??\C:&lt;/code&gt; and &lt;code&gt;\GLOBAL??\C:&lt;/code&gt;, and which one does the HackSys / CVE-2023-35359 worked example abuse?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Recommended reading.&lt;/strong&gt; Russinovich, Ionescu, and Solomon, &lt;em&gt;Windows Internals, Part 1&lt;/em&gt; (7th edition, Microsoft Press, 2017), Chapter 8 [@microsoftpressstore-wininternals7-part1]. James Forshaw, &lt;em&gt;Windows Security Internals&lt;/em&gt; (No Starch Press, 2024), Chapter 8 [@nostarch-windows-security-internals]. Alex Ionescu, &lt;em&gt;Battle of SKM and IUM&lt;/em&gt;, Black Hat USA 2015 [@ionescu-bh2015-pdf]. The Google Project Zero blog&apos;s symlink mitigations [@p0-symlink-mitigations], arbitrary directory creation [@p0-issue1550], and who contains the containers [@p0-who-contains-containers] posts. James P. Anderson, &lt;em&gt;Computer Security Technology Planning Study&lt;/em&gt; [@csrc-nist-ande72].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
</content:encoded><category>windows-internals</category><category>object-manager</category><category>kernel</category><category>sandbox</category><category>appcontainer</category><category>security-boundaries</category><category>edr</category><category>vbs</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>&quot;Who Is This Code?&quot; -- The Quiet 33-Year Reinvention of App Identity in Windows</title><link>https://paragmali.com/blog/windows-app-identity-33-year-reinvention/</link><guid isPermaLink="true">https://paragmali.com/blog/windows-app-identity-33-year-reinvention/</guid><description>NT 3.1 could prove which user typed at the keyboard but had no answer to which code was running. Eight successive primitives later, Windows is still answering the same question.</description><pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate><content:encoded>
Windows NT 3.1 (1993) could prove which **user** typed at the keyboard but had no answer to **which code was running**. Over the next thirty-three years, eight successive primitives -- Authenticode, Kernel-Mode Code Signing, Protected Process Light, AppContainer with the Package SID, App Control for Business, Mark of the Web with SmartScreen, the Vulnerable Driver Block List, and Pluton-rooted attestation -- accreted into a single layered code-identity stack. Each was forced into existence by a specific, named failure of the one before it. This is that story, told as one system.
&lt;h2&gt;Two identities, one operating system&lt;/h2&gt;
&lt;p&gt;On July 27, 1993 -- the day Windows NT 3.1 shipped -- the new operating system could prove with cryptographic precision who Alice was, which group she belonged to, which file she was allowed to open, and at what level of privilege she was running. It could prove exactly nothing about the program she had just double-clicked.&lt;/p&gt;
&lt;p&gt;Thirty-three years later, &quot;Alice&quot; has barely changed. The code she runs has acquired a publisher signature stamped onto its Portable Executable file, a kernel-loader gate that refuses to load unsigned drivers, a signer level in a runtime lattice that decides whether one process can read another&apos;s memory, a Package SID derived from a Crockford-Base32 hash of the manifest publisher [@ms-package-identity], a publisher-rule entry in a centrally managed App Control policy [@ms-appcontrol], a Mark-of-the-Web alternate data stream from the browser that downloaded it [@ms-fscc-motw], a SmartScreen reputation score [@learn-smartscreen], a possible entry on a Microsoft-curated denylist that overrides its own valid signature [@msft-driver-blocklist], and -- on a Pluton-equipped 2026 laptop -- a hardware-attested measurement of the boot chain that loaded it [@learn-pluton]. Every one of those identities was forced into existence by a specific failure of the one before. This is that story.&lt;/p&gt;
&lt;p&gt;A modern symptom makes the asymmetry concrete. In April 2026, attackers seized the publishing pipeline for the &lt;code&gt;@bitwarden/cli&lt;/code&gt; npm package -- a credential they had no business holding -- and shipped a backdoored release for ninety-three minutes before maintainers caught it [@bitwarden-statement]. Code identity, as it existed at every layer of every operating system that consumed that package, said the artifact was authentic. The signature was valid. The publisher&apos;s account was real. The package metadata was correct. Every check passed. &lt;em&gt;And the binary was hostile.&lt;/em&gt; That gap, between &quot;who shipped it&quot; and &quot;is it safe to run,&quot; is the same gap NT 3.1 first stepped over in 1993 and that Windows has been trying to close ever since.&lt;/p&gt;
&lt;p&gt;The Bitwarden case sits in a long company. Stuxnet&apos;s stolen Realtek and JMicron driver-signing keys (2010) [@symantec-stuxnet], Flame&apos;s MD5 collision against Microsoft&apos;s own intermediate CAs (2012) [@ms-2718704], the ASUS ShadowHammer pipeline compromise (operation 2018, disclosed 2019) [@securelist-shadowhammer], every &quot;Bring Your Own Vulnerable Driver&quot; rootkit since 2018 -- they all have the same shape. A valid Windows-anchored signature, on code the publisher did not intend to ship, on a machine that loaded it without complaint.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every Windows code-identity primitive introduced since 1996 was forced into existence by a specific failure of the layer before it. The article&apos;s spine is that cascade.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The pieces in 2026 are not a feature checklist. They are a layered system, each layer answering a question its predecessor structurally could not. If you read the Microsoft Learn pages one at a time you see eight unrelated products. If you read them in the order their failures forced them into existence, you see one operating system slowly learning to name the code it runs.&lt;/p&gt;

timeline
    title Windows code identity, 1993 to 2026
    1993 : NT 3.1 ships : user-only principal
    1996 : Authenticode : publisher signature on PE
    2002 : Trustworthy Computing memo : SDL forcing function
    2006 : Vista x64 KMCS : refusal of unsigned kernel code
    2010 : Stuxnet : stolen Realtek + JMicron keys
    2012 : AppContainer : per-app SID
    2012 : Flame : MD5 collision against MS CA
    2013 : Windows 8.1 PPL : signer level as runtime ACL
    2015 : Device Guard / WDAC : publisher policy
    2019 : ASUS ShadowHammer disclosed : compromised pipeline (2018 operation)
    2020 : Pluton announced : in-die security processor
    2022 : Driver Block List default-on : signed != trusted
    2024 : CrowdStrike outage : placement is identity
    2025 : MVI 3.0 user-mode preview : kernel/user split
&lt;p&gt;&lt;em&gt;Timeline sources, in row order (Mermaid syntax does not permit inline tokens inside the timeline block; each event is independently cited in the surrounding prose as well):&lt;/em&gt; 1993 NT 3.1 [@custer-inside-nt]; 1996 Authenticode [@ms-news-1996-authenticode]; 2002 Trustworthy Computing memo [@cnet-gates-memo] [@theregister-tcm]; 2006 Vista x64 KMCS [@ms-kmcs]; 2010 Stuxnet [@symantec-stuxnet]; 2012 AppContainer [@ms-package-identity]; 2012 Flame MD5 collision [@ms-2718704] [@msrc-2718704]; 2013 Windows 8.1 PPL [@ionescu-ppl] [@ms-protected-processes]; 2015 Device Guard / WDAC [@ms-appcontrol]; 2019 ASUS ShadowHammer disclosed (operation 2018) [@securelist-shadowhammer]; 2020 Pluton announced [@learn-pluton]; 2022 Driver Block List default-on [@msft-driver-blocklist]; 2024 CrowdStrike outage [@ms-crowdstrike-blog] [@msft-crowdstrike-best-practices]; 2025 MVI 3.0 user-mode preview [@weston-2024] [@weston-2025].&lt;/p&gt;
&lt;p&gt;If user identity was easy, why did code identity take thirty-three years -- and where exactly did each generation break?&lt;/p&gt;
&lt;h2&gt;Why code had no name&lt;/h2&gt;
&lt;p&gt;Helen Custer&apos;s 1992 &lt;em&gt;Inside Windows NT&lt;/em&gt; opens its security chapter on a single principle: the user is the principal [@custer-inside-nt]. Every action the kernel arbitrates is attributable to a user account. The token that the kernel manufactures at logon carries a Security Identifier (SID) for the user, SIDs for each group the user belongs to, a privilege bitmap, and a set of impersonation flags. Every Discretionary Access Control List on every securable object is evaluated against that token [@ms-sids]. The kernel never asks what binary is running. It asks who is running it.&lt;/p&gt;

A variable-length value that uniquely identifies a security principal in Windows. Users, groups, computer accounts, and (later) packages and capabilities all receive SIDs. Until Windows 8, every SID encoded a *user* or *group*; AppContainer and Package SIDs (the `S-1-15-2-...` form) extended SIDs to name code instead.
&lt;p&gt;For 1993&apos;s threat model, the user-as-principal model was defensible. NT 3.1 lived on multi-user workstations in a trusted local-area network. The attacker the designers worried about was a malicious insider, a contractor with the wrong group membership, an admin who exceeded his authority. Code arrived on floppies and CDs from coworkers and shrink-wrapped vendors; nobody downloaded executables off the public internet, because for most of the world there was no public internet to download them from.Integrity levels (Low, Medium, High, System) were added later, in Vista (2006), and they are still attributes of the &lt;em&gt;token&lt;/em&gt;, not of the binary on disk. A Low-integrity Internet Explorer process and a Low-integrity Notepad receive the same write restrictions because their tokens carry the same Mandatory Integrity Control label, regardless of which binary loaded.&lt;/p&gt;
&lt;p&gt;Then came Internet Explorer 3.0 in August 1996 and ActiveX. Microsoft repositioned OLE/COM as a cross-internet component model and committed to letting any compliant ActiveX control execute inside the browser [@ms-news-1996-authenticode]. The decision was not casually made; it was the strategic foundation of Microsoft&apos;s bet on the web. But its consequence at the security layer was immediate and devastating.&lt;/p&gt;
&lt;p&gt;If Alice double-clicks a control on a web page, the operating system&apos;s question is &quot;who is running this?&quot; The answer is &quot;Alice.&quot; She is allowed to run anything she wants. The control does whatever it likes -- with her token, her files, her privileges, her network access. The user-as-principal model has no second axis to invoke.&lt;/p&gt;
&lt;p&gt;There was no theoretical fix at this layer. Alice did genuinely request the download. She did genuinely double-click. NT had no other principal to consult. The model was complete, internally consistent, and exactly wrong for the new threat surface.&lt;/p&gt;
&lt;p&gt;What was missing was a cryptographic, network-portable identity for the code itself, attached to the binary in a way nobody downstream could forge. If the kernel cannot see the code, who can put a name on it -- and how do we attach that name to a running PE?&lt;/p&gt;
&lt;h2&gt;The first naive attempt: Authenticode (1996)&lt;/h2&gt;
&lt;p&gt;On August 7, 1996, Microsoft and VeriSign jointly announced the first cryptographic answer Windows had ever offered to &quot;who is this code?&quot; The press release ran twenty-two paragraphs and named every design choice that the next thirty years of Windows code identity would inherit: an X.509 certificate issued by an external commercial Certificate Authority, a PKCS#7 SignedData blob attached directly to the binary, and verification at download or install time by Internet Explorer 3.0 [@ms-news-1996-authenticode].&lt;/p&gt;

A cryptographic format for binding a publisher&apos;s identity and a tamper-evident hash to a Portable Executable. The signature is stored in the PE Attribute Certificate Table as a PKCS#7 SignedData structure containing an X.509 certificate chain and a hash that excludes the checksum field, the certificate-table directory entry, and the certificate table itself. Authenticode names the *publisher*, not the code; this is the founding constraint the rest of the article is forced to work around.

The new Microsoft Authenticode technology uniquely identifies the publisher of a piece of software and provides assurance to end users that it has not been tampered with or modified. -- Microsoft press release, August 7, 1996 [@ms-news-1996-authenticode]
&lt;p&gt;That sentence is the founding promise of Windows code identity. Read it once and the rest of the article becomes inevitable. Authenticode promises two things. It identifies the publisher. It detects tampering. It does not promise that the publisher is trustworthy, that the publisher&apos;s key is uncompromised, or that the bytes it covers are safe to execute. Three decades of failure modes follow from exactly that scoping.&lt;/p&gt;
&lt;p&gt;The mechanism is precise enough to demand a diagram. SignTool computes a hash that deliberately skips three regions of the PE: the checksum field (which the loader recomputes), the certificate-table directory entry, and the certificate table itself. The signature does not have to sign the bytes of its own embedding [@ms-pe-format].&lt;/p&gt;
&lt;p&gt;It then forms a PKCS#7 SignedData structure [@rfc-2315] containing the hash, an algorithm identifier, the X.509 chain, and an optional RFC 3161 timestamp. That blob is appended to the certificate table. At verify time, &lt;code&gt;WinVerifyTrust&lt;/code&gt; recomputes the hash, walks the chain to a trusted root, and (if a timestamp is present) honours signatures that were valid as of the timestamped time even if the issuer has since revoked the certificate [@ms-cryptotools].&lt;/p&gt;

sequenceDiagram
    participant Dev as Developer
    participant Sign as SignTool
    participant PE as PE binary
    participant Win as WinVerifyTrust
    participant CA as CA / chain store
    Dev-&amp;gt;&amp;gt;Sign: signtool sign /a app.exe
    Sign-&amp;gt;&amp;gt;PE: hash bytes (skip checksum + cert table)
    Sign-&amp;gt;&amp;gt;PE: build PKCS#7 SignedData
    Sign-&amp;gt;&amp;gt;PE: append RFC 3161 timestamp
    Sign-&amp;gt;&amp;gt;PE: write into Attribute Cert Table
    Note over Win: at install / download time
    Win-&amp;gt;&amp;gt;PE: re-hash same byte ranges
    Win-&amp;gt;&amp;gt;PE: extract PKCS#7 SignedData
    Win-&amp;gt;&amp;gt;CA: verify X.509 chain to trusted root
    CA--&amp;gt;&amp;gt;Win: chain ok
    Win--&amp;gt;&amp;gt;Win: trust verdict (advisory pre-Vista)
&lt;p&gt;Three structural failure modes shipped on day one and still ship in 2026.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Userland was advisory.&lt;/strong&gt; A signed &lt;code&gt;.exe&lt;/code&gt; ran. An unsigned &lt;code&gt;.exe&lt;/code&gt; also ran. Internet Explorer would prompt the user with a publisher name, but the prompt was a UI feature, not a kernel gate. The signature was a credential offered for inspection, never a wall the loader refused to cross. Closing this gap took ten years for kernel code (Authenticode 1996 [@ms-news-1996-authenticode] -&amp;gt; KMCS, Vista 2006 [@ms-kmcs]) and nineteen years for managed user-mode policy (Authenticode 1996 [@ms-news-1996-authenticode] -&amp;gt; Device Guard, 2015 [@ms-appcontrol]). Unmanaged consumer Windows in 2026 still permits arbitrary unsigned &lt;code&gt;.exe&lt;/code&gt; to run if the user clicks through SmartScreen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The signed hash did not cover the whole file.&lt;/strong&gt; This is CVE-2013-3900, disclosed by Microsoft on December 10, 2013 in security bulletin MS13-098 [@ms13-098]. The Authenticode hash skips the certificate-table region by design, and the verifier in &lt;code&gt;WinVerifyTrust&lt;/code&gt; did not constrain the size of the unsigned PKCS#7 blob. An attacker could append arbitrary unauthenticated bytes inside the &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; structure of an already-signed PE without invalidating the signature.&lt;/p&gt;
&lt;p&gt;The fix was a registry value, &lt;code&gt;EnableCertPaddingCheck=1&lt;/code&gt;, that turned on strict verification. Microsoft chose not to enable it by default. Twelve years later, the National Vulnerability Database still records the same scoping note: &quot;Microsoft does not plan to enforce the stricter verification behavior as a default functionality on supported releases of Microsoft Windows&quot; [@nvd-cve-2013-3900]. CISA added CVE-2013-3900 to its Known Exploited Vulnerabilities catalog on January 10, 2022 -- eight years after disclosure, because attackers were still abusing the unfixed default [@nvd-cve-2013-3900].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; CVE-2013-3900 is still default-off in 2026. On any Windows endpoint where strict signature verification matters, set &lt;code&gt;HKLM\Software\Microsoft\Cryptography\Wintrust\Config\EnableCertPaddingCheck=1&lt;/code&gt; (and the WOW6432Node mirror on 64-bit). Microsoft documents the change as opt-in by design [@msrc-cve-2013-3900].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Timestamped signatures survive revocation.&lt;/strong&gt; The trust evaluator in &lt;code&gt;WinVerifyTrust&lt;/code&gt; is told to trust signatures as of the timestamped instant, not as of now. Removing this property would invalidate large catalogs of legitimate, archived signed software whose signing certificates have since expired [@ms-cryptotools]. The same property is what let the Stuxnet drivers load on every Windows machine that received them, because Microsoft revoked the Realtek and JMicron certificates &lt;em&gt;after&lt;/em&gt; Stuxnet had already shipped.The architectural choice here is genuinely hard. Synchronous global revocation would break offline software install. Asynchronous revocation, the alternative Microsoft chose, lets pre-revocation signatures continue to verify forever. There is no third option inside the Authenticode design.&lt;/p&gt;
&lt;p&gt;Pull these three threads together and the first aha falls out. Authenticode names the &lt;em&gt;publisher&lt;/em&gt;, not the code. A signed binary is a credential, not a verdict. The signature proves the bytes came from a holder of the publisher&apos;s private key. It does not prove the publisher is trustworthy, that the publisher&apos;s key has not been stolen, or that the bytes are safe to execute. Every failure mode of the next twenty-five years lives in that gap.&lt;/p&gt;
&lt;p&gt;Six years of failure modes had to accumulate before Microsoft executive priorities caught up. On January 15, 2002, Bill Gates sent the &quot;Trustworthy Computing&quot; memo company-wide, declaring security a higher priority than features and freezing engineering work for security review across its Windows product line (with SDL processes later extended company-wide) [@cnet-gates-memo] [@theregister-tcm]. The memo did not specify a code-identity mechanism. It is in this story because every later code-identity primitive -- the Security Development Lifecycle&apos;s mandatory SignTool integration, the XP SP2 hardening pass that produced MOTW, and the Vista work that produced KMCS -- shipped under the executive cover the memo provided [@windows-internals-7e].&lt;/p&gt;
&lt;p&gt;If unsigned code still runs in userland, what makes us think the same primitive will work for a kernel driver -- where the wrong binary owns the operating system?&lt;/p&gt;
&lt;h2&gt;The first refusal: KMCS, EV, and the WHQL pipeline (Vista, 2006)&lt;/h2&gt;
&lt;p&gt;Vista x64 shipped in November 2006 as the first Windows release that &lt;em&gt;refuses to load unsigned kernel code&lt;/em&gt; [@ms-kmcs]. The refusal was uncompromising. The kernel loader and the Plug-and-Play manager call into &lt;code&gt;WinVerifyTrust&lt;/code&gt; for every driver image; if the chain does not terminate at one of a small set of Microsoft-trusted roots, &lt;code&gt;MmLoadSystemImage&lt;/code&gt; returns &lt;code&gt;STATUS_INVALID_IMAGE_HASH&lt;/code&gt; and the driver does not load.&lt;/p&gt;

The Vista-era policy that requires every kernel-mode driver to carry an Authenticode signature chained to a Microsoft-trusted root. From Windows 10 1607 onward (the August 2016 Anniversary Update), only drivers signed by Microsoft via the Hardware Developer Center are accepted on Secure-Boot systems; end-entity cross-signed certificates issued before July 29, 2015 are grandfathered for legacy devices [@ms-kmcs].
&lt;p&gt;The mechanism is a load-time gate. In 2026, Microsoft offers three signing tiers that all terminate at a Microsoft cross-signed cert: HLK-tested (the full Windows Hardware Lab Kit run, eligible for retail Windows Update distribution), attestation-signed (lighter-weight, EV cert plus Microsoft attestation key, no hardware testing), and preproduction (developer signing for pre-release Windows builds) [@learn-driver-signing-offerings] [@ms-attestation-signing]. Driver &lt;code&gt;.cat&lt;/code&gt; catalog files extend Authenticode coverage from a single PE to an entire driver package, including INF files and supporting executables [@learn-embedded-sig].&lt;/p&gt;
&lt;p&gt;EV certificates -- Extended Validation, with mandatory hardware-security-module key storage and audited issuance -- became the practical floor for kernel signing. The reason was not pedagogical. A Domain Validated Authenticode cert from a commodity CA in that era could be obtained cheaply, often with little more than a working email address. EV raised the cost and binding strength of the publisher claim by an order of magnitude.&lt;/p&gt;
&lt;p&gt;Then, on June 17, 2010, Sergey Ulasen of the Belarusian anti-virus vendor VirusBlokAda flagged a strange piece of malware on a customer machine in Iran. It had been signed [@wikipedia-stuxnet].&lt;/p&gt;
&lt;p&gt;The Stuxnet dropper carried two kernel drivers, &lt;code&gt;mrxnet.sys&lt;/code&gt; and &lt;code&gt;mrxcls.sys&lt;/code&gt;, signed with legitimate Authenticode certificates issued to Realtek Semiconductor and JMicron Technology -- two Taiwanese hardware vendors. Investigators concluded the private keys had been physically exfiltrated from the publishers&apos; Taiwanese offices. VeriSign revoked the Realtek certificate on July 16, 2010 (and the JMicron certificate shortly afterward); Microsoft issued advisories and pushed Windows CTL updates to propagate the revocation [@symantec-stuxnet]. While the certs were valid, Vista x64 KMCS happily loaded both drivers on every system it touched.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; KMCS verifies &lt;em&gt;who signed&lt;/em&gt;, never &lt;em&gt;whether the signed code is safe&lt;/em&gt;. Every kernel-mode-identity failure between 2010 and 2026 reduces to that single sentence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Stuxnet certificates were not anomalies. The same failure shape -- valid Microsoft-rooted signature, on code the publisher did not intend to ship, on a healthy KMCS-enforcing kernel -- replays at predictable intervals.&lt;/p&gt;

The Flame espionage toolkit produced a *forged* Microsoft-rooted certificate by exploiting an MD5 chosen-prefix collision against Microsoft&apos;s Terminal Services Licensing Service, which still issued MD5-hash code-signing certificates years after MD5&apos;s brokenness was known. Microsoft Security Advisory 2718704 revoked three of its own intermediate CAs and emergency-deployed a new Untrusted Certificate Store mechanism through Windows Update [@ms-2718704] [@msrc-2718704]. The episode forced Microsoft to deprecate MD5 in code signing and led directly to the curation infrastructure the Driver Block List uses today.
&lt;p&gt;ASUS ShadowHammer in 2018, disclosed by Kaspersky in 2019, added a third variant. The attackers did not steal an HSM-bound key. They compromised ASUS&apos;s signing pipeline and got their backdoor signed by ASUS&apos;s &lt;em&gt;production&lt;/em&gt; signing key in the normal course of a normal release, distributed through ASUS Live Update [@securelist-shadowhammer]. Kaspersky&apos;s analysis recorded &quot;trojanized updaters were signed with legitimate certificates (eg: &apos;ASUSTeK Computer Inc.&apos;)&quot; and that &quot;over 57,000 Kaspersky users have downloaded and installed the backdoored version of ASUS Live Update.&quot; The trust root, the chain, the cert -- all valid. The bytes -- attacker-controlled.&lt;/p&gt;
&lt;p&gt;KMCS verified that a driver was signed, not that it was safe. Signing alone was not enough. But what was?&lt;/p&gt;
&lt;h2&gt;The second refusal: identity as a runtime attribute (PPL, 2013)&lt;/h2&gt;
&lt;p&gt;Until October 17, 2013, code identity gated &lt;em&gt;whether&lt;/em&gt; code could load. Windows 8.1 quietly shipped a structural shift: code identity now also gated &lt;em&gt;what one running process could do to another&lt;/em&gt; [@ionescu-ppl]. Alex Ionescu, then CrowdStrike&apos;s founding Chief Architect and previously a co-author of &lt;em&gt;Windows Internals&lt;/em&gt;, was the first person to publish a detailed external map of the new mechanism. The lineage runs back to Vista&apos;s 2006 Protected Process model, originally introduced as a DRM container for protected media playback; PPL is the security-grade descendant of that primitive, repurposed seven years later as a general-purpose process-protection mechanism [@windows-internals-7e].&lt;/p&gt;

A protection attribute attached to running processes that mediates inter-process access checks above and beyond the user-token DACL. PPL processes carry a *signer level* (in increasing order, roughly: `Authenticode`, `CodeGen`, `Antimalware`, `Lsa`, `Windows`, `WinTcb`, `WinSystem`). A process can open `PROCESS_VM_READ`, `PROCESS_VM_WRITE`, or `CREATE_THREAD` rights against another protected process only if its own signer level is greater than or equal to the target&apos;s [@ionescu-ppl] [@ms-protected-processes].
&lt;p&gt;The mechanism lives in the kernel&apos;s &lt;code&gt;EPROCESS&lt;/code&gt; object. When process A opens process B, the kernel calls into &lt;code&gt;RtlTestProtectedAccess&lt;/code&gt; (and downstream &lt;code&gt;PsTestProtectedProcessIncompatibility&lt;/code&gt;) before any DACL evaluation [@scrt-ppl-bypass]. If A&apos;s signer level is below B&apos;s, sensitive access masks are silently stripped from the returned handle. The classic effect: an attacker running with a SYSTEM token, holding &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, calling &lt;code&gt;OpenProcess&lt;/code&gt; on LSASS, gets back a handle without &lt;code&gt;PROCESS_VM_READ&lt;/code&gt;. Mimikatz can no longer dump the LSASS process memory.&lt;/p&gt;
&lt;p&gt;The signer level itself is set by an Enhanced Key Usage extension on the Authenticode certificate Microsoft issues to the binary&apos;s publisher. Antimalware vendors receive a certificate carrying the &lt;code&gt;Antimalware&lt;/code&gt; EKU; only Microsoft-internal binaries carry &lt;code&gt;WinTcb&lt;/code&gt; [@itm4n-runasppl]. Identity, in this model, is an EKU OID baked into a Microsoft-issued Authenticode cert, attached to the binary, evaluated by the kernel at every cross-process access check.&lt;/p&gt;

flowchart TD
    A[WinSystem]
    B[WinTcb]
    C[Windows]
    D[Lsa]
    E[Antimalware]
    F[CodeGen]
    G[Authenticode]
    A --&amp;gt; B --&amp;gt; C --&amp;gt; D --&amp;gt; E --&amp;gt; F --&amp;gt; G
    H[&quot;Caller (signer level X)&quot;] -- &quot;OpenProcess(target T, signer Y)&quot; --&amp;gt; I{&quot;X &amp;gt;= Y ?&quot;}
    I -- yes --&amp;gt; J[&quot;full access mask&quot;]
    I -- no  --&amp;gt; K[&quot;VM_READ / VM_WRITE / CREATE_THREAD stripped&quot;]
&lt;p&gt;LSASS-as-PPL is the canonical demonstration of the mechanism in practice. Setting &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL=1&lt;/code&gt; causes the next boot&apos;s LSASS to start with &lt;code&gt;PsProtectedSignerLsa&lt;/code&gt;. From that moment, no process below the &lt;code&gt;Lsa&lt;/code&gt; signer level can read LSASS memory, regardless of the user account. Mimikatz still runs as code; its &lt;code&gt;OpenProcess(LSASS, PROCESS_VM_READ)&lt;/code&gt; call returns a handle with the read right stripped, and its memory dump fails with &lt;code&gt;STATUS_ACCESS_DENIED&lt;/code&gt; before it ever sees a credential blob [@itm4n-runasppl].The &lt;code&gt;RunAsPPL=1&lt;/code&gt; setting is mirrored into a UEFI variable on Secure Boot systems precisely so that an attacker with &lt;code&gt;HKLM\SYSTEM&lt;/code&gt; registry write but no firmware-level access cannot disable LSA Protection by editing the registry and rebooting. The UEFI mirror is checked before the registry value is read [@itm4n-runasppl].&lt;/p&gt;
&lt;p&gt;ELAM -- Early Launch Antimalware -- is the same idea applied to boot. An ELAM driver, signed with a Microsoft-issued antimalware certificate, runs before any third-party boot driver and gets to vote on which subsequent drivers are allowed to load [@learn-elam]. Signer level enters the boot chain at the earliest moment third-party code can enter the boot chain.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; PPL&apos;s invention is conceptual, not just mechanical. Code identity becomes a runtime ACL between two running processes, not merely a load-time gate. App Control, HVCI, and the Driver Block List all operate on this same conceptual frame: identity continuously evaluated, in context, while code is executing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PPL was, and is, the right idea. It is also incomplete in two ways that drove every subsequent layer.&lt;/p&gt;
&lt;p&gt;The first gap is BYOVD -- Bring Your Own Vulnerable Driver. A signed-but-vulnerable driver such as &lt;code&gt;RTCore64.sys&lt;/code&gt; (shipped with MSI Afterburner), &lt;code&gt;Capcom.sys&lt;/code&gt; (shipped with the &lt;em&gt;Street Fighter V&lt;/em&gt; anti-cheat), or &lt;code&gt;gdrv.sys&lt;/code&gt; (shipped with Gigabyte motherboard utilities) gives any local administrator arbitrary kernel read/write through an IOCTL. Because these drivers are validly KMCS-signed, they load. From kernel mode, the attacker simply zeroes the &lt;code&gt;Protection&lt;/code&gt; byte in the target process&apos;s &lt;code&gt;EPROCESS&lt;/code&gt; structure, and PPL evaporates. The signing chain is sound. The signer level is correctly evaluated. The mechanism that decides which kernel code is allowed to &lt;em&gt;exist&lt;/em&gt; -- not just to be signed -- is what fails.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PPL is bypassed not by attacking PPL itself but by editing &lt;code&gt;EPROCESS.Protection&lt;/code&gt; from kernel mode. That is exactly why the Driver Block List had to exist as a separate layer above KMCS [@msft-driver-blocklist].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The second gap is the user-mode side. PPLdump and PPLfault demonstrated that confused-deputy DLL loads inside higher-PPL services could be turned into an arbitrary memory read of LSASS. Microsoft eventually patched PPLdump in Windows 10 21H2 build 19044.1826, but the failure &lt;em&gt;class&lt;/em&gt; remains structural: trusting a higher-signer process to safely load DLLs from publisher-controlled paths is a foot-gun every time a new such service ships [@ppldump-github] [@scrt-ppl-bypass].&lt;/p&gt;
&lt;p&gt;If signer level is the principal for OS-internal processes, what is the principal for the next layer up -- the application?&lt;/p&gt;
&lt;h2&gt;The application becomes a principal: AppContainer and the Package SID&lt;/h2&gt;
&lt;p&gt;Two processes, same user, same machine. One can read the user&apos;s SSH private keys. The other cannot. Same token. Same DACLs on the file. Different verdict. That is the AppContainer promise [@ms-appcontainer-isolation], and to keep it the operating system needs a &lt;em&gt;cryptographic identity for the application itself&lt;/em&gt; -- something derived from the application, not from the user, that ACLs can name.&lt;/p&gt;
&lt;p&gt;Windows 8 shipped AppContainer in 2012. Internally it was called LowBox, the name surviving in the legacy documentation [@ms-appcontainer-legacy]. Windows 10 generalised the model into MSIX, the modern app-package format [@ms-msix].&lt;/p&gt;

AppContainer is a per-process sandbox that augments the user-token security check with an *AppContainer SID* (`S-1-15-2-...`) derived from the package identity of the running application. ACLs and capability claims (such as `internetClient` or `picturesLibrary`) are evaluated against this SID, not against the user. Two processes running as the same user can therefore receive different access verdicts because their AppContainer SIDs differ.
&lt;p&gt;The cryptographic move is in how the SID is built.&lt;/p&gt;

Every MSIX/APPX package is identified by a five-element tuple: `(Name, Version, Architecture, ResourceId, Publisher)` [@ms-package-identity]. The `Publisher` field is the X.509 subject Distinguished Name of the certificate that signed the package. A 13-character `PublisherId` is derived deterministically from the Publisher DN by Crockford-Base32 encoding the first 64 bits of a SHA-256 hash (per community reverse-engineering; Microsoft&apos;s public documentation does not confirm the specific algorithm). The *Package Family Name* is then `_`; the *AppContainer SID* is computed deterministically from the full identity tuple and slotted into the `S-1-15-2-...` namespace.
&lt;p&gt;The derivation is dense enough to deserve a worked example. &lt;code&gt;Microsoft Corporation&lt;/code&gt; plus the &lt;code&gt;Microsoft.WindowsCalculator&lt;/code&gt; package name yields &lt;code&gt;Microsoft.WindowsCalculator_8wekyb3d8bbwe&lt;/code&gt; -- the suffix is the Crockford-Base32 PublisherId of &lt;code&gt;Microsoft Corporation&lt;/code&gt;&apos;s subject DN [@ms-package-identity]. Every MSIX package whose Publisher DN matches will share that suffix; every package whose Publisher DN differs will have a different suffix; an attacker who does not hold the publisher&apos;s signing key cannot make a package masquerade as belonging to that publisher&apos;s family.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;async function publisherIdOf(publisherDN) {   const data = new TextEncoder().encode(publisherDN);   const digest = await crypto.subtle.digest(&apos;SHA-256&apos;, data);   const first8 = new Uint8Array(digest.slice(0, 8));   // Crockford Base32 alphabet (no I, L, O, U)   const alpha = &apos;0123456789abcdefghjkmnpqrstvwxyz&apos;;   let bits = 0n;   for (const b of first8) bits = (bits &amp;lt;&amp;lt; 8n) | BigInt(b);   let out = &apos;&apos;;   for (let i = 0; i &amp;lt; 13; i++) {     out = alpha[Number(bits &amp;amp; 31n)] + out;     bits &amp;gt;&amp;gt;= 5n;   }   return out; } const dn = &apos;CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US&apos;; publisherIdOf(dn).then(pid =&amp;gt; console.log(&apos;PFN suffix candidate:&apos;, pid)); console.log(&apos;Real PFN: Microsoft.WindowsCalculator_8wekyb3d8bbwe&apos;); console.log(&apos;Note: the real algorithm is documented in package-identity-overview; this snippet demonstrates the structure, not the exact hash.&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Capabilities sit at the same layer. When an MSIX manifest declares &lt;code&gt;&amp;lt;Capability Name=&quot;internetClient&quot; /&amp;gt;&lt;/code&gt;, the package is tagged at install time with a &lt;em&gt;capability SID&lt;/em&gt; of the form &lt;code&gt;S-1-15-3-1&lt;/code&gt;, and the Windows Filtering Platform evaluates outbound TCP connections against that SID, not against the user&apos;s [@p0-appcontainer]. Mandatory Integrity Control labels (Low/Medium/High) compose with the AppContainer SID rather than replacing it [@learn-mic]. A broker process running outside the AppContainer is the only path back to user-scoped resources, and the broker keys its trust decisions on the calling Package SID.&lt;/p&gt;

Windows Hello&apos;s biometric authentication broker is itself an MSIX-style protected service whose AppContainer-flavoured identity is the Package SID derived from its Microsoft-signed manifest. Other processes that want to ask Hello to verify a face or a fingerprint must talk to the broker, and the broker decides whether to honour the request based partly on the caller&apos;s package identity. The reason this matters is the same as the LSASS reason: the secret material the broker holds (the user&apos;s TPM-bound private key) needs a principal that an attacker holding a SYSTEM token cannot impersonate. User-SID equality is not enough. Package-SID equality is.
&lt;p&gt;The &lt;code&gt;8wekyb3d8bbwe&lt;/code&gt; suffix you see on Calculator, Edge, the Microsoft Store, and most other in-box apps is &lt;code&gt;Microsoft Corporation&lt;/code&gt;&apos;s PublisherId. Once you know what it is, you start seeing it everywhere -- it is the cryptographic fingerprint of &quot;Microsoft signed this package&quot; [@ms-package-identity].&lt;/p&gt;
&lt;p&gt;The aha is the same shape as the PPL aha but at the layer above. Two binaries running as the same user can be authorised differently because the Package SID is derived from the manifest publisher and the package cannot forge it. AppContainer is not a sandbox you opt into. It is a SID you have. Capability ACLs name that SID. The firewall keys on it. The MIC label composes with it. The broker checks it.&lt;/p&gt;
&lt;p&gt;The limits are also visible. AppContainer is opt-in for Win32 desktop apps that have not been packaged. Forshaw&apos;s 2021 Project Zero analysis of the AppContainer firewall identified loopback-exemption and namespace-isolation holes that Microsoft classified as WontFix [@p0-appcontainer]. Per-app sandbox identity solves the Modern-app problem; it does not solve the legacy Win32 problem. For that, the operating system needs a policy plane that names code in publisher vocabulary instead of path vocabulary.&lt;/p&gt;
&lt;p&gt;What does an enterprise admin do when the application refuses to be packaged at all?&lt;/p&gt;
&lt;h2&gt;The policy plane: AppLocker, App Control, and the publisher rule&lt;/h2&gt;
&lt;p&gt;Path-based whitelisting failed for the same reason path-based ACLs failed. Anything writeable can be planted. AppLocker, shipped in Windows 7 in 2009, still stays in the box for compatibility, but Microsoft&apos;s own documentation recommends App Control for Business -- the rebranded Windows Defender Application Control -- for new deployments [@ms-applocker] [@ms-appcontrol]. The change is not cosmetic. It is the difference between filename-as-identity and Authenticode-publisher-as-identity.&lt;/p&gt;

A Code Integrity policy mechanism that expresses allow and deny rules in Authenticode-publisher vocabulary. Policies are authored in XML, compiled to a binary `siPolicy.p7b`, and enforced by the Code Integrity engine at every PE load. With HVCI active, enforcement happens inside the Hyper-V-protected secure kernel, immune to a compromised NT kernel [@ms-appcontrol].
&lt;p&gt;The certificate-and-publisher rule levels run from strictest to broadest as &lt;code&gt;Hash &amp;gt; FileName &amp;gt; FilePath &amp;gt; FilePublisher &amp;gt; SignedVersion &amp;gt; LeafCertificate &amp;gt; Publisher &amp;gt; PcaCertificate&lt;/code&gt;, with a parallel WHQL-only family for kernel drivers ordered &lt;code&gt;WHQLFilePublisher &amp;gt; WHQLPublisher &amp;gt; WHQL&lt;/code&gt; [@ms-appcontrol]. &lt;code&gt;Hash&lt;/code&gt; is the strictest (this exact byte string); &lt;code&gt;PcaCertificate&lt;/code&gt; is the broadest signer-based level (anything signed under that intermediate CA). Microsoft documents &lt;code&gt;RootCertificate&lt;/code&gt; as not supported, and &lt;code&gt;FilePath&lt;/code&gt; -- available for user-mode binaries from Windows 10 1903 onward -- is path-based and so inherits the failure modes the publisher-rule model was designed to escape.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;LeafCertificate &amp;gt; Publisher&lt;/code&gt; adjacency is the subtle one. &lt;code&gt;LeafCertificate&lt;/code&gt; pins to one specific signing certificate, so a renewal under a new leaf cert no longer matches. &lt;code&gt;Publisher&lt;/code&gt; matches any certificate with the same PCA + leaf-CN combination, including future renewals. &lt;code&gt;LeafCertificate&lt;/code&gt; is the stricter of the two [@ms-appcontrol].&lt;/p&gt;
&lt;p&gt;The practical sweet spot is &lt;code&gt;FilePublisher&lt;/code&gt;. It binds an allow rule to the tuple &lt;code&gt;(certificate authority + leaf publisher CN + original filename + minimum version)&lt;/code&gt;. That tuple survives recompiles: a benign update from the same publisher under the same name, signed by the same key, with a higher version still passes. It does not survive tampering. Change the original filename in the resource section, change the publisher, change the leaf certificate, and the rule no longer matches.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy primitive&lt;/th&gt;
&lt;th&gt;Era&lt;/th&gt;
&lt;th&gt;Rule basis&lt;/th&gt;
&lt;th&gt;Kernel coverage&lt;/th&gt;
&lt;th&gt;Default state&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Software Restriction Policies (SRP)&lt;/td&gt;
&lt;td&gt;XP, 2001&lt;/td&gt;
&lt;td&gt;path / hash / certificate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;unmanaged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppLocker&lt;/td&gt;
&lt;td&gt;Windows 7 Enterprise, 2009&lt;/td&gt;
&lt;td&gt;path / publisher / hash&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WDAC (Device Guard)&lt;/td&gt;
&lt;td&gt;Windows 10, 2015&lt;/td&gt;
&lt;td&gt;publisher / file attributes / hash&lt;/td&gt;
&lt;td&gt;full (with &lt;a href=&quot;https://paragmali.com/blog/the-windows-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;HVCI&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Control for Business&lt;/td&gt;
&lt;td&gt;renamed 2023&lt;/td&gt;
&lt;td&gt;publisher / file attributes / hash&lt;/td&gt;
&lt;td&gt;full (with HVCI)&lt;/td&gt;
&lt;td&gt;off; on by default in S Mode and on Windows 11 SE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The Code Integrity engine evaluates an App Control policy on every PE load -- user mode and kernel mode alike. With HVCI active, the policy lives behind the Hyper-V security boundary; even an NT-kernel-level attacker with arbitrary memory write cannot edit it without breaking out of the virtualization layer [@ms-appcontrol]. Deny rules always win; an explicit deny can never be undone by any number of allows on the same binary.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Author every App Control policy in audit mode for at least one full reference-image cycle before promoting to enforce. Audit mode logs every load that &lt;em&gt;would have been&lt;/em&gt; blocked, into the &lt;code&gt;Microsoft-Windows-CodeIntegrity/Operational&lt;/code&gt; event channel, without breaking anything. The pre-deployment failure rate of strict policies on real fleets is high enough that audit mode is not optional [@ms-appcontrol].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;App Control inherits the same structural ceiling Authenticode put in place. &lt;code&gt;Allow Signer = Microsoft Windows&lt;/code&gt; admits the entire LOLBins inventory -- &lt;code&gt;regsvr32&lt;/code&gt;, &lt;code&gt;mshta&lt;/code&gt;, &lt;code&gt;installutil&lt;/code&gt;, &lt;code&gt;rundll32&lt;/code&gt;, every signed-by-Microsoft binary an attacker can call to execute arbitrary content. &lt;code&gt;Allow Signer = ASUSTeK&lt;/code&gt; would have admitted ShadowHammer (operation 2018, disclosed 2019), every byte of which carried a valid ASUS production signature [@securelist-shadowhammer]. The publisher-rule model is the right primitive for managed endpoints, and the LOLBins / supply-chain-attack failure modes are the structural ceiling on what the primitive can prove.&lt;/p&gt;
&lt;p&gt;PKI-rooted publisher policy still trusts the publisher&apos;s key custody. When the key is stolen or the binary is signed but malicious, what does the operating system fall back on?&lt;/p&gt;
&lt;h2&gt;Reputation as identity: Mark of the Web and SmartScreen&lt;/h2&gt;
&lt;p&gt;A novel binary, signed by a freshly issued EV cert, has zero history. PKI says yes. Reputation says: I have never seen this before -- run it past the user.&lt;/p&gt;

An NTFS alternate data stream named `Zone.Identifier` written by browsers, mail clients, and other downloaders to record the trust zone of a downloaded file. The stream contains an INI-style `[ZoneTransfer]` block with `ZoneId=3` for files from the public internet, plus optional `ReferrerUrl=` and `HostUrl=` fields. The protocol is documented in the MS-FSCC reference [@ms-fscc-motw]. SmartScreen, Office Protected View, and the Attachment Execution Service all read MOTW to gate behaviour on origin.
&lt;p&gt;MOTW is not an Authenticode replacement. It is a parallel, &lt;em&gt;origin-based&lt;/em&gt; identity: the binary&apos;s provenance, encoded as data the file system carries with it, separate from any signature. Origin is the input to SmartScreen. SmartScreen submits a hash of the binary together with publisher metadata to a Microsoft-hosted reputation service; if the service has not seen the binary before, or has not seen enough downloads to be confident, the user gets the familiar &quot;Windows protected your PC&quot; prompt that requires an explicit More info / Run anyway click [@learn-smartscreen].&lt;/p&gt;
&lt;p&gt;The pipeline is parallel to Authenticode and App Control, not a successor. PKI says &quot;this signature chains to a real publisher.&quot; Reputation says &quot;this hash has been observed N times in the last 30 days, with prevalence trending up; the publisher account is six years old; M of the downloads were from machines later flagged for malware.&quot; None of those signals are derivable from a signature.The Defender machine-learning pipeline that powers SmartScreen reputation is the deeper version of the same idea -- already covered in &lt;em&gt;The Defender&apos;s Dilemma&lt;/em&gt; sibling article, which traces the twenty-year arc from Defender&apos;s 0.5/6 AV-TEST score to its 100% MITRE detection rate. The reputation primitive sits on top of that ML pipeline.&lt;/p&gt;
&lt;p&gt;The bypass surface is now well-known. Container formats (ISO, IMG, VHD, 7z) historically did not propagate MOTW to files extracted from them, because their on-disk representation does not preserve alternate data streams. Phishing campaigns adapted: send the attacker&apos;s &lt;code&gt;.exe&lt;/code&gt; inside an &lt;code&gt;.iso&lt;/code&gt;, the user mounts the &lt;code&gt;.iso&lt;/code&gt;, double-clicks the &lt;code&gt;.exe&lt;/code&gt;, and SmartScreen sees a binary with no MOTW and offers no warning.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s response combined fixes -- VHD and ISO MOTW propagation shipped in the December 2022 cumulative update for Windows 11 22H2, MOTW-aware extraction in OneDrive and the new Windows Archive APIs -- with two attack-surface-reduction rules that gate execution on prevalence and trust independently of MOTW [@learn-asr-reference]. The most useful is rule &lt;code&gt;01443614-cd74-433a-b99e-2ecdc07bfc25&lt;/code&gt;, &quot;Block executable files from running unless they meet a prevalence, age, or trusted list criterion.&quot;&lt;/p&gt;
&lt;p&gt;Office is the most consequential consumer of MOTW. A Word, Excel, or PowerPoint file carrying a &lt;code&gt;ZoneId=3&lt;/code&gt; Mark of the Web opens in Protected View: read-only, in a sandboxed renderer, with macros and active content disabled, until the user clicks &quot;Enable Editing&quot; on the message bar [@learn-protected-view].&lt;/p&gt;
&lt;p&gt;The 2022 wave of HTML-smuggling and ISO-borne malware that bypassed SmartScreen still tripped over Protected View at the document layer, and the post-2022 macro-blocked-by-default change extended the same MOTW-gated logic from container files to embedded VBA. Origin is now an input to two parallel pipelines: SmartScreen&apos;s reputation check on the executable, and Office&apos;s read-only-until-confirmed gate on the document.&lt;/p&gt;
&lt;p&gt;The full ASR rule GUIDs are in the Defender for Endpoint reference. Memorise none of them; pin the page.&lt;/p&gt;
&lt;p&gt;A useful way to read the layered system at this point: Authenticode answered &quot;who shipped it?&quot; KMCS answered &quot;is the kernel allowed to load it?&quot; PPL answered &quot;is this running process allowed to touch that one?&quot; AppContainer answered &quot;what application is this?&quot; App Control answered &quot;does the enterprise honour this publisher?&quot; MOTW and SmartScreen answer the question PKI cannot: &quot;have we seen this before, and from where?&quot; When PKI identity is necessary but not sufficient, reputation closes the gap -- statistically, never absolutely.&lt;/p&gt;
&lt;p&gt;PKI says yes; reputation says unknown. What does the operating system do when Microsoft itself says &lt;em&gt;no&lt;/em&gt; to a signature it just minted?&lt;/p&gt;
&lt;h2&gt;The breakthrough: signed is not trusted (Driver Block List, 2022)&lt;/h2&gt;
&lt;p&gt;December 8, 2021. Microsoft launches the Vulnerable and Malicious Driver Reporting Center [@msft-driver-reporting]. The blog post enumerates the failure shape that drove it: drivers that &quot;map arbitrary kernel, physical, or device memory to user mode,&quot; drivers that &quot;provide access to storage that bypass Windows access control,&quot; drivers whose IOCTLs let a local admin become an arbitrary kernel writer. Every one of those drivers was signed. Every one of those signatures was valid. Every one of those binaries was loadable on a default Windows install.&lt;/p&gt;
&lt;p&gt;By the Windows 11 22H2 update in September 2022, the Vulnerable Driver Block List was enabled by default [@msft-driver-blocklist]. The mechanism is a Microsoft-curated &lt;code&gt;SiPolicy.p7b&lt;/code&gt; (the same WDAC binary policy format), distributed through Windows Update and Defender intelligence updates, enforced by the Code Integrity engine -- with HVCI when present -- at every driver load. The published rules deny drivers by publisher, original filename, and hash. Critically, &lt;em&gt;the publisher&apos;s signature is still valid&lt;/em&gt;. The Block List is an explicit Microsoft veto layered on top of a working PKI verdict.&lt;/p&gt;

The blocklist included in this article ... usually contains a more complete set of known vulnerable drivers than the version in the OS and delivered by Windows Update. -- Microsoft Learn, *Microsoft recommended driver block rules* [@msft-driver-blocklist]
&lt;p&gt;That sentence, in Microsoft&apos;s own documentation, is the breakthrough. Microsoft is openly admitting that the version of the list shipped with the operating system trails the curated reference list. Curation is now a continuous, asynchronous activity, distinct from signing. The list ships on a quarterly cadence. New BYOVD drivers ship faster than that. The LOLDrivers community catalogue tracks hundreds of vulnerable drivers, many of which are not (yet) on Microsoft&apos;s list [@loldrivers].&lt;/p&gt;
&lt;p&gt;The Block List has a write-time companion. ASR rule &lt;code&gt;56a863a9-875e-4185-98a7-b882c64b5ce5&lt;/code&gt;, &quot;Block abuse of exploited vulnerable signed drivers,&quot; prevents &lt;em&gt;writing&lt;/em&gt; a known-vulnerable driver to disk in the first place [@learn-asr-reference]. The defence is layered: the Block List denies load; the ASR rule denies install; together they form a curtain across the BYOVD attack class. Together they do not close the BYOVD class -- the catalogue is a list, the threat is a set, and the gap is structural.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A signature attests &lt;em&gt;who&lt;/em&gt;. A reputation score attests &lt;em&gt;unfamiliar versus seen-good&lt;/em&gt;. A block list attests &lt;em&gt;Microsoft has revoked trust at runtime, even though the signature still verifies&lt;/em&gt;. These are three distinct identity layers, and 2022 is the year all three were finally co-deployed by default on the same operating system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;Curated identity at runtime&quot; is the conceptual breakthrough. &quot;Quarterly cadence&quot; is its operational ceiling. The Driver Block List is a list, the BYOVD threat is a set, and the gap between them is the open problem the next layer (Pluton + attestation + faster curation pipelines) is being asked to close.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Driver Block List is the operational expression of a 25-year admission. After 1996&apos;s &quot;the new Microsoft Authenticode technology uniquely identifies the publisher,&quot; after Vista&apos;s &quot;we will refuse unsigned kernel drivers,&quot; after Windows 8.1&apos;s &quot;signer level mediates inter-process access,&quot; after Windows 10&apos;s &quot;App Control names policy in publisher vocabulary,&quot; Microsoft&apos;s December 2021 blog post said something different. It said: a signature is a publisher claim; trust is a different claim; we, Microsoft, will curate the second claim continuously, even when we ourselves issued the first one. Identity has become curated, not just verified.&lt;/p&gt;
&lt;p&gt;If even Microsoft can no longer trust a valid signature, where does trust ultimately have to live?&lt;/p&gt;
&lt;h2&gt;The 2026 stack and the hardware future&lt;/h2&gt;
&lt;p&gt;The eight primitives from the previous sections do not run in isolation. They compose. A modern Windows boot -- on a Pluton-equipped 2026 laptop running Windows 11 24H2 with HVCI on, App Control in enforce mode, Smart App Control on, and Microsoft Defender as the active anti-malware -- evaluates code identity continuously, top to bottom, from firmware through user mode.&lt;/p&gt;

flowchart LR
    A[&quot;UEFI Secure Boot&lt;br /&gt;firmware-rooted PKI&quot;] --&amp;gt; B[&quot;Pluton / TPM&lt;br /&gt;measured boot, PCRs&quot;]
    B --&amp;gt; C[&quot;KMCS&lt;br /&gt;chain-to-Microsoft&quot;]
    C --&amp;gt; D[&quot;Driver Block List&lt;br /&gt;Microsoft curated veto&quot;]
    D --&amp;gt; E[&quot;ELAM&lt;br /&gt;signer-level boot gate&quot;]
    E --&amp;gt; F[&quot;User-mode Authenticode&lt;br /&gt;publisher attribution&quot;]
    F --&amp;gt; G[&quot;PPL signer-level&lt;br /&gt;runtime ACL&quot;]
    G --&amp;gt; H[&quot;AppContainer + Package SID&lt;br /&gt;per-app principal&quot;]
    H --&amp;gt; I[&quot;App Control for Business&lt;br /&gt;publisher policy&quot;]
    I --&amp;gt; J[&quot;MOTW + SmartScreen&lt;br /&gt;origin + reputation&quot;]
    J --&amp;gt; K[&quot;Pluton attestation&lt;br /&gt;device-identity claim&quot;]
&lt;p&gt;The hardware root has shifted in five years. Pluton, announced on November 17, 2020 by Microsoft together with AMD, Intel, and Qualcomm, is a security processor integrated into the CPU die rather than a discrete TPM chip on the motherboard bus [@ms-pluton-blog]. AMD Ryzen 6000-series and later (including Ryzen AI), Intel Core Series 3, Core Ultra Series 3, and Core Ultra 200V, and Qualcomm Snapdragon 8cx Gen 3 and Snapdragon X Series ship Pluton as the on-die TPM. Pluton&apos;s firmware is updated through Windows Update -- not through OEM-controlled SPI flash patches -- and Microsoft started delivering Rust-based Pluton firmware on 2024 AMD and Intel systems, with broader rollout ongoing [@learn-pluton].&lt;/p&gt;
&lt;p&gt;The architectural significance is twofold. The trust root is no longer a chip with its bus exposed to a trace-and-sniff attacker. The firmware update path is now a Microsoft-controlled channel rather than thirty different OEM-controlled channels. The same hardware root is what &lt;a href=&quot;https://paragmali.com/blog/bitlocker-on-windows-architecture-attacks-and-the-limits-of-/&quot; rel=&quot;noopener&quot;&gt;BitLocker&lt;/a&gt; depends on when it seals the Volume Master Key to a &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;measured boot&lt;/a&gt; chain via TPM PCRs [@ms-bitlocker]. On Pluton, those PCR measurements live in-die rather than on a bus-exposed chip, and the sibling article &lt;em&gt;BitLocker on Windows&lt;/em&gt; traces what that buys and what it does not.&lt;/p&gt;

Apple Gatekeeper plus Notarization is a single-CA model. All Mac binaries that pass Gatekeeper are notarized by Apple, scanning happens server-side, and Apple&apos;s own notary signature is the trust root [@apple-gatekeeper]. Linux IMA-Appraisal expresses code identity as a per-host keyring of cryptographic measurements; the kernel evaluates a load against a policy stored in the same keyring [@linux-ima]. Android APK Signature Scheme v3 binds the APK to a per-app signing key with an explicit proof-of-rotation chain that lets a publisher rotate keys without breaking the app&apos;s identity [@apksigning-v3]. Windows is the only one of the four that accepts third-party CAs in user mode while reserving Microsoft roots for the kernel. The cost of pluralism is exactly the long tail of failure modes this article enumerates; the benefit is the freedom every Windows ISV has used since 1996 to ship without asking Microsoft&apos;s permission.
&lt;p&gt;Then came July 19, 2024.&lt;/p&gt;
&lt;p&gt;CrowdStrike&apos;s Falcon kernel driver loaded a malformed Channel File 291 update that triggered an out-of-bounds memory read inside &lt;code&gt;csagent.sys&lt;/code&gt; and raised an invalid page fault [@msft-crowdstrike-best-practices], bug-checking roughly 8.5 million Windows endpoints simultaneously [@ms-crowdstrike-blog]. The driver was correctly Microsoft-signed through the Hardware Developer Center attestation pipeline. Every code-identity layer in the stack -- KMCS, the cross-cert, the EV cert, the attestation key, even the Block List -- said yes. The thing that went wrong was not identity. It was that an identity-blessed driver, running in kernel mode, can fail in ways that take entire continents offline.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The CrowdStrike outage proves that a correctly-signed, attested kernel driver is still a planet-scale liability if its placement is wrong. Identity is not the only dimension. Where in the privilege hierarchy a binary runs is itself a dimension that signing cannot capture.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft&apos;s reaction was structural. On September 12, 2024, David Weston published the recap of the September 10 WESES summit Microsoft had hosted with its endpoint-security partners, committing to provide &quot;additional security capabilities outside of kernel mode&quot; so that EDR vendors could run their detection logic in user mode [@weston-2024].&lt;/p&gt;
&lt;p&gt;On June 26, 2025, the Windows Resiliency Initiative announced a private preview of the new endpoint security platform, scheduled for July 2025 delivery to selected MVI partners: Bitdefender, CrowdStrike, ESET, and SentinelOne [@weston-2025]. CrowdStrike&apos;s representative was Alex Ionescu, now its Chief Technology Innovation Officer -- the same Alex Ionescu whose 2013 Breakpoint talk publicly mapped PPL signer levels. The arc had closed in twelve years.&lt;/p&gt;
&lt;p&gt;MVI 3.0 -- the Microsoft Virus Initiative, version three -- adds Safe Deployment Practices as a contractual condition: staged rollouts, deployment rings, monitoring. The same playbook Microsoft itself follows for Windows updates after the 2024 outage [@msft-crowdstrike-best-practices].&lt;/p&gt;
&lt;p&gt;The conceptual move is the same one PPL made in 2013, projected one layer higher. Then: identity becomes a runtime ACL between processes. Now: identity-bound &lt;em&gt;placement&lt;/em&gt; (kernel mode versus user mode) becomes a trust dimension co-equal with identity-bound &lt;em&gt;signing&lt;/em&gt;. The question is no longer &quot;is this driver signed and on the allow list?&quot; The question is &quot;should code with this identity be running in this context at all?&quot;&lt;/p&gt;
&lt;p&gt;If even attested, signed, blessed kernel code can fail catastrophically, what could code identity in principle ever prove -- and what is provably out of reach?&lt;/p&gt;
&lt;h2&gt;Theoretical bounds and open problems&lt;/h2&gt;
&lt;p&gt;Two papers from the 1980s bound everything that followed.&lt;/p&gt;
&lt;p&gt;Fred Cohen&apos;s 1984 paper at IFIP-Sec, republished in &lt;em&gt;Computers &amp;amp; Security&lt;/em&gt; in 1987, proved that perfect virus detection is undecidable: there is no algorithm that, given an arbitrary program, can decide whether it is a virus [@cohen-1986]. Reputation systems are necessarily heuristic. The &quot;first 1,000 downloads&quot; gap -- the window where SmartScreen has not yet seen enough of a new binary to be confident -- is structural, not a tuning problem. You cannot close it by waiting harder.&lt;/p&gt;
&lt;p&gt;Ken Thompson&apos;s 1984 ACM Turing Award lecture, &quot;Reflections on Trusting Trust,&quot; made a different point about a different layer [@thompson-trusting-trust]. Thompson exhibited a compiler that, when used to build itself, inserted a backdoor into a target program; when used to build the compiler, propagated the backdoor invisibly to the next-generation binary. Signing what the compiler emitted never proved the compiler was unmodified. SLSA Level 3+ provenance, reproducible builds, hermetic build environments [@slsa-spec] push the bound back one level. They do not eliminate it.&lt;/p&gt;
&lt;p&gt;A third bound is Authenticode-specific. Asynchronous revocation, the property that lets pre-revocation timestamped signatures continue to verify forever, is the reason Stuxnet&apos;s drivers loaded after Realtek&apos;s certificate was revoked, and the reason every other stolen-key compromise has a window of cryptographic legitimacy [@symantec-stuxnet]. Synchronous global revocation would invalidate large catalogs of legitimate, archived, signed software whose signing certs have since expired. There is no fix inside the design.&lt;/p&gt;
&lt;p&gt;Pulled together, these bounds explain the persistent gap. Stolen-but-not-yet-revoked publisher keys are the same failure mode replayed three times in sixteen years: Stuxnet (2010, Realtek and JMicron), ASUS ShadowHammer (operation 2018, disclosed 2019, ASUSTeK production key), &lt;a href=&quot;https://paragmali.com/blog/when-your-password-manager-attacks-you-inside-the-bitwarden-/&quot; rel=&quot;noopener&quot;&gt;Bitwarden CLI&lt;/a&gt; (2026, npm publishing credential). The Pluton firmware-update pipeline is the most credible architectural response yet -- a Microsoft-controlled key-rotation channel that does not depend on OEM-side custody -- but it does not eliminate the class. It compresses the response window.&lt;/p&gt;
&lt;p&gt;The other open problem is identity for non-PE artifacts. The Authenticode hash and the WDAC publisher rule were designed for Portable Executable files; everything else gets uneven coverage. PowerShell &lt;code&gt;.ps1&lt;/code&gt; scripts can be signed and gated through Constrained Language Mode, which the runtime enters automatically when an AppLocker or App Control policy is in force [@learn-clm]. .NET assemblies have strong-name signatures, separate from Authenticode and explicitly not a security boundary; Microsoft&apos;s own documentation warns &quot;do not rely on strong names for security&quot; [@learn-strong-name].&lt;/p&gt;
&lt;p&gt;JIT-compiled code -- the most common shape of &quot;code&quot; in 2026 -- is signed only insofar as the JIT host is signed. The JIT itself produces unsigned bytes. Container images, WSL guests, AI model files, and (now) agent prompts all live outside the Authenticode universe entirely. Each is its own substrate, with its own emerging signing scheme, and the unification has not happened.&lt;/p&gt;
&lt;p&gt;$$\text{trust}_{2026}(\text{binary}) = \text{publisher}(\text{binary}) \land \text{provenance}(\text{build}) \land \text{placement}(\text{runtime}) \land \text{reputation}(\text{telemetry}) \land \neg \text{revoked}(\text{Microsoft})$$&lt;/p&gt;
&lt;p&gt;That conjunction is the 2026 verdict. None of its terms are sufficient on their own. Each was forced into existence by a failure of the term before. The arc from &quot;who launched this thread?&quot; in 1993 to that conjunction in 2026 is what thirty-three years of forced moves produced.&lt;/p&gt;
&lt;p&gt;What does the layered system look like in practice on a 2026 endpoint -- and what should an admin actually do?&lt;/p&gt;
&lt;h2&gt;Practical guide&lt;/h2&gt;
&lt;p&gt;Six concrete recommendations for a 2026 Windows fleet, each tied to a primary Microsoft Learn or MSRC source.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Windows 11 22H2 and later it is enabled by default. On Windows 10, Server, and downlevel Windows 11 builds, enable it explicitly through Settings &amp;gt; Privacy &amp;amp; security &amp;gt; Windows Security &amp;gt; Device security &amp;gt; Core isolation &amp;gt; Microsoft Vulnerable Driver Blocklist. HVCI must be on for full enforcement [@msft-driver-blocklist].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The published Microsoft baseline policies (&lt;code&gt;Default Windows&lt;/code&gt;, &lt;code&gt;Allow Microsoft&lt;/code&gt;, the Windows S Mode policy) are the right starting points. Run any custom policy in audit mode for a full reference-image cycle, mine the &lt;code&gt;Microsoft-Windows-CodeIntegrity/Operational&lt;/code&gt; event log for blocked loads, then promote to enforce. Pair with HVCI so the policy lives behind the secure-kernel boundary [@ms-appcontrol]. Deploy through Microsoft Intune (or your MDM of choice), Configuration Manager, or Group Policy -- App Control policy distribution is a first-class managed-endpoint scenario rather than a per-machine hand edit [@learn-appcontrol-deployment].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Secure Boot systems the value is mirrored into a UEFI variable, so registry-only attackers cannot turn it off. Verify with &lt;code&gt;Get-ItemProperty -Path &apos;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa&apos; -Name RunAsPPL&lt;/code&gt; and the corresponding &lt;code&gt;RunAsPPLBoot&lt;/code&gt; UEFI variable [@itm4n-runasppl].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; SmartScreen alone is bypassed by container-format MOTW stripping. Pair it with ASR rule &lt;code&gt;01443614-cd74-433a-b99e-2ecdc07bfc25&lt;/code&gt;, which gates execution on prevalence, age, or a trusted list, independently of MOTW [@learn-smartscreen] [@learn-asr-reference].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Package SID is a free identity for any internal app you ship as MSIX. ACL sensitive resources to it, declare capabilities explicitly in the manifest, and let the AppContainer SID enforce the ACL at the kernel boundary [@ms-package-identity] [@ms-msix].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Treat your code-signing key like a credential, not a build artifact. Rotate the EV cert, revoke the old one, notify customers, and -- if the binary already shipped -- request the offending hash on the Driver Block List or the ASR rule [@msft-driver-reporting]. The Bitwarden CLI 2026 incident took 93 minutes from release to containment, with rollback continuing for several hours afterward [@bitwarden-statement]; have the playbook ready before you need it.&lt;/p&gt;
&lt;/blockquote&gt;

```js
function loadDecision({ signed, signerLevel, motwed, onBlockList, allowedByAppControl, smartScreenVerdict }) {
  if (onBlockList) return &apos;BLOCK -- Microsoft veto, signature ignored&apos;;
  if (signed === false &amp;amp;&amp;amp; allowedByAppControl === false) return &apos;BLOCK -- unsigned, App Control denies&apos;;
  if (signerLevel === &apos;WinTcb&apos; || signerLevel === &apos;WinSystem&apos;) return &apos;LOAD -- protected process&apos;;
  if (allowedByAppControl === false) return &apos;BLOCK -- App Control deny&apos;;
  if (motwed &amp;amp;&amp;amp; smartScreenVerdict === &apos;unknown&apos;) return &apos;WARN -- SmartScreen, user gate&apos;;
  if (motwed &amp;amp;&amp;amp; smartScreenVerdict === &apos;malicious&apos;) return &apos;BLOCK -- SmartScreen&apos;;
  return &apos;LOAD&apos;;
}
console.log(loadDecision({
  signed: true, signerLevel: &apos;Authenticode&apos;,
  motwed: true, onBlockList: false,
  allowedByAppControl: true, smartScreenVerdict: &apos;good&apos;,
}));
```
The decision tree is the practical mental model. Every branch of it is the consequence of one of the failures this article tracks.

No. A signature attests *publisher identity* and *binary integrity*. It does not attest safety. Microsoft trust is a separate, runtime claim expressed through the Driver Block List, App Control policies, and Defender reputation -- evaluated continuously, even on signatures Microsoft itself once minted [@msft-driver-blocklist].

Extended Validation Authenticode signing vets organisational identity through an audited issuance process and mandates that the private key live in a hardware security module; the publisher&apos;s signature is the trust root. Attestation signing is Microsoft&apos;s lighter-weight pipeline for kernel drivers: the publisher submits an EV-signed binary to the Hardware Developer Center, Microsoft re-signs with its own attestation key, and the result is delivered back. Attestation-signed drivers are not WHQL tested and are not distributed via retail Windows Update [@learn-driver-signing-offerings] [@ms-attestation-signing].

MOTW plus low prevalence. SmartScreen sees a binary it has not observed enough times in the global telemetry to be confident, on a file marked as having been downloaded from the internet. Sign the binary with an EV certificate, accumulate downloads on a stable hash, and the warning fades. Internal binaries can have MOTW stripped at deployment time if your distribution channel is itself trusted [@learn-smartscreen].

No. AppLocker is the Windows 7-era policy mechanism with rules in path/publisher/hash form, no kernel coverage, and no virtualization-based protection of the policy itself. App Control for Business -- formerly Windows Defender Application Control -- is the publisher-rule Code Integrity policy mechanism with HVCI enforcement at the kernel boundary. Microsoft recommends App Control for new deployments and keeps AppLocker for compatibility [@ms-applocker] [@ms-appcontrol].

LSASS is running as a Protected Process Light at the `Lsa` signer level. Signer-level gating sits *above* the token DACL check. Even a SYSTEM-token caller with `SeDebugPrivilege` gets a process handle with `PROCESS_VM_READ` and `PROCESS_VM_WRITE` stripped, because PPL strips access masks before the DACL evaluation. Disable LSA Protection (`RunAsPPL=0`) on a test machine and the same call succeeds [@itm4n-runasppl] [@scrt-ppl-bypass].

Only if the publisher&apos;s signing-key custody and build pipeline are themselves uncompromised. Stuxnet (stolen Realtek and JMicron keys, 2010), ASUS ShadowHammer (compromised production signing pipeline, operation 2018 / disclosed 2019), and the Bitwarden CLI npm incident (2026) all produced cryptographically valid signatures on attacker-controlled bytes [@symantec-stuxnet] [@securelist-shadowhammer] [@bitwarden-statement]. SLSA-level build provenance and Pluton-rooted attestation are the architectural responses; neither is yet universally deployed [@slsa-spec] [@learn-pluton].
&lt;h2&gt;Where this is going&lt;/h2&gt;
&lt;p&gt;Pluton-rooted device attestation, MVI 3.0&apos;s user-mode security platform, SLSA build provenance, and the post-CrowdStrike push to make placement a first-class identity attribute are all in motion in 2026 [@weston-2025] [@slsa-spec]. The follow-on articles -- Driver Block List in production, App Control with HVCI on real fleets, Secure Boot internals, the Pluton firmware-update channel -- are the operational complement to the conceptual story this article has told.&lt;/p&gt;
&lt;p&gt;The arc that began with Windows NT 3.1 having no answer to &quot;who is this code?&quot; now has eight overlapping answers, each insufficient on its own. Identity in 2026 is a multi-layered claim about a binary&apos;s publisher, its build provenance, its runtime placement, and its reputation, evaluated continuously while the code is running. The arc from 1993&apos;s &quot;who launched this thread?&quot; to 2026&apos;s &quot;is this signed binary, in this placement, with this build provenance, on Microsoft&apos;s curated honour list, today, on this hardware-attested device?&quot; is the answer thirty-three years of forced moves produced -- and the question the next thirty-three years will keep asking, because none of the bounds Cohen and Thompson proved have moved.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;app-identity-in-windows&quot; keyTerms={[
  { term: &quot;Authenticode&quot;, definition: &quot;PE-attached PKCS#7 SignedData that names the publisher and detects tampering. Names the publisher, not the code.&quot; },
  { term: &quot;Kernel-Mode Code Signing (KMCS)&quot;, definition: &quot;Vista x64 policy that refuses to load unsigned kernel drivers; chain-to-Microsoft requirement post-2015.&quot; },
  { term: &quot;Protected Process Light (PPL)&quot;, definition: &quot;Windows 8.1 attribute that mediates inter-process access by signer level; LSASS-as-PPL defeats user-mode credential dumpers.&quot; },
  { term: &quot;Package SID&quot;, definition: &quot;Cryptographic application identity (S-1-15-2-...) derived from the MSIX manifest publisher; first-class principal in ACLs and capability checks.&quot; },
  { term: &quot;App Control for Business&quot;, definition: &quot;Publisher-rule Code Integrity policy formerly called WDAC; enforced by HVCI; ships in S Mode and Windows 11 SE by default.&quot; },
  { term: &quot;Mark of the Web (MOTW)&quot;, definition: &quot;Zone.Identifier alternate data stream that records a file&apos;s origin; input to SmartScreen reputation.&quot; },
  { term: &quot;Vulnerable Driver Block List&quot;, definition: &quot;Microsoft-curated WDAC-format deny list shipped quarterly; default-on since Windows 11 22H2; the operational expression of &apos;signed != trusted&apos;.&quot; },
  { term: &quot;Pluton&quot;, definition: &quot;On-die Microsoft security processor in AMD Ryzen 6000+, Intel Core Ultra 200V, and Qualcomm 8cx Gen 3; firmware updated through Windows Update.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>authenticode</category><category>code-signing</category><category>protected-process-light</category><category>appcontainer</category><category>app-control</category><category>driver-blocklist</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>