<?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: identity</title><description>Posts tagged identity.</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/identity/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Twenty-Year Local Admin Password Crisis: From GPP cpassword to Windows LAPS</title><link>https://paragmali.com/blog/the-twenty-year-local-admin-password-crisis-from-gpp-cpasswo/</link><guid isPermaLink="true">https://paragmali.com/blog/the-twenty-year-local-admin-password-crisis-from-gpp-cpasswo/</guid><description>Microsoft published the AES key that &quot;protected&quot; Group Policy Preferences passwords. Twelve years later, MS14-025 still has not deleted the artefacts. Here is how Windows LAPS finally fixed the architecture -- and what it still cannot solve.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>
**Eleven years separated Microsoft&apos;s December 2012 architectural articulation of the shared-local-admin problem from the April 11, 2023 in-box default.** Group Policy Preferences &quot;encrypted&quot; the local Administrator password with an AES key Microsoft published in its own protocol specification (2008-2014). MS14-025 disabled new authoring but deleted no SYSVOL artefacts (2014). Legacy LAPS shipped as a separate MSI with plaintext in `ms-Mcs-AdmPwd` (2015-2023). In-box Windows LAPS finally added CNG DPAPI encryption-at-rest, Microsoft Entra ID backup, and post-authentication rotation. The 2026 default is `BackupDirectory = 2` (AD) or `1` (Entra), `PasswordAgeDays` \&amp;lt;= 30, `ADPasswordEncryptionEnabled` left at its default `True` (the failure mode is silent fallback to plaintext when the domain functional level is below Windows Server 2016, not an off-by-default bit), `ADPasswordEncryptionPrincipal` overridden to a dedicated decryptor group, and `PostAuthenticationActions` left at default `3` (reset + sign out). The residual attack surface is delegated-decryptor compromise, the screenshotted-password OPSEC tail, unmanaged BYOD endpoints, and the multi-decade tail of un-cleaned SYSVOL `cpassword` XMLs that MS14-025 never deleted.
&lt;h2&gt;1. One Password, Fifty Thousand Laptops&lt;/h2&gt;
&lt;p&gt;In May 2012, a domain user with twelve lines of PowerShell could read the local Administrator password for every machine in the organisation. The tool was &lt;code&gt;Get-GPPPassword.ps1&lt;/code&gt; [@obscuresec-gpp-2012]. The &quot;encryption&quot; was AES-256-CBC with a 32-byte key Microsoft had published in its own protocol specification [@ms-gppref-aes-key] -- not leaked, &lt;em&gt;published&lt;/em&gt;, as a feature, so that third-party Group Policy implementations could read the format. Eleven years later, on April 11, 2023, Microsoft finally shipped the in-box fix [@tc-windows-laps-ga-2023].&lt;/p&gt;
&lt;p&gt;This is an article about those eleven years.&lt;/p&gt;

A lateral-movement technique in which an attacker uses the NTLM hash of a captured password directly in an authentication exchange, without recovering the cleartext. If the same local Administrator password is reused across a fleet, one dumped hash unlocks every machine. MITRE catalogues the technique as **T1550.002**.
&lt;p&gt;The pattern was old before 2012. Through the 2000s, the only practical way to provision the local Administrator account on a Windows fleet was to bake one shared password into the reference image and ship the image to every endpoint. Helpdesk knew the password. Pentesters guessed at it. And once Benjamin Delpy&apos;s Mimikatz had pulled the hash from a single phished workstation in 2011, the rest of the org fell to a single &lt;code&gt;psexec&lt;/code&gt; spray. Microsoft documented the threat model precisely in its December 2012 &lt;em&gt;Mitigating Pass-the-Hash&lt;/em&gt; whitepaper [@ms-pth-whitepaper], which named the shared local Administrator credential as the architectural enabler of the entire intrusion class [@mitre-t1550-002].&lt;/p&gt;
&lt;p&gt;Microsoft also had a &lt;em&gt;fix&lt;/em&gt;. It had shipped one in 2008 with Group Policy Preferences (GPP), the feature that could push a per-machine local-admin password from a Group Policy Object to every endpoint. GPP put the password in an XML file in SYSVOL. SYSVOL was world-readable to every authenticated user in the domain. Microsoft encrypted the password with AES-256-CBC -- and then published the key. The result, after a four-author weaponisation chain in mid-2012 [@sogeti-2012-wayback; @obscuresec-gpp-2012; @rewtdance-gpp-2012; @metasploit-gpp], was that GPP made the original problem &lt;em&gt;worse&lt;/em&gt;: instead of one shared password recoverable by physical access to a help-desk laptop, it was now one shared password recoverable by any authenticated domain user with a copy of &lt;code&gt;Get-GPPPassword.ps1&lt;/code&gt;. Microsoft &quot;patched&quot; it on May 13, 2014 with MS14-025 [@ms14-025-bulletin], which disabled new authoring but deleted nothing already deployed. Twelve years later, PingCastle still finds the artefacts in production AD [@pingcastle-rules].&lt;/p&gt;
&lt;p&gt;The first real fix was Generation 2: the legacy Microsoft LAPS, shipped May 1, 2015 as a separate MSI [@ms-advisory-3062591-wayback]. It stored a per-machine random password in the &lt;code&gt;ms-Mcs-AdmPwd&lt;/code&gt; attribute on the computer object, marked CONFIDENTIAL [@adsec-laps-2016]. The directory-side ACL was tighter than SYSVOL, but the deployment surface (install on every endpoint, extend the schema, delegate the OU) capped its real coverage; the password sat in plaintext in AD, one DCSync from &quot;plaintext everywhere&quot;; and a delegation pattern that helpdesks regularly issued -- &quot;All Extended Rights&quot; on the computer OU -- silently included read access to the CONFIDENTIAL attribute [@adsec-laps-2016]. SpecterOps modelled that bypass as the &lt;code&gt;ReadLAPSPassword&lt;/code&gt; BloodHound edge on August 7, 2018 [@specterops-bh2].&lt;/p&gt;
&lt;p&gt;Generation 3 -- Windows LAPS, in-box, no MSI -- shipped on Patch Tuesday April 11, 2023 [@tc-windows-laps-ga-2023] across Windows 11 22H2 and 21H2, Windows 10 22H2, Windows Server 2022, Windows Server 2019, and Windows Server Annual Channel. Windows Server 2016 was explicitly excluded [@ms-laps-overview]. The new architecture wrapped the password with CNG DPAPI&apos;s group key-protector against a configurable principal, exposed Microsoft Entra ID as a peer backup directory [@tc-entra-laps-ga-2023], and added a post-authentication rotation primitive that closed the screenshotted-password OPSEC tail on the &lt;em&gt;next&lt;/em&gt; managed-account logon [@ms-laps-policy-settings].&lt;/p&gt;
&lt;p&gt;The local Administrator account always has the well-known &lt;strong&gt;relative identifier (RID) 500&lt;/strong&gt; in the machine&apos;s SAM, irrespective of any administrative renaming. Renaming the account at the friendly-name level does not change its SID, which is why Windows LAPS resolves the target account by SID and not by name -- and why an empty &lt;code&gt;AdministratorAccountName&lt;/code&gt; policy still finds the right account even on a renamed-built-in host.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Microsoft knew the right architecture for managing local Administrator passwords in December 2012, when its own Pass-the-Hash whitepaper named the shared-credential pattern as the architectural enabler of lateral movement. It took until April 11, 2023 to ship that architecture as a Windows default. Eleven years is a long time. The intervening generations each solved part of the previous problem and introduced a new one. The 2026 baseline is, for the first time, an OS-default solution rather than an out-of-band one -- and for the first time, the residual attack surface is the actual surface rather than an artefact of incomplete shipping.&lt;/p&gt;
&lt;/blockquote&gt;

gantt
    dateFormat YYYY-MM-DD
    axisFormat %Y
    title Local-administrator password management on Windows, 1998-2026&lt;pre&gt;&lt;code&gt;section Generation 0 -- Imaged-build era
Shared local admin password baked into image          :gen0, 1998-01-01, 2008-02-26

section Generation 1 -- GPP cpassword
Group Policy Preferences ships in WS2008 RTM           :g1a, 2008-02-27, 2014-05-12
Linda Moore re-posts &quot;Passwords in GPP (Updated)&quot;     :milestone, 2009-04-22, 1d
Sogeti / obscuresec / rewtdance / Metasploit chain    :crit, 2012-04-01, 2012-07-31
MS PtH whitepaper v1 (architecture articulated)       :milestone, 2012-12-01, 1d
MS14-025 disables new authoring (no remediation)      :milestone, 2014-05-13, 1d

section Generation 2 -- Legacy MSI LAPS
Microsoft LAPS GA (KB3062591 MSI)                      :g2a, 2015-05-01, 2023-04-10
Metcalf publishes All-Extended-Rights bypass           :milestone, 2016-08-01, 1d
SpecterOps BloodHound 2.0 ships ReadLAPSPassword edge :milestone, 2018-08-07, 1d

section Generation 3 -- In-box Windows LAPS
Windows LAPS ships in-box (AD backup)                  :crit, 2023-04-11, 2026-12-31
Windows LAPS with Entra ID GA                          :milestone, 2023-10-23, 1d
Win 11 24H2 passphrases and Automatic Account Mgmt    :milestone, 2024-10-01, 1d
Win 11 25H2 Administrator Protection (orthogonal)     :milestone, 2025-11-19, 1d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The article that follows traces the architecture of each generation, the attacks each one solved and each one enabled, and what &quot;standard local admin password management&quot; looks like as a 2026 default. To see why this took twenty years, we have to start in 1998, before Active Directory.&lt;/p&gt;
&lt;h2&gt;2. Origins: Why Every Workstation Had the Same Local-Admin Password (1998-2008)&lt;/h2&gt;
&lt;p&gt;Picture a system administrator in 2005. They are holding a CD-R labelled &lt;code&gt;Win-Build-7.iso&lt;/code&gt; and a sticky note with a 12-character password. Those two artefacts are the entire local-Administrator-credential lifecycle for ten thousand desktops. The CD will be cloned to a USB drive, the USB drive will reseed Norton Ghost, and Ghost will paint the build onto every new workstation the company buys for the next eight months. Each painted machine will boot with the sticky-note password as its built-in local Administrator. Helpdesk knows the password because they typed it into the image. Five hundred field technicians know the password because they have to be able to recover unmanaged laptops off-network. The pentester who shows up in March will know the password by Tuesday lunch.&lt;/p&gt;
&lt;p&gt;This was not a deviation. It was the architecture.&lt;/p&gt;

Every Windows machine ships with a built-in local Administrator account whose security identifier ends in the **relative identifier 500**. The RID is constant across machines, languages, and SKUs. Renaming the account changes the friendly name but not the RID, so identity-aware tooling (including Windows LAPS) resolves the account by SID rather than by name. Disabling the account is a configuration choice, not a deletion: the account remains in SAM and can be re-enabled at any time.
&lt;p&gt;The mechanics were a function of how Windows was deployed at scale. Microsoft Sysprep &lt;code&gt;/generalize&lt;/code&gt; strips a reference image&apos;s machine SID before duplication, but it leaves the SAM intact. Whatever local Administrator password sits in the reference image is the local Administrator password on every endpoint painted from that image. Imaging pipelines were built around this: Norton Ghost in the late 1990s, Microsoft Deployment Toolkit (MDT) and later System Center Configuration Manager Operating System Deployment in the 2000s, all assumed the same SAM. Sean Metcalf&apos;s December 2015 SYSVOL retrospective walks the era end-to-end and explains why every shop in the world ended up with a single password [@adsec-gpp-2015].&lt;/p&gt;
&lt;p&gt;The operational reality kept the pattern alive. Help-desk needed &lt;em&gt;one&lt;/em&gt; known credential to break-glass a laptop that had wandered off the corporate network for six months. Field technicians needed &lt;em&gt;one&lt;/em&gt; known credential to swap a failed hard drive on a roof-top kiosk in Houston without phoning home. A known-to-the-org local-admin password was the only realistic fallback path, and the alternative -- a different password per machine, stored somewhere retrievable -- required a &lt;em&gt;retrieval&lt;/em&gt; primitive Microsoft had not yet shipped.&lt;/p&gt;
&lt;p&gt;The threat model that made the trade-off catastrophic did not get articulated by Microsoft itself until December 2012, in version 1 of the Pass-the-Hash whitepaper [@ms-pth-whitepaper]. The chain was already common knowledge in offensive-security circles: phish a single user, run Benjamin Delpy&apos;s 2011-vintage Mimikatz to pull credentials from LSASS, capture the NT hash of the built-in &lt;code&gt;Administrator&lt;/code&gt; account, replay that hash to every other host via &lt;code&gt;psexec&lt;/code&gt; or &lt;code&gt;wmiexec&lt;/code&gt;, and pivot up to the first server an enterprise admin has touched. MITRE catalogues the default-account abuse as &lt;strong&gt;T1078.001&lt;/strong&gt; [@mitre-t1078-001] and the hash-replay step as &lt;strong&gt;T1550.002&lt;/strong&gt; [@mitre-t1550-002]. The whitepaper&apos;s recommended controls included exactly the architecture Microsoft would eventually ship as LAPS: per-machine random local-admin passwords, rotated frequently, retrievable only by an authorised principal.&lt;/p&gt;

The hard part was never the cryptography. It was the operations. A pre-2008 sysadmin who proposed &quot;let&apos;s give every workstation a random local-Administrator password&quot; was correctly told that the answer required, at minimum, a directory-scoped retrieval primitive that did not exist; an ACL model that could distinguish &quot;help-desk can read this for their own OU&quot; from &quot;any authenticated user can read this for the whole forest&quot;; and a rotation pipeline that did not depend on the workstation being on the corporate network. Microsoft would not ship those primitives until 2008 (GPP, badly), 2015 (legacy LAPS, well), and 2023 (Windows LAPS, with encryption-at-rest). Until then, &quot;do not get compromised&quot; was the entire mitigation.
&lt;p&gt;The third-party prehistory matters because it set the terms Microsoft would eventually use. PolicyMaker, the engineering parent of what became Group Policy Preferences, was a product of DesktopStandard Corporation that Microsoft acquired in October 2006 [@adsec-gpp-2015]. Thycotic was founded in 1996 by Jonathan Cogley and shipped its Secret Server vault from the mid-2000s [@kuppingercole-cogley]; Lieberman Software (later acquired by Bomgar in January 2018) had operated as Lieberman and Associates since 1978 [@wikipedia-lieberman]; Quest Software was founded in 1987 in Newport Beach, California and was a public company well before the mid-2000s LAPS prehistory began -- its August 14, 1999 NASDAQ IPO saw its shares surge to $47 in a single Wall Street session [@wikipedia-quest; @latimes-quest-ipo-1999]. None of those vendors solved the local-admin-on-every-Windows-machine problem from inside the OS, and Microsoft&apos;s own first-party tooling -- restricted groups, logon scripts, Group Policy Object security templates -- offered no rotation primitive at all. The gap was not a knowledge gap; it was a &lt;em&gt;first-party-feature&lt;/em&gt; gap.&lt;/p&gt;
&lt;p&gt;In February 2008, Microsoft shipped Windows Server 2008. With it came Group Policy Preferences -- and with GPP came a &quot;Local Users and Groups&quot; preference that could push a per-machine local-admin password from a domain GPO to every endpoint in scope. It was the first first-party rotation mechanism Microsoft had ever shipped. It made the problem dramatically worse.&lt;/p&gt;
&lt;h2&gt;3. Decoration Is Not Encryption: GPP cpassword (2008-2012)&lt;/h2&gt;
&lt;p&gt;Microsoft Server 2008 reached release-to-manufacturing in February 2008. Group Policy Preferences shipped with it. The new &quot;Local Users and Groups&quot; preference -- alongside Scheduled Tasks, Services, Data Sources, Drive Maps, and Printers -- could push a password from a GPO down to every endpoint in scope. The password went into an XML file in SYSVOL, the domain&apos;s replicated policy share. SYSVOL was world-readable to every authenticated user in the domain. The password was AES-256-CBC encrypted in the XML, in a field called &lt;code&gt;cpassword&lt;/code&gt;. The key was a 32-byte value published in &lt;code&gt;[MS-GPPREF]&lt;/code&gt; section 2.2.1.1.4 [@ms-gppref-aes-key], in Microsoft&apos;s own Open Specifications protocol corpus -- &lt;em&gt;as a feature&lt;/em&gt;, so that third-party Group Policy implementations could interoperate.&lt;/p&gt;

A file share replicated to every Domain Controller in an Active Directory domain, used to distribute Group Policy templates and logon scripts. The default share permissions allow **Read** access to every Authenticated User in the forest. Any file placed in SYSVOL is, operationally, readable by every domain user.

The XML attribute defined by `[MS-GPPREF]` that carries an encrypted password inside a Group Policy Preferences item. The encryption is AES-256-CBC with a 16-byte zero IV and a static 32-byte key published in the same protocol specification. The name is short for &quot;ciphertext password&quot; and was the canonical search term for finding deployed credentials in SYSVOL between 2012 and 2026.

A loadable component on each Windows endpoint that processes one class of Group Policy setting. Each preference type (Local Users and Groups, Scheduled Tasks, Services, etc.) is implemented by its own CSE, which runs during the Group Policy refresh cycle. CSEs read the policy XML out of SYSVOL, decrypt any `cpassword` field locally, and apply the setting to the host.
&lt;p&gt;Microsoft was not unaware. On April 22, 2009, the Group Policy Team blog re-posted (and updated) a piece by Linda Moore titled &lt;em&gt;&quot;Passwords in Group Policy Preferences (Updated)&quot;&lt;/em&gt; [@ms-gp-blog-grouppolicy-2009-wayback]. The phrasing is unambiguous.&lt;/p&gt;

the password is not secured. Because the password is stored in SYSVOL, all authenticated users have read access to it. -- Linda Moore, Group Policy Team blog, April 22, 2009 [@ms-gp-blog-grouppolicy-2009-wayback]
&lt;p&gt;The post recommended a list of mitigations: prefer secure mechanisms, audit who can read the SYSVOL share, prefer not to use the field at all. None of those mitigations could rotate the key. None could revoke the &lt;em&gt;static AES-256 key value&lt;/em&gt; published in &lt;code&gt;[MS-GPPREF]&lt;/code&gt;. Microsoft was telling its customers, in 2009, three years and eight months before the public weaponisation, that the credential they were storing was decryptable by every user in the domain by design.&lt;/p&gt;
&lt;p&gt;Three years later, the offensive-security community spent twelve weeks turning the publication into a default-on red-team primitive.&lt;/p&gt;
&lt;p&gt;In April and May of 2012, Emilien Girault of Sogeti ESEC published a Python decryptor on the firm&apos;s research blog [@sogeti-2012-wayback]. The site has since been retired and the canonical reference is the Wayback Machine capture. In mid-May 2012, Chris Campbell (@obscuresec) published &lt;code&gt;Get-GPPPassword.ps1&lt;/code&gt;, a PowerShell port that fetched the relevant XML from SYSVOL, decoded the base64, and called .NET&apos;s AES primitives with the published key [@obscuresec-gpp-2012]. The script was folded into PowerSploit at &lt;code&gt;Exfiltration/Get-GPPPassword.ps1&lt;/code&gt;, where its header still reads &lt;em&gt;&quot;Author: Chris Campbell (@obscuresec)&quot;&lt;/em&gt; [@powersploit-getgpppwd] and explicitly credits Emilien Girault for the underlying research. In June 2012, &lt;strong&gt;Ben Campbell&lt;/strong&gt; (the &lt;code&gt;rewtdance.blogspot.com&lt;/code&gt; blog handle), working with &lt;code&gt;scriptmonkey&lt;/code&gt; (a named collaborator with his own blog at &lt;code&gt;blog.owobble.co.uk&lt;/code&gt;), extended the attack to &lt;em&gt;all six&lt;/em&gt; XML wire-format carriers that &lt;code&gt;[MS-GPPREF]&lt;/code&gt; permits [@rewtdance-gpp-2012]. The rewtdance post body credits the collaboration verbatim: &lt;em&gt;&quot;Working with scriptmonkey (&lt;a href=&quot;http://blog.owobble.co.uk/&quot; rel=&quot;noopener&quot;&gt;http://blog.owobble.co.uk/&lt;/a&gt;), who already had a DC configured, we verified this theory.&quot;&lt;/em&gt; On July 25, 2012, the Metasploit module &lt;code&gt;post/windows/gather/credentials/gpp.rb&lt;/code&gt; landed [@metasploit-gpp] with five co-authors: Ben Campbell, Loic Jaquemet, scriptmonkey, theLightCosine, and mubix. A companion auxiliary scanner, &lt;code&gt;auxiliary/scanner/smb/smb_enum_gpp.rb&lt;/code&gt;, was authored independently by Joshua D. Abraham of Praetorian [@metasploit-smb-enum-gpp].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A widespread folk attribution credits &lt;code&gt;Get-GPPPassword.ps1&lt;/code&gt; to &quot;scriptjunkie.&quot; The primary sources do not support that claim. The PowerSploit script header credits Chris Campbell (@obscuresec) [@powersploit-getgpppwd]; the rewtdance June 2012 follow-up is by Ben Campbell with scriptmonkey as a named collaborator (scriptmonkey blogs at &lt;code&gt;blog.owobble.co.uk&lt;/code&gt;, not at rewtdance) [@rewtdance-gpp-2012]; the Metasploit &lt;code&gt;gpp.rb&lt;/code&gt; module&apos;s author field names Ben Campbell, Loic Jaquemet, scriptmonkey, theLightCosine, and mubix [@metasploit-gpp]; and the &lt;code&gt;smb_enum_gpp&lt;/code&gt; scanner is by Joshua D. Abraham [@metasploit-smb-enum-gpp]. No primary source ties &quot;scriptjunkie&quot; (Matt Weeks) to the GPP cpassword research chain at all. The names are similar; the people are different.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The whole exercise was twelve lines of code. The interesting part was not the cryptography. The interesting part was that the operation was &lt;em&gt;decryption-by-reference&lt;/em&gt;: with a published key, the AES envelope was not protecting a secret, it was carrying a secret in a format the protocol specification told everyone how to read.&lt;/p&gt;

```
4e 99 06 e8  fc b6 6c c9  fa f4 93 10  62 0f fe e8
f4 96 e8 06  cc 05 79 90  20 9b 09 a4  33 b6 6c 1b
```
These bytes are reproduced verbatim from Microsoft&apos;s published `[MS-GPPREF]` Group Policy Preferences specification [@ms-gppref-aes-key]. They have appeared in the public Microsoft Open Specifications corpus since the `[MS-GPPREF]` protocol document was first published as part of the Windows Server 2008 protocol-documentation programme; the earliest tangible third-party reuse of the key dates to the April-July 2012 Sogeti / obscuresec / rewtdance / Metasploit research chain [@sogeti-2012-wayback; @obscuresec-gpp-2012; @rewtdance-gpp-2012; @metasploit-gpp]. The key is *not* a secret; it is an interoperability primitive.
&lt;p&gt;{`&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ReadSucceeds[&quot;Read succeeds (silent CONTROL_ACCESS bypass)&quot;]
ReadFails[&quot;Read fails (correctly ACL-gated)&quot;]
Endpoint --&amp;gt; GPRefresh
GPRefresh --&amp;gt; Rotate
Rotate --&amp;gt; SAMWrite
Rotate --&amp;gt; ADWrite
ADWrite --&amp;gt; LDAPRead
LDAPRead --&amp;gt; Bypass
Bypass -- yes --&amp;gt; ReadSucceeds
Bypass -- no --&amp;gt; ReadFails
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The other structural limit was the directory&apos;s own integrity boundary. The password sat in plaintext in the directory. A stolen &lt;code&gt;NTDS.dit&lt;/code&gt; -- obtained via DCSync, NTDSUtil dump, or physical theft of a DC&apos;s disk -- exposed every managed local-Administrator password in the forest at once. There was no encryption-at-rest in legacy LAPS, by design. The trust model was &quot;the directory is tier 0 and DCSync is a domain-compromise event already,&quot; which is operationally true and architecturally lazy.&lt;/p&gt;
&lt;p&gt;Microsoft fixed both of those structural defects on April 11, 2023. The fix shipped in the operating system, with no MSI. We come to it next.&lt;/p&gt;
&lt;h2&gt;6. The In-Box Era: Windows LAPS (April 11, 2023 to Present)&lt;/h2&gt;
&lt;p&gt;Patch Tuesday, April 11, 2023. The April cumulative update for Windows 11 22H2 was KB5025239. The Windows 11 21H2 update was KB5025224. Windows 10 22H2 was KB5025221. Windows Server 2022 was KB5025230. Windows Server 2019 was KB5025229. The Server Annual Channel shipped it too. Windows Server 2016 was, and remains, explicitly excluded -- the per-SKU April-2023 cumulative-update KB numbers are catalogued in the Tenable retrospective on the Windows LAPS GA wave [@tc-windows-laps-ga-2023] and the official Microsoft LAPS overview page [@ms-laps-overview]. The MSI was gone. The &lt;code&gt;admpwd.dll&lt;/code&gt; Client-Side Extension was gone. In its place: &lt;strong&gt;exactly three&lt;/strong&gt; OS binaries -- &lt;code&gt;laps.dll&lt;/code&gt; for core LAPS logic, &lt;code&gt;lapscsp.dll&lt;/code&gt; for the Microsoft Intune Configuration Service Provider, and &lt;code&gt;lapspsh.dll&lt;/code&gt; for the &lt;code&gt;LAPS&lt;/code&gt; PowerShell module -- all shipped together, all part of the OS, all available without installing anything [@ms-laps-concepts-overview; @tc-windows-laps-ga-2023]. The Microsoft Learn &lt;code&gt;laps-concepts-overview&lt;/code&gt; page enumerates the three binaries verbatim and lists no fourth.&lt;/p&gt;
&lt;p&gt;The most consequential architectural change is the one most often missed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The legacy &lt;code&gt;admpwd.dll&lt;/code&gt; was a Group Policy CSE; its rotation cycle was driven by the GP refresh interval (90 minutes plus jitter on member computers). The new &lt;code&gt;laps.dll&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; a CSE. It runs on a hard-coded in-process background timer of approximately one hour inside &lt;code&gt;laps.dll&lt;/code&gt; itself -- &lt;strong&gt;not&lt;/strong&gt; a Windows Task Scheduler task, and not configurable. The cited Microsoft Learn page is unambiguous: &lt;em&gt;&quot;Windows LAPS uses a background task that wakes up every hour to process the currently active policy. This task isn&apos;t implemented with a Windows Task Scheduler task and isn&apos;t configurable.&quot;&lt;/em&gt; The polling cycle is decoupled from the Group Policy refresh cycle entirely [@ms-laps-concepts-overview]. The implications: the rotation cadence is not configurable below one hour; reducing the GP refresh interval does not accelerate LAPS rotation; the Task Scheduler library will not show a LAPS task because there isn&apos;t one; and Windows LAPS will rotate a password on an off-network domain-joined machine the moment it re-establishes line-of-sight to a Domain Controller, regardless of whether a GP refresh has fired.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The new schema added six attributes to the &lt;code&gt;Computer&lt;/code&gt; object: &lt;code&gt;msLAPS-Password&lt;/code&gt; (the plaintext-fallback location), &lt;code&gt;msLAPS-EncryptedPassword&lt;/code&gt; (the CNG-DPAPI-wrapped ciphertext blob), &lt;code&gt;msLAPS-EncryptedPasswordHistory&lt;/code&gt; (rotation history), &lt;code&gt;msLAPS-PasswordExpirationTime&lt;/code&gt;, &lt;code&gt;msLAPS-EncryptedDSRMPassword&lt;/code&gt; (Directory Services Restore Mode account on a DC), and &lt;code&gt;msLAPS-EncryptedDSRMPasswordHistory&lt;/code&gt; [@ms-laps-concepts-overview]. The DSRM pair is a Windows-LAPS-only capability; legacy LAPS never covered Domain Controller DSRM accounts. The schema extension is performed once per forest by &lt;code&gt;Update-LapsADSchema&lt;/code&gt;, which is idempotent and coexists with the legacy &lt;code&gt;ms-Mcs-AdmPwd&lt;/code&gt; attribute [@ms-laps-mig-scenarios].&lt;/p&gt;
&lt;p&gt;A seventh attribute, &lt;code&gt;msLAPS-CurrentPasswordVersion&lt;/code&gt;, exists in the &lt;strong&gt;Windows Server 2025 forest schema&lt;/strong&gt; only. It is added automatically when the first Windows Server 2025 Domain Controller is promoted -- &lt;em&gt;not&lt;/em&gt; by running &lt;code&gt;Update-LapsADSchema&lt;/code&gt; -- and is used by &lt;code&gt;laps.dll&lt;/code&gt; to mitigate a virtual-machine-snapshot torn-state class. The attribute is read-only as far as the LAPS feature is concerned and is not part of the &lt;code&gt;ReadLAPSPassword&lt;/code&gt; BloodHound edge&apos;s calculus [@ms-laps-concepts-overview].&lt;/p&gt;
&lt;h3&gt;Encryption-at-rest with CNG DPAPI&lt;/h3&gt;
&lt;p&gt;The load-bearing addition is encryption of the password &lt;em&gt;before&lt;/em&gt; it leaves the client. The mechanism is the &lt;strong&gt;CNG DPAPI&lt;/strong&gt; group key-protector (still commonly called DPAPI-NG in Microsoft&apos;s older documentation) [@ms-cng-dpapi]. The client generates the new local-Administrator password, then wraps the plaintext against a security principal SID using the Active Directory Key Distribution Service (KDS) root key infrastructure. The wrapped blob is the only thing the LDAP write places into &lt;code&gt;msLAPS-EncryptedPassword&lt;/code&gt;. To decrypt, a reader Kerberos-authenticates to the KDC; only members of the configured principal group at decryption time can derive the protector. The directory itself never sees plaintext, and a stolen &lt;code&gt;NTDS.dit&lt;/code&gt; yields ciphertext only [@ms-laps-concepts-overview].&lt;/p&gt;

A protection mechanism in Windows&apos;s CNG (Cryptography API: Next Generation) Data Protection API in which a payload is encrypted against a security principal -- typically an AD group SID -- rather than against a local user. Decryption is gated by Kerberos authentication and the principal&apos;s group membership at the time of decryption [@ms-cng-dpapi]. Microsoft Learn currently spells the primitive *&quot;CNG DPAPI&quot;* on the canonical reference; older Microsoft documentation and Win32 references continue to use the shorthand *&quot;DPAPI-NG&quot;*. They are the same primitive.
&lt;p&gt;There are two policy settings that gate the encryption path, and the failure modes are operationally important.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft Learn&apos;s &lt;code&gt;laps-management-policy-settings&lt;/code&gt; page lists &lt;code&gt;ADPasswordEncryptionEnabled&lt;/code&gt; with a default of &lt;strong&gt;True&lt;/strong&gt; [@ms-laps-policy-settings]. The genuine failure mode is &lt;em&gt;not&lt;/em&gt; an unset default; it is silent fallback to plaintext in &lt;code&gt;msLAPS-Password&lt;/code&gt; when (a) the forest&apos;s Domain Functional Level is below Windows Server 2016, or (b) the &lt;code&gt;BackupDirectory&lt;/code&gt; value is not &lt;code&gt;2&lt;/code&gt; (AD). Configure the policy &lt;em&gt;explicitly&lt;/em&gt; anyway: the explicit configuration makes the choice visible to policy audits and forces the operator to verify the DFL prerequisite. Do not flip a bit that is already True; do verify the prerequisites that make True work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When &lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt; is unspecified, Windows LAPS wraps the password against the &lt;em&gt;Domain Admins&lt;/em&gt; group of the computer&apos;s domain [@ms-laps-concepts-overview; @ms-laps-policy-settings]. Most fleets do not want every Domain Admin to be a routine LAPS reader. Configure a dedicated, audited, minimum-membership decryption group (a common naming convention is &lt;code&gt;LAPS-DPAPI-Decryptors&lt;/code&gt;) and assign it explicitly. Decryption authority is delegated separately from LDAP read authority; minimising membership of the decryption group is the single most useful hardening lever on a Windows LAPS deployment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The backup-directory choice&lt;/h3&gt;

The CSP / GPO node `BackupDirectory` selects where Windows LAPS writes the rotated password. The three valid values are **0** (do not back up; passwords rotate locally but are not retrievable), **1** (Microsoft Entra ID via the `deviceLocalCredentials` resource on Microsoft Graph), and **2** (Active Directory via the `msLAPS-*` attribute set). The values are mutually exclusive per device; a hybrid-joined device can choose either backend but not both [@ms-laps-policy-settings; @ms-laps-entra-scenarios].
&lt;p&gt;The Entra-backup path went generally available on October 23, 2023 [@tc-entra-laps-ga-2023]. With &lt;code&gt;BackupDirectory = 1&lt;/code&gt;, the local LAPS component posts the rotated password to the &lt;code&gt;deviceLocalCredentials&lt;/code&gt; resource on the device object in Microsoft Entra ID via the Microsoft Graph API [@ms-graph-localcredinfo]. Retrieval is via &lt;code&gt;Get-LapsAADPassword&lt;/code&gt; (a thin wrapper over the Graph endpoint), the Entra portal Devices blade, or a direct &lt;code&gt;GET /directory/deviceLocalCredentials/{deviceId}&lt;/code&gt; call [@ms-laps-entra-scenarios].&lt;/p&gt;
&lt;p&gt;The Entra-backup path has a &lt;strong&gt;seven-day minimum&lt;/strong&gt; for &lt;code&gt;PasswordAgeDays&lt;/code&gt;. The AD-backup path&apos;s minimum is one day. A tier-0 fleet that targets daily rotation on Entra-joined endpoints will not get daily rotation -- Entra-side policy validation rejects the value. Section 7&apos;s baseline table reflects this asymmetry.&lt;/p&gt;
&lt;h3&gt;Policy surface and the FQ-anchored corrections&lt;/h3&gt;
&lt;p&gt;Windows LAPS is configurable via Group Policy (for AD-joined hosts), the LAPS Configuration Service Provider at &lt;code&gt;./Device/Vendor/MSFT/LAPS/Policies/*&lt;/code&gt; for Intune-managed hosts [@ms-laps-csp], local policy, or the legacy LAPS GPO if &lt;code&gt;PolicySourceMode&lt;/code&gt; selects emulation mode. The settings include &lt;code&gt;BackupDirectory&lt;/code&gt;, &lt;code&gt;PasswordComplexity&lt;/code&gt; (values 1 through 8), &lt;code&gt;PasswordLength&lt;/code&gt;, &lt;code&gt;PasswordAgeDays&lt;/code&gt;, &lt;code&gt;PostAuthenticationActions&lt;/code&gt;, &lt;code&gt;PostAuthenticationResetDelay&lt;/code&gt;, &lt;code&gt;AdministratorAccountName&lt;/code&gt;, &lt;code&gt;PassphraseLength&lt;/code&gt;, &lt;code&gt;ADPasswordEncryptionEnabled&lt;/code&gt;, &lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt;, and &lt;code&gt;ADBackupDSRMPassword&lt;/code&gt;. On Windows 11 24H2 and Windows Server 2025 and later, the policy surface adds Automatic Account Management settings: &lt;code&gt;AutomaticAccountManagementEnabled&lt;/code&gt;, &lt;code&gt;AutomaticAccountManagementNameOrPrefix&lt;/code&gt;, &lt;code&gt;AutomaticAccountManagementRandomizeName&lt;/code&gt;, &lt;code&gt;AutomaticAccountManagementTarget&lt;/code&gt;, and &lt;code&gt;AutomaticAccountManagementEnableAccount&lt;/code&gt; [@ms-laps-policy-settings; @ms-laps-account-modes].&lt;/p&gt;

The action Windows LAPS performs after the managed account has authenticated to the host. Valid values are **1** (reset the password), **3** (reset and sign out the interactive session; default), **5** (reset and reboot, with a one-minute reboot delay), and **11** (reset, sign out, and terminate remaining processes; Windows 11 24H2 / Windows Server 2025 and later). The action fires after `PostAuthenticationResetDelay` hours have elapsed since the authentication that triggered it [@ms-laps-policy-settings].
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A widespread misreading of the older Microsoft documentation lists &lt;code&gt;PostAuthenticationActions&lt;/code&gt; as a 1-2-3 enum. The correct enumeration per the current Microsoft Learn reference [@ms-laps-policy-settings] is &lt;strong&gt;1&lt;/strong&gt; (reset password), &lt;strong&gt;3&lt;/strong&gt; (reset + sign out; &lt;em&gt;default&lt;/em&gt;), &lt;strong&gt;5&lt;/strong&gt; (reset + reboot), and &lt;strong&gt;11&lt;/strong&gt; (reset + sign out + terminate remaining processes; Win 11 24H2 / Server 2025+). Value &lt;strong&gt;11&lt;/strong&gt; is &lt;em&gt;not&lt;/em&gt; &quot;force shutdown without warning&quot;; interactive users receive the same non-configurable two-minute warning as on value 3, and remaining processes are terminated after the warning expires. SMB sessions on the host are deleted on values 3 and 11.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;PostAuthenticationResetDelay&lt;/code&gt; defaults to &lt;strong&gt;24 hours&lt;/strong&gt;. The range is 0 to 24 hours; a value of 0 disables the post-authentication action entirely [@ms-laps-policy-settings]. A tier-0 fleet aiming to close the screenshotted-password OPSEC tail aggressively will configure this down to 1 hour; tier-2 deployments typically leave it at 8 or 24.&lt;/p&gt;
&lt;h3&gt;PasswordComplexity values 5 through 8 (Windows 11 24H2+ / Windows Server 2025+)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PasswordComplexity&lt;/code&gt; values &lt;strong&gt;1 through 4&lt;/strong&gt; are character-class modes (uppercase only; uppercase plus lowercase; uppercase plus lowercase plus numbers; and -- value 4, the default -- all four character classes). Value &lt;strong&gt;5&lt;/strong&gt; is &lt;em&gt;not&lt;/em&gt; a &quot;no vowels or numbers&quot; mode, despite a common folk attribution; it is the &lt;strong&gt;&quot;improved readability&quot; four-class variant&lt;/strong&gt; of value 4, equivalent to value 4 with the visually ambiguous glyphs &lt;code&gt;I&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt;, &lt;code&gt;Q&lt;/code&gt;, &lt;code&gt;l&lt;/code&gt;, &lt;code&gt;o&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt; removed and the symbols &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt; added [@ms-laps-passwords-passphrases]. Microsoft&apos;s own documented example password for value 5 is &lt;code&gt;vnJ!!?MTb5=U7Y&lt;/code&gt; -- which retains vowels and digits 2 through 9. Values &lt;strong&gt;6, 7, and 8&lt;/strong&gt; are passphrase modes drawn from a Microsoft-curated wordlist derived from the EFF Diceware wordlists [@eff-dice; @eff-wordlists-2016] with internal modifications. The published word counts after Microsoft&apos;s curation are &lt;strong&gt;7776 / 1276 / 1276&lt;/strong&gt; for modes 6 / 7 / 8 respectively; the EFF originals (the EFF Long Wordlist, EFF Short Wordlist #1, and EFF Short Wordlist #2 published July 2016) are &lt;strong&gt;7776 / 1296 / 1296&lt;/strong&gt; [@eff-dice; @eff-wordlists-2016]. &lt;strong&gt;Values 5 through 8 are all gated on Windows 11 24H2 / Windows Server 2025 and later&lt;/strong&gt; -- not only values 6-8. The cited Microsoft Learn page reads verbatim for value 5: &lt;em&gt;&quot;The PasswordComplexity setting of &apos;5&apos; is only supported in Windows 11 24H2, Windows Server 2025, and later releases.&quot;&lt;/em&gt; [@ms-laps-passwords-passphrases]. Passphrase modes exist for DSRM-account scenarios where the password must be typed by a human under duress; the article&apos;s section 7 baseline recommends them for tier-0 break-glass accounts.&lt;/p&gt;
&lt;h3&gt;PowerShell surface and one important cmdlet name&lt;/h3&gt;
&lt;p&gt;The native &lt;code&gt;LAPS&lt;/code&gt; PowerShell module ships eight cmdlets the article calls out by name: &lt;code&gt;Get-LapsADPassword&lt;/code&gt;, &lt;code&gt;Reset-LapsPassword&lt;/code&gt;, &lt;code&gt;Update-LapsADSchema&lt;/code&gt;, &lt;code&gt;Set-LapsADAuditing&lt;/code&gt;, &lt;code&gt;Set-LapsADComputerSelfPermission&lt;/code&gt;, &lt;code&gt;Set-LapsADReadPasswordPermission&lt;/code&gt;, &lt;code&gt;Set-LapsADResetPasswordPermission&lt;/code&gt;, and &lt;code&gt;Find-LapsADExtendedRights&lt;/code&gt; [@ms-laps-ps-overview; @ms-laps-get-adpassword]. The auditing cmdlet is &lt;code&gt;Set-LapsADAuditing&lt;/code&gt; -- &lt;em&gt;not&lt;/em&gt; &lt;code&gt;Set-LapsADAuditingSettings&lt;/code&gt;, which does not exist as a cmdlet name [@ms-laps-set-adauditing]. The Entra-backup retrieval cmdlet is &lt;code&gt;Get-LapsAADPassword&lt;/code&gt;, a wrapper around Microsoft Graph.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A common copy-paste error in deployment runbooks is to write &lt;code&gt;Set-LapsADAuditingSettings&lt;/code&gt;. The cmdlet name is &lt;code&gt;Set-LapsADAuditing&lt;/code&gt; [@ms-laps-set-adauditing], and the cmdlet emits Directory Service audit event 4662 on configured attribute reads. The SACL it installs targets the LAPS attribute set; you still need the host-side Audit Directory Service Access subcategory enabled on Domain Controllers for the event to land in the Security log.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Migration coexistence&lt;/h3&gt;
&lt;p&gt;Legacy LAPS and Windows LAPS can coexist on the same host only if they target &lt;em&gt;different&lt;/em&gt; local accounts. The documented coexistence pattern is to run legacy LAPS against the built-in RID 500 Administrator while introducing Windows LAPS against a named secondary local-admin account, then retire the legacy MSI once Windows LAPS coverage is verified [@ms-laps-mig-scenarios]. The cross-pointer in section 11 details the seven-step migration sequence.&lt;/p&gt;

flowchart TD
    Tick[&quot;laps.dll background timer (~1 hr)&quot;]
    ReadPolicy[&quot;Read effective policy&lt;br /&gt;CSP &amp;gt; GPO &amp;gt; local &amp;gt; legacy emulation&quot;]
    BackupDir{&quot;BackupDirectory&lt;br /&gt;1 (Entra) / 2 (AD) / 0?&quot;}
    EntraPath[&quot;Write to Graph deviceLocalCredentials&lt;br /&gt;(min PasswordAgeDays = 7)&quot;]
    ADPath[&quot;Write to msLAPS-* attribute set&lt;br /&gt;(min PasswordAgeDays = 1)&quot;]
    EncryptionGate{&quot;ADPasswordEncryptionEnabled = True&lt;br /&gt;AND DFL ≥ Server 2016?&quot;}
    Encrypted[&quot;msLAPS-EncryptedPassword&lt;br /&gt;(DPAPI-NG, principal = ADPasswordEncryptionPrincipal)&quot;]
    Plaintext[&quot;msLAPS-Password (plaintext fallback)&quot;]
    SetSAM[&quot;Set SAM password on&lt;br /&gt;AdministratorAccountName (empty = RID 500)&quot;]
    Auth[&quot;Managed account authenticates&quot;]
    PAA{&quot;PostAuthenticationActions&lt;br /&gt;0 / 1 / 3 / 5 / 11?&quot;}
    Wait[&quot;Wait PostAuthenticationResetDelay (default 24 h)&quot;]
    Action1[&quot;1: reset password&quot;]
    Action3[&quot;3: reset + sign out, 2-min warning, DEFAULT&quot;]
    Action5[&quot;5: reset + reboot, 1-min delay&quot;]
    Action11[&quot;11: reset + sign out + terminate procs (24H2 / WS2025+)&quot;]
    Tick --&amp;gt; ReadPolicy
    ReadPolicy --&amp;gt; BackupDir
    BackupDir -- 1 --&amp;gt; EntraPath
    BackupDir -- 2 --&amp;gt; EncryptionGate
    BackupDir -- 0 --&amp;gt; SetSAM
    EncryptionGate -- yes --&amp;gt; Encrypted
    EncryptionGate -- no --&amp;gt; Plaintext
    EntraPath --&amp;gt; SetSAM
    Encrypted --&amp;gt; SetSAM
    Plaintext --&amp;gt; SetSAM
    SetSAM --&amp;gt; Auth
    Auth --&amp;gt; Wait
    Wait --&amp;gt; PAA
    PAA -- 1 --&amp;gt; Action1
    PAA -- 3 --&amp;gt; Action3
    PAA -- 5 --&amp;gt; Action5
    PAA -- 11 --&amp;gt; Action11
&lt;p&gt;With the in-box era settled, what does a 2026 deployment actually look like? A short list of policy settings, and a slightly longer list of footguns.&lt;/p&gt;
&lt;h2&gt;7. The 2026 Baseline as a Settings Table&lt;/h2&gt;
&lt;p&gt;Architecture is interesting. Audits are not. Here is the 2026 settings table that, in production, separates a deployment that meets its goal from one that quietly does not. Every row carries the policy node, the documented default, the recommended tier-2 value (a typical end-user fleet), the recommended tier-0 value (Domain Controllers and break-glass), and the citation. Cross-check the row against the Microsoft Learn policy-settings page before you ship it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Recommended (tier 2)&lt;/th&gt;
&lt;th&gt;Recommended (tier 0)&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;th&gt;Citation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BackupDirectory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt; (no backup)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt; (AD) for AD-joined and hybrid-joined; &lt;code&gt;1&lt;/code&gt; (Entra) for pure Entra-joined&lt;/td&gt;
&lt;td&gt;same as tier 2&lt;/td&gt;
&lt;td&gt;One directory per device; AD for hybrid where on-prem identity is canonical&lt;/td&gt;
&lt;td&gt;[@ms-laps-policy-settings]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PasswordComplexity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4&lt;/code&gt; (all character classes)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;6&lt;/code&gt; (3-word passphrase) for accounts a human must type under duress (DSRM / break-glass); &lt;code&gt;4&lt;/code&gt; for automated retrieval&lt;/td&gt;
&lt;td&gt;Passphrases for human typing; character-set for tool-only retrieval. Values &lt;strong&gt;5 through 8&lt;/strong&gt; are gated on Windows 11 24H2 / Windows Server 2025 and later: value 5 is the &quot;improved-readability&quot; four-class variant of 4 (not a &quot;no vowels&quot; mode); values 6/7/8 are passphrase modes with Microsoft-curated EFF-derived wordlists of 7776 / 1276 / 1276 entries (EFF originals: 7776 / 1296 / 1296)&lt;/td&gt;
&lt;td&gt;[@ms-laps-passwords-passphrases; @eff-dice; @eff-wordlists-2016]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PasswordLength&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Eliminates the rainbow-table threat class&lt;/td&gt;
&lt;td&gt;[@ms-laps-passwords-passphrases]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PasswordAgeDays&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;30&lt;/code&gt; (1-day minimum AD; 7-day minimum Entra; 365-day max)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt; (AD) / &lt;code&gt;7&lt;/code&gt; (Entra; lower fails policy validation)&lt;/td&gt;
&lt;td&gt;Caps the blast radius of an undetected credential theft to one rotation window&lt;/td&gt;
&lt;td&gt;[@ms-laps-policy-settings]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostAuthenticationActions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt; (reset + sign out)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;, or &lt;code&gt;11&lt;/code&gt; on Win 11 24H2+ if process termination is required&lt;/td&gt;
&lt;td&gt;Closes the screenshot-leak OPSEC tail on the next managed-account interactive logon. Value &lt;code&gt;11&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; &quot;force shutdown without warning&quot; -- it is reset + sign out + terminate remaining processes with the same two-minute warning as &lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;[@ms-laps-policy-settings]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostAuthenticationResetDelay&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;24&lt;/code&gt; (hours)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trade-off between operational task completion and exposure window&lt;/td&gt;
&lt;td&gt;[@ms-laps-policy-settings]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADPasswordEncryptionEnabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt; per Microsoft Learn&apos;s defaults table -- &lt;em&gt;not&lt;/em&gt; off-by-default&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;, configured explicitly so the choice is visible in policy audits and the DFL prerequisite is verified&lt;/td&gt;
&lt;td&gt;same&lt;/td&gt;
&lt;td&gt;The genuine failure mode is silent fallback to plaintext when DFL is below Server 2016 or &lt;code&gt;BackupDirectory&lt;/code&gt; is not &lt;code&gt;2&lt;/code&gt;, &lt;em&gt;not&lt;/em&gt; a default-off bit&lt;/td&gt;
&lt;td&gt;[@ms-laps-policy-settings; @ms-laps-csp]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Domain Admins&lt;/code&gt; of the computer&apos;s domain when unspecified&lt;/td&gt;
&lt;td&gt;Dedicated &lt;code&gt;LAPS-DPAPI-Decryptors&lt;/code&gt; group, &lt;em&gt;not&lt;/em&gt; Domain Admins&lt;/td&gt;
&lt;td&gt;same, with PIM-gated activation&lt;/td&gt;
&lt;td&gt;Decryption authority is delegated separately from LDAP read; minimise membership&lt;/td&gt;
&lt;td&gt;[@ms-laps-concepts-overview]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AdministratorAccountName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;empty (manages built-in RID 500)&lt;/td&gt;
&lt;td&gt;empty on Server SKUs; named account (e.g. &lt;code&gt;lapsadmin&lt;/code&gt;) on Client SKUs with the built-in disabled&lt;/td&gt;
&lt;td&gt;On Win 11 24H2 / WS2025+, prefer Automatic Account Management with random name and disabled-by-default&lt;/td&gt;
&lt;td&gt;Defeats predictable-RID-500 enumeration&lt;/td&gt;
&lt;td&gt;[@ms-laps-policy-settings; @ms-laps-account-modes]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADBackupDSRMPassword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;n/a (member servers)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt; on Domain Controllers&lt;/td&gt;
&lt;td&gt;Brings DSRM-account management into LAPS scope -- a capability legacy LAPS never had&lt;/td&gt;
&lt;td&gt;[@ms-laps-concepts-overview]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

Tier-0 deviations from the tier-2 baseline are narrow but consequential. (a) `PasswordAgeDays` to 1 (AD) or 7 (Entra) caps the undetected-theft window. (b) `PostAuthenticationResetDelay` to 1 hour aggressively rotates after legitimate use. (c) `ADPasswordEncryptionPrincipal` to a dedicated decryptor group with PIM-gated activation [@ms-entra-pim] -- not standing membership. (d) `ADBackupDSRMPassword = True` only on DCs, so the Directory Services Restore Mode account is in LAPS scope. (e) `PasswordComplexity = 6` on accounts that a human must type under duress (DSRM, ESAE break-glass), `4` everywhere else. The tier-0 baseline is more expensive operationally -- daily rotation and 1-hour post-auth delay create a non-trivial volume of password reads through the decryption group -- and the cost is the entire point. Anything cheaper does not warrant the tier-0 label.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The single most useful hardening move on a Windows LAPS deployment is to explicitly set &lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt; to a dedicated group with minimum membership. Default = Domain Admins of the computer&apos;s domain is operationally correct (Domain Admins should be the readers of last resort) but architecturally lazy (most fleets do not want their DA group to be the routine LAPS-read group). Name the group something searchable -- &lt;code&gt;LAPS-DPAPI-Decryptors&lt;/code&gt; is a defensible convention -- and put helpdesk LAPS-read permissions in &lt;em&gt;that&lt;/em&gt; group, gated by Entra PIM activation [@ms-entra-pim] for non-emergency reads.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The audit-primitives sub-table&lt;/h3&gt;
&lt;p&gt;The decision of which tool answers which question is, in practice, the difference between a LAPS deployment that meets its goal and one that quietly does not. The five (and a half) primitives:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;Primary source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;BloodHound &lt;code&gt;ReadLAPSPassword&lt;/code&gt; edge&lt;/td&gt;
&lt;td&gt;Which principals can read the LAPS password on which computer objects, transitively across the graph?&lt;/td&gt;
&lt;td&gt;[@bloodhound-edge-readlaps]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PingCastle &lt;code&gt;A-LAPS-Not-Installed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Does this domain have any LAPS solution installed for the native local administrator account?&lt;/td&gt;
&lt;td&gt;[@pingcastle-rules]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PingCastle &lt;code&gt;A-LAPS-Joined-Computers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Can a user who manually domain-joined a computer (via &lt;code&gt;mS-DS-CreatorSID&lt;/code&gt; ownership) still read that computer&apos;s LAPS password?&lt;/td&gt;
&lt;td&gt;[@pingcastle-rules]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PingCastle &lt;code&gt;A-PwdGPO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Does this domain still have residual GPP &lt;code&gt;cpassword&lt;/code&gt; artefacts in SYSVOL? (MITRE T1552.006)&lt;/td&gt;
&lt;td&gt;[@pingcastle-rules; @mitre-t1552-006]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows event 4662 on &lt;code&gt;msLAPS-*&lt;/code&gt; (SACL via &lt;code&gt;Set-LapsADAuditing&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Who read which LAPS attribute on which computer object, and when?&lt;/td&gt;
&lt;td&gt;[@ms-laps-set-adauditing; @ms-laps-ps-overview]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entra audit log + Graph &lt;code&gt;GET /directory/deviceLocalCredentials/{deviceId}&lt;/code&gt; reads&lt;/td&gt;
&lt;td&gt;Who retrieved which LAPS password from Microsoft Entra ID (&lt;code&gt;BackupDirectory = 1&lt;/code&gt;), and when?&lt;/td&gt;
&lt;td&gt;[@ms-graph-localcredinfo; @ms-laps-entra-scenarios]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;No Microsoft Defender for Identity alert in the current public taxonomy names LAPS specifically [@ms-defender-alerts]; instead, lean on the event 4662 SACL primitive plus advanced hunting in the &lt;code&gt;IdentityDirectoryEvents&lt;/code&gt; table for principal-pattern anomalies. Microsoft&apos;s Compromised Credentials and Lateral Movement categories surface the downstream behaviour when a stolen LAPS password gets used.&lt;/p&gt;
&lt;p&gt;{`
// In production, run: Get-LapsADPassword -Identity * | Where-Object {
//   $&lt;em&gt;.ExpirationTimestamp -lt (Get-Date) -or $&lt;/em&gt;.Source -eq &apos;Plaintext&apos;
// }
// This in-browser demo mirrors the same logic against an array of mock computer objects.&lt;/p&gt;
&lt;p&gt;const ONE_DAY_MS = 86400000;
const computers = [
  { name: &quot;WS-001&quot;, msLapsExpiry: Date.now() + 5 * ONE_DAY_MS, encrypted: true  },
  { name: &quot;WS-002&quot;, msLapsExpiry: Date.now() - 2 * ONE_DAY_MS, encrypted: true  },
  { name: &quot;WS-003&quot;, msLapsExpiry: null,                        encrypted: false },
  { name: &quot;WS-004&quot;, msLapsExpiry: Date.now() + 1 * ONE_DAY_MS, encrypted: false },
];&lt;/p&gt;
&lt;p&gt;const gaps = computers.flatMap(c =&amp;gt; {
  const issues = [];
  if (c.msLapsExpiry === null)           issues.push(&quot;no password stored&quot;);
  else if (c.msLapsExpiry &amp;lt; Date.now())  issues.push(&quot;expired (overdue rotation)&quot;);
  if (!c.encrypted)                      issues.push(&quot;plaintext (msLAPS-Password)&quot;);
  return issues.length ? [`${c.name}: ${issues.join(&quot;, &quot;)}`] : [];
});&lt;/p&gt;
&lt;p&gt;console.log(gaps.length === 0
  ? &quot;All computers have current, encrypted LAPS passwords&quot;
  : &quot;Coverage gaps:\n  &quot; + gaps.join(&quot;\n  &quot;));
`}&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;AdministratorAccountName&lt;/code&gt; decision deserves one paragraph of its own. On Server SKUs, the built-in Administrator (RID 500) is enabled by default, and leaving the policy empty manages it -- this is what most deployments want. On Client SKUs the built-in is disabled by default; many shops create a named admin account (a common convention is &lt;code&gt;lapsadmin&lt;/code&gt;) and set &lt;code&gt;AdministratorAccountName&lt;/code&gt; to that name. On Windows 11 24H2 and Windows Server 2025 and later, the better answer is Automatic Account Management: set &lt;code&gt;AutomaticAccountManagementEnabled = 1&lt;/code&gt;, &lt;code&gt;AutomaticAccountManagementRandomizeName = 1&lt;/code&gt;, and &lt;code&gt;AutomaticAccountManagementEnableAccount = 0&lt;/code&gt;, and the host will auto-create a randomised-name disabled-by-default local-admin account that Windows LAPS owns end to end [@ms-laps-account-modes]. The result is that an attacker enumerating local accounts cannot guess the LAPS-managed account name from RID 500, RID 1000, or any other predictable identifier.&lt;/p&gt;
&lt;p&gt;This is the baseline. But LAPS is not the only answer to &quot;who knows the local admin password.&quot; For three classes of fleet, the right answer is something else.&lt;/p&gt;
&lt;h2&gt;8. When LAPS Is Not the Right Tool&lt;/h2&gt;
&lt;p&gt;Three classes of fleet should not -- or should not &lt;em&gt;only&lt;/em&gt; -- run Windows LAPS. The first wants a workflow LAPS does not offer. The second wants no standing local admin at all. The third is orthogonal: it changes the in-session elevation surface without changing the recoverable break-glass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third-party Privileged Access Management (PAM) vaults.&lt;/strong&gt; Delinea Secret Server [@delinea-secretserver], CyberArk Endpoint Privilege Manager [@cyberark-epm], and BeyondTrust Password Safe are the dominant 2026 commercial offerings in the category. The case for running a PAM vault alongside (or instead of) Windows LAPS is rarely about cryptography and almost always about workflow. PAM vaults bring multi-factor authentication on checkout, full session recording, dual-approval gates for high-risk accounts, and cross-OS scope (Windows, macOS, Linux, network gear, hypervisors) under one ACL model. The total cost of ownership is higher than LAPS; the security model, properly deployed, is comparable. Many shops run both: Windows LAPS for the workstation floor, PAM for tier-0 break-glass with session recording. The split is a &lt;em&gt;workflow&lt;/em&gt; trade-off, not an architectural one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero standing local admin plus Entra PIM JIT elevation.&lt;/strong&gt; Tier-0 fleets that have reached the &quot;no routine local admin&quot; architectural state disable the built-in RID 500 entirely and gate every admin operation through just-in-time elevation. Microsoft Entra Privileged Identity Management [@ms-entra-pim] supports the eligibility / activation / approval workflow at scale: an operator is &lt;em&gt;eligible&lt;/em&gt; for an admin role, &lt;em&gt;activates&lt;/em&gt; it for a bounded duration with optional MFA and ticket reference, and an &lt;em&gt;approver&lt;/em&gt; signs off on the activation if policy requires. Windows LAPS coexists in this model as the absolute-last-resort break-glass mechanism -- for the case where Entra itself is down, the network is partitioned, and a human has to walk to a console and type a password. The architectural alignment is with MITRE T1078.001 (Default Accounts) [@mitre-t1078-001]: if the default account is permanently disabled and only re-enabled under PIM workflow, the entire technique class is bounded by the PIM activation log.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Windows 11 25H2 Administrator Protection.&lt;/strong&gt; Per-elevation transient admin sessions arrived as a Tech Community preview in late 2025 [@tc-admin-protection-win11]. The feature creates a temporary, isolated &quot;shadow admin&quot; identity for the duration of each elevation prompt, brokering UAC-class elevation through a per-elevation token that is destroyed when the elevated process exits. &lt;strong&gt;This is orthogonal to LAPS, not a replacement.&lt;/strong&gt; Administrator Protection addresses in-session UAC elevation; Windows LAPS addresses the recoverable break-glass password for off-network and non-bootable recovery. The two systems answer different questions. Conflating them produces designs that drop LAPS in favour of Administrator Protection and then discover, six months later, that there is no recovery primitive for a laptop the user has dropped off the corporate network for a year.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Recommended method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;On-premises AD-joined, no Entra ID&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; -- in-box Windows LAPS with AD backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Entra hybrid-joined, on-prem AD authoritative&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; -- Microsoft&apos;s current hybrid recommendation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pure Entra-joined, no on-prem AD&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;B&lt;/strong&gt; -- in-box Windows LAPS with Entra ID backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stuck on Windows Server 2016 (excluded from Windows LAPS)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; -- legacy MSI LAPS until OS migration completes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In active migration from legacy LAPS to Windows LAPS&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt; in side-by-side mode with different managed accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-Windows scope (Linux, macOS, network gear) needs unified vaulting&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt; -- third-party PAM vault, often alongside A/B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regulated industry requiring session recording / MFA checkout&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt; alongside A/B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tier-0 fleet with a zero-standing-credential goal and Entra ID P2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;E&lt;/strong&gt; -- PIM-gated JIT elevation layered on A or B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows 11 fleet wanting in-session credential-theft mitigation&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;F&lt;/strong&gt; -- Administrator Protection alongside A/B (orthogonal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BYOD, workgroup, or unmanaged endpoints&lt;/td&gt;
&lt;td&gt;None of A through F -- &lt;em&gt;enrollment&lt;/em&gt; is the answer, not LAPS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart TD
    Start[&quot;Local-admin password problem for a fleet&quot;]
    BYOD{&quot;BYOD or unmanaged?&quot;}
    EnrollFirst[&quot;Enrollment is the answer, not LAPS&quot;]
    Join{&quot;AD-joined / hybrid / Entra-joined?&quot;}
    WS2016{&quot;Stuck on WS2016 or in migration?&quot;}
    Tier0{&quot;Tier 0 with zero-standing-credential goal?&quot;}
    CrossOS{&quot;Non-Windows scope or checkout workflow needed?&quot;}
    WinElev{&quot;Win 11 25H2 in-session elevation hardening?&quot;}
    MA[&quot;Method A: Windows LAPS, AD backup&quot;]
    MB[&quot;Method B: Windows LAPS, Entra backup&quot;]
    MC[&quot;Method C: legacy MSI LAPS&quot;]
    MD[&quot;Method D: PAM vault, alongside A/B&quot;]
    ME[&quot;Method E: PIM-gated JIT, layered on A/B&quot;]
    MF[&quot;Method F: Administrator Protection (orthogonal)&quot;]
    Start --&amp;gt; BYOD
    BYOD -- yes --&amp;gt; EnrollFirst
    BYOD -- no --&amp;gt; Join
    Join -- AD or hybrid --&amp;gt; MA
    Join -- pure Entra --&amp;gt; MB
    MA --&amp;gt; WS2016
    MB --&amp;gt; WS2016
    WS2016 -- yes --&amp;gt; MC
    WS2016 -- no --&amp;gt; Tier0
    Tier0 -- yes --&amp;gt; ME
    Tier0 -- no --&amp;gt; CrossOS
    CrossOS -- yes --&amp;gt; MD
    CrossOS -- no --&amp;gt; WinElev
    WinElev -- yes --&amp;gt; MF

The terminology is genuinely confusing. *Microsoft Entra hybrid joined* is a device join state: the workstation is joined to both an on-premises AD domain and Microsoft Entra ID, and both directories know about it. *Microsoft Entra hybrid runbook worker*, by contrast, is an Azure Automation primitive that runs Automation runbooks on a worker process inside an on-premises environment. They share a word and nothing else. Windows LAPS policy for hybrid-*joined* devices is a `BackupDirectory` choice (typically AD for on-prem-authoritative hybrid fleets, Entra for Entra-authoritative); Hybrid runbook workers are an Azure Automation concern and entirely outside the LAPS scope.
&lt;p&gt;All five answers above -- methods A through F -- have a structural ceiling. There is one bound none of them can break.&lt;/p&gt;
&lt;h2&gt;9. What LAPS Structurally Cannot Solve&lt;/h2&gt;
&lt;p&gt;Every recoverable-secret system has a privileged reader. Whether you call it &lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt;, a &quot;CyberArk vault admin,&quot; or a &quot;PIM eligible approver,&quot; somebody can break the glass -- which means somebody can compromise the glass. This is a lower bound, not an implementation defect.&lt;/p&gt;
&lt;p&gt;The eleven-year arc converged on a tight bound. It did not abolish the underlying problem. Four structural limits are worth naming, because each maps onto a real residual attack surface in 2026 deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bound 1: at least one reader exists, by construction.&lt;/strong&gt; Symbolically, $|\text{readers}| \geq 1$. CNG DPAPI&apos;s group-key-protector substitution does not eliminate the privileged class; it relocates the trust boundary. The boundary moves from &quot;every principal with LDAP read on the attribute&quot; (legacy LAPS) to &quot;every principal in the configured &lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt; group at decryption time&quot; (Windows LAPS). The relocation tightens the bound by orders of magnitude in typical fleets -- a &lt;code&gt;LAPS-DPAPI-Decryptors&lt;/code&gt; group with five members beats an &quot;All Extended Rights on the helpdesk OU&quot; delegation with five hundred -- but it does not move the bound to zero. The directory that stores the LAPS secret remains a tier-0 asset, and the decryptor group remains a tier-0 principal class.&lt;/p&gt;

Every recoverable secret has a privileged reader. The architectural game is to make the reader class small, audited, time-bounded, and reachable from the directory only through Kerberos. The game is not to make the reader class empty. That game has no winning move.
&lt;p&gt;&lt;strong&gt;Bound 2: the out-of-protocol OPSEC tail.&lt;/strong&gt; Once a plaintext password leaves the directory -- pasted into a helpdesk ticket, screenshotted into a Slack DM, stored in a shared KeePass database that the team forgot to rotate -- the protocol&apos;s rotation knob is the only remaining mitigation. &lt;code&gt;PostAuthenticationActions&lt;/code&gt; only fires after the &lt;em&gt;next managed-account interactive logon&lt;/em&gt; [@ms-laps-policy-settings]; pre-logon exposure is bounded only by &lt;code&gt;PasswordAgeDays&lt;/code&gt;. A password screenshotted into a chat log at 10:14 AM and never used is the password on that endpoint for the remainder of the configured rotation window, regardless of whether anyone has noticed the leak. The protocol does not, and cannot, solve &quot;the password is now in a chat log.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bound 3: unmanaged and BYOD endpoints.&lt;/strong&gt; A machine that is neither AD-joined nor Microsoft Intune-managed has no LAPS policy applied to it. Personal-device BYO MAM scope is outside the LAPS protection model entirely. The fix for these endpoints is enrollment, not LAPS. A non-trivial portion of the residual local-admin-password risk in 2026 is concentrated on the long tail of unmanaged endpoints that exist precisely because management was politically or contractually infeasible. The protocol does not solve this; &lt;em&gt;governance&lt;/em&gt; solves this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bound 4: verification asymmetry.&lt;/strong&gt; The directory&apos;s audit log says what it &lt;em&gt;chose&lt;/em&gt; to log. An unprivileged observer cannot verify enforcement from outside the directory. This is the structural ceiling that motivates external audit primitives -- PingCastle [@pingcastle-rules], BloodHound [@bloodhound-edge-readlaps], Defender for Identity [@ms-defender-alerts] -- because they sit outside the directory&apos;s own self-report. The bound cannot be closed inside the protocol; only an out-of-band attestation primitive can certify enforcement to a party that does not trust the directory.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Somebody has to break the glass. The decryptor group is the new tier-0 asset; LAPS bounds the problem, it does not abolish it. The eleven-year arc was a convergence on a tighter bound, not an arrival at a clean answer. The right framing for the 2026 baseline is &quot;the residual attack surface is now the &lt;em&gt;actual&lt;/em&gt; attack surface, rather than an artefact of incomplete shipping.&quot; That is real progress -- it just is not closure.&lt;/p&gt;
&lt;/blockquote&gt;

A structurally tighter design would have three properties: threshold cryptography so no single principal can decrypt (an $m$-of-$n$ Shamir secret-sharing scheme over the password protector, with $m \geq 2$ in tier-0 fleets); attestation-bound retrieval so the decryptor&apos;s device state is part of the decryption policy (Azure Managed HSM&apos;s secure-key-release policy grammar [@ms-mhsm-policy-grammar] is the closest shipping primitive that approaches this -- a key-release decision conditioned on attestation claims like `x-ms-attestation-type` or `tee:sevsnpvm`); and a ledger-of-reads so every retrieval is recorded on a tamper-evident substrate that the directory itself cannot rewrite (Azure Confidential Ledger [@ms-conf-ledger] is the closest shipping primitive on the Microsoft side). None of these three are wired into Windows LAPS in 2026. Each exists as an adjacent Microsoft product. The architectural integration -- a Windows LAPS that requires two `LAPS-DPAPI-Decryptors` members to co-sign a retrieval, attests the retrieving device&apos;s state at decryption time, and writes the retrieval event to an append-only ledger the directory cannot edit -- is engineering work that nobody has shipped.
&lt;p&gt;Some of those structural bounds map onto open problems with no clean 2026 answer. We close on six of them.&lt;/p&gt;
&lt;h2&gt;10. Open Problems in 2026&lt;/h2&gt;
&lt;p&gt;Six open problems in local-admin password management for which no first-party Microsoft answer ships in 2026. Each is one paragraph, framed as &quot;what is the question,&quot; &quot;what has been tried,&quot; and &quot;what is the current best partial result.&quot;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Open question&lt;/th&gt;
&lt;th&gt;What has been tried&lt;/th&gt;
&lt;th&gt;Current best partial result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Legacy SYSVOL &lt;code&gt;cpassword&lt;/code&gt; cleanup at scale&lt;/td&gt;
&lt;td&gt;MS14-025 (UI disable, no remediation); PingCastle scanning; community &lt;code&gt;Get-GPPDeployedPasswords&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Third-party scan-and-manual-delete; no first-party cmdlet ships in the OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-tenant / cross-directory LAPS coverage report&lt;/td&gt;
&lt;td&gt;Microsoft Intune compliance reports; manual &lt;code&gt;Get-LapsADPassword&lt;/code&gt; and &lt;code&gt;Get-LapsAADPassword&lt;/code&gt; joins&lt;/td&gt;
&lt;td&gt;DIY KQL across two directories; no unified portal report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid-joined &lt;code&gt;BackupDirectory&lt;/code&gt; ambiguity&lt;/td&gt;
&lt;td&gt;Microsoft Learn guidance (&quot;AD for hybrid&quot;)&lt;/td&gt;
&lt;td&gt;Most shops configure both and reconcile downstream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Win 11 25H2 Administrator Protection and LAPS interaction&lt;/td&gt;
&lt;td&gt;Tech Community guidance; Microsoft Learn architectural notes&lt;/td&gt;
&lt;td&gt;Operate them as orthogonal, with no architectural integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LDAP channel binding / signing enforcement migration&lt;/td&gt;
&lt;td&gt;Microsoft KB4520412 enforcement push 2020-2024; cross-platform tool updates&lt;/td&gt;
&lt;td&gt;Some Linux pentest tooling still incomplete; &lt;code&gt;bloodyAD&lt;/code&gt; / &lt;code&gt;lapsv2decrypt&lt;/code&gt; lead the field [@kb4520412-canonical]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval-event audit gap (cross-directory)&lt;/td&gt;
&lt;td&gt;Event 4662 SACL via &lt;code&gt;Set-LapsADAuditing&lt;/code&gt;; Entra audit log; Defender for Identity hunting&lt;/td&gt;
&lt;td&gt;DIY KQL unification across AD + Entra; no unified audit pane&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;1. Legacy SYSVOL cpassword cleanup at scale.&lt;/strong&gt; MS14-025 disabled new authoring twelve years ago; it never deleted what it patched [@ms14-025-bulletin]. No first-party &lt;code&gt;Find-GPPPassword&lt;/code&gt; or &lt;code&gt;Remove-GPPPassword&lt;/code&gt; cmdlet ships in the OS in 2026. PingCastle&apos;s &lt;code&gt;A-PwdGPO&lt;/code&gt; rule and Semperis Purple Knight&apos;s equivalent scanner fill the gap [@pingcastle-rules]. The 2026 answer is: scan with a third-party tool, rotate the discovered credentials in whatever account-management primitive owns them, then delete the XML. The open question is why Microsoft has not shipped this in the twelve years since the bulletin. The blast-radius argument from 2014 -- &quot;we cannot risk auto-deleting policy XMLs from SYSVOL&quot; -- is now strictly weaker than the cleanup-tail argument that the residual artefacts keep showing up on internal pentest reports a decade later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Cross-tenant and cross-directory LAPS coverage view.&lt;/strong&gt; No portal-level &quot;every Entra-joined and every AD-joined device that does not have a current LAPS password&quot; report exists. Microsoft Intune compliance reports help on the Intune-managed side; &lt;code&gt;Get-LapsADPassword -Identity *&lt;/code&gt; covers the AD side; &lt;code&gt;Get-LapsAADPassword&lt;/code&gt; covers the Entra side. There is no single pane that unifies them. The 2026 answer is custom KQL or PowerShell that joins the three result sets on a normalised device identifier. The bottleneck is identity: Intune device IDs, AD &lt;code&gt;objectGuid&lt;/code&gt; values, and Entra &lt;code&gt;deviceId&lt;/code&gt; values are three different surrogate keys, and a fleet&apos;s mapping table is its own engineering investment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Hybrid-joined &lt;code&gt;BackupDirectory&lt;/code&gt; ambiguity.&lt;/strong&gt; Microsoft Learn&apos;s current guidance is that hybrid-joined devices should typically use &lt;code&gt;BackupDirectory = 2&lt;/code&gt; (AD) when on-premises AD is the canonical identity store, and may use &lt;code&gt;BackupDirectory = 1&lt;/code&gt; (Entra) when Intune is the primary policy-delivery mechanism [@ms-laps-entra-scenarios]. In practice, the documentation hedges, and many shops configure both directions (one via GPO, one via Intune CSP) and rely on the per-device evaluation order to pick one. The result is a coverage-verification problem: a device that is &quot;configured for AD backup&quot; by GPO and &quot;configured for Entra backup&quot; by CSP can end up with the password in either backend, and the source of truth depends on policy precedence rules most operators do not memorise.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Windows 11 25H2 Administrator Protection and LAPS interaction.&lt;/strong&gt; Administrator Protection&apos;s per-elevation transient admin tokens and Windows LAPS&apos;s recoverable break-glass password are operationally adjacent but architecturally disjoint [@tc-admin-protection-win11]. The documentation covers each feature on its own; the interaction matrix -- &quot;what does a LAPS-managed RID 500 look like under Administrator Protection on a Win 11 25H2 host&quot; -- is not laid out in one place. Tier-0 architects who want both behaviours have to assemble the answer from two product pages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. LDAP channel binding and signing enforcement migration.&lt;/strong&gt; Microsoft has been hardening LDAP channel binding through a multi-year 2020-2024 enforcement push tracked under KB4520412 [@kb4520412-canonical]. The original March 10, 2020 update introduced Channel Binding Token (CBT) signing events 3039, 3040, and 3041; the manual enablement step was removed on November 14, 2023 for Windows Server 2022 and on January 9, 2024 for Windows Server 2019, after which the hardening became the default posture; starting with Windows Server 2022 23H2, all new versions ship with the full set of changes in the KB applied [@kb4520412-canonical]. Tooling that does not speak LDAPS-with-channel-binding will break when enforcement reaches its terminal state. Modern attack-graph tooling -- &lt;code&gt;bloodyAD&lt;/code&gt; [@bloodyad-repo] and the &lt;code&gt;lapsv2decrypt&lt;/code&gt; reference implementation [@lapsv2decrypt-repo] -- has tracked the changes. Not every Linux pentest stack has. Practitioners building Linux-based LAPS retrieval pipelines should validate their stack against the channel-binding-required posture before the enforcement wave reaches them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. The retrieval-event audit gap (cross-directory).&lt;/strong&gt; Active Directory does not natively log every read of &lt;code&gt;msLAPS-EncryptedPassword&lt;/code&gt;; &lt;code&gt;Set-LapsADAuditing&lt;/code&gt; installs a SACL that emits Directory Service event 4662 for configured attribute reads [@ms-laps-set-adauditing]. Microsoft Entra ID logs LAPS retrieval through its own audit log, surfaced via the Graph endpoint [@ms-graph-localcredinfo]. The two log streams have different schemas, different timestamp normalisations, and different principal identifiers. Cross-pane unification of &quot;who read which LAPS password when&quot; across both backends is a DIY engineering problem in 2026. Microsoft Defender for Identity surfaces some of the AD-side reads under the Compromised Credentials and Lateral Movement categories [@ms-defender-alerts] but does not name LAPS specifically in the public alert taxonomy.&lt;/p&gt;
&lt;p&gt;The threshold-cryptography open problem (an $m$-of-$n$ Shamir scheme over the LAPS password protector, with $m \geq 2$ in tier-0 fleets) is theoretically closed by the 1979 Shamir secret-sharing construction. The deployment-side block is that no Microsoft-shipped primitive wires the construction to the LAPS rotation pipeline. Adjacent shipping primitives (Azure Managed HSM key-release [@ms-mhsm-policy-grammar], Azure Confidential Ledger [@ms-conf-ledger]) exist on the Azure side, but the integration with on-premises LAPS clients is not on any public roadmap. The companion posts on DPAPI internals (#20) and Defender for Identity (#87) cover adjacent territory but do not close this gap.&lt;/p&gt;
&lt;p&gt;None of those six dissolves the architectural lesson the eleven-year arc taught: the right defaults take a decade to ship. Here is the practitioner field manual for the meantime.&lt;/p&gt;
&lt;h2&gt;11. Practitioner Field Manual and FAQ&lt;/h2&gt;
&lt;p&gt;What follows is a seven-step deployment list, three named sidebars that surface the most common misconceptions, and a seven-question FAQ. Lift the step list verbatim into your deployment runbook; the sidebars exist because the article would not be defensible without them.&lt;/p&gt;
&lt;h3&gt;The audit-and-migrate seven-step list&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Audit SYSVOL for &lt;code&gt;cpassword&lt;/code&gt; first.&lt;/strong&gt; Run PingCastle&apos;s &lt;code&gt;A-PwdGPO&lt;/code&gt; (MITRE T1552.006) [@pingcastle-rules; @mitre-t1552-006] before touching anything else. A Windows triage one-liner -- &lt;code&gt;findstr /s /i cpassword \\domain\SYSVOL\*.xml&lt;/code&gt; -- will land on most environments in under a minute. Remediate the discovered XML files (rotate the underlying account passwords, then delete the XMLs) before deploying Windows LAPS so the attack surface and the defence are not co-evolving in the same window.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extend the AD schema for Windows LAPS.&lt;/strong&gt; Run &lt;code&gt;Update-LapsADSchema&lt;/code&gt; once per forest from a Domain Admin context. The cmdlet is idempotent and coexists with the legacy &lt;code&gt;ms-Mcs-AdmPwd&lt;/code&gt; attribute on the same &lt;code&gt;Computer&lt;/code&gt; object [@ms-laps-mig-scenarios].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delegate.&lt;/strong&gt; Run &lt;code&gt;Set-LapsADComputerSelfPermission&lt;/code&gt; on each target OU so that computer accounts can write their own &lt;code&gt;msLAPS-*&lt;/code&gt; attributes. Audit existing &quot;All Extended Rights&quot; delegations with &lt;code&gt;Find-LapsADExtendedRights&lt;/code&gt; and remove any that do not have an explicit operational justification [@ms-laps-ps-overview]. This is the legacy-LAPS lesson applied to the new attribute set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configure encryption-at-rest.&lt;/strong&gt; Verify that the forest&apos;s Domain Functional Level is Windows Server 2016 or higher. Configure &lt;code&gt;ADPasswordEncryptionEnabled = 1&lt;/code&gt; &lt;em&gt;explicitly&lt;/em&gt; even though the default is True -- the explicit configuration makes the choice visible in policy audits and forces the operator to verify the DFL prerequisite [@ms-laps-policy-settings]. Assign &lt;code&gt;ADPasswordEncryptionPrincipal&lt;/code&gt; to a dedicated &lt;code&gt;LAPS-DPAPI-Decryptors&lt;/code&gt; group, not Domain Admins [@ms-laps-concepts-overview].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy policy.&lt;/strong&gt; GPO for AD-joined, Intune CSP for Entra-joined and hybrid-joined [@ms-laps-csp]. Settings as per section 7&apos;s baseline table. Validate via &lt;code&gt;Get-LapsADPassword -Identity &amp;lt;computer&amp;gt;&lt;/code&gt; against a representative sample of hosts after the first one-hour rotation timer has fired [@ms-laps-get-adpassword].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrate from legacy LAPS.&lt;/strong&gt; Use the documented coexistence pattern: the legacy MSI&apos;s CSE keeps running against the built-in RID 500, the new in-box LAPS takes over against a named secondary local-admin account, then retire the legacy &lt;code&gt;ms-Mcs-AdmPwd&lt;/code&gt; schema readers and uninstall the MSI once Windows LAPS coverage is verified [@ms-laps-mig-scenarios]. The legacy MSI&apos;s installation is blocked on Windows 11 23H2 and later [@ms-laps-msi-download].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous audit.&lt;/strong&gt; PingCastle for coverage rules (&lt;code&gt;A-LAPS-Not-Installed&lt;/code&gt;, &lt;code&gt;A-LAPS-Joined-Computers&lt;/code&gt;, and the GPP &lt;code&gt;A-PwdGPO&lt;/code&gt;) [@pingcastle-rules]; BloodHound for the &lt;code&gt;ReadLAPSPassword&lt;/code&gt; edge across the graph [@bloodhound-edge-readlaps]; Defender for Identity for downstream behaviour under Compromised Credentials and Lateral Movement [@ms-defender-alerts]; and a custom KQL on the Entra audit log for &lt;code&gt;LapsPasswordRetrieved&lt;/code&gt; events. None of these is optional in a deployment that intends to detect compromise.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Sidebar A: MS16-072 is NOT the LAPS attribute-readability bulletin&lt;/h3&gt;
&lt;p&gt;A recurring misattribution credits MS16-072 / KB3163622 / CVE-2016-3223 (June 14, 2016) [@ms16-072-bulletin; @ms16-072-kb; @cve-2016-3223] with closing the legacy LAPS attribute-readability issue. It does not. MS16-072 is a Group Policy retrieval-context fix: it moved user-side GPO fetch into the &lt;em&gt;computer&apos;s&lt;/em&gt; security context to defeat a man-in-the-middle class on policy traffic. The actual LAPS attribute-readability issue -- &quot;All Extended Rights&quot; delegations silently including &lt;code&gt;CONTROL_ACCESS&lt;/code&gt; on the CONFIDENTIAL &lt;code&gt;ms-Mcs-AdmPwd&lt;/code&gt; attribute -- has no Microsoft-assigned CVE or bulletin. The canonical write-up is Sean Metcalf&apos;s August 2016 ADSecurity piece [@adsec-laps-2016], and the operational primitive is SpecterOps&apos;s &lt;code&gt;ReadLAPSPassword&lt;/code&gt; BloodHound edge [@bloodhound-edge-readlaps].&lt;/p&gt;
&lt;h3&gt;Sidebar B: &quot;Hybrid joined&quot; is not &quot;Hybrid Worker&quot;&lt;/h3&gt;
&lt;p&gt;Microsoft Entra &lt;em&gt;hybrid joined&lt;/em&gt; devices are workstations joined to both an on-premises AD domain and Microsoft Entra ID. The LAPS conversation about hybrid joined is a &lt;code&gt;BackupDirectory&lt;/code&gt; choice. Microsoft Entra &lt;em&gt;hybrid runbook workers&lt;/em&gt;, on the other hand, are an Azure Automation primitive -- worker processes that execute Automation runbooks against on-premises resources. They share a word and nothing else. A LAPS policy targeted at &quot;hybrid devices&quot; means hybrid joined; it has nothing to do with hybrid runbook workers. The article&apos;s section 8 includes the same disambiguation because operators conflate them with surprising frequency.&lt;/p&gt;
&lt;h3&gt;Sidebar C: How GPP cpassword still gets found in 2026&lt;/h3&gt;
&lt;p&gt;MS14-025 disabled new authoring but did not delete the artefacts [@ms14-025-bulletin]. The artefacts persist because SYSVOL replication is conservative -- nothing in the forest&apos;s design &lt;em&gt;deletes&lt;/em&gt; anything from SYSVOL just because the editor UI was hot-patched on the administrative workstation. A fresh PingCastle scan against a long-lived forest will routinely surface 2010-era &lt;code&gt;Groups.xml&lt;/code&gt; files [@pingcastle-rules], and the third-party scanner cohort is the only practical defence. The one-shot remediation pattern is: find with &lt;code&gt;A-PwdGPO&lt;/code&gt;, rotate the underlying password via the replacement tool (Windows LAPS for built-in local admin; a PAM vault for service accounts that were stored in GPP), then delete the &lt;code&gt;Groups.xml&lt;/code&gt; and let SYSVOL replication propagate the deletion.&lt;/p&gt;

No. Administrator Protection addresses in-session UAC-class elevation by brokering each elevation through a per-elevation transient shadow-admin identity [@tc-admin-protection-win11]; it does not provide a recoverable break-glass password for an off-network or non-bootable endpoint. The two systems are orthogonal and Microsoft recommends running them together on Windows 11 25H2 fleets. Replacing LAPS with Administrator Protection produces designs that lose the recovery primitive for laptops that have wandered off the corporate network for a year.

Defence in depth, plus a coverage-leak primitive. An LDAP reader who is not in `ADPasswordEncryptionPrincipal` gets only an opaque ciphertext blob [@ms-laps-concepts-overview] -- but the same reader can still enumerate which computer objects have a current `msLAPS-EncryptedPassword`, which gives them target-selection telemetry on managed-versus-unmanaged hosts. The canonical write-up of this class is Sean Metcalf&apos;s August 2016 ADSecurity piece on the legacy `ms-Mcs-AdmPwdExpirationTime` attribute [@adsec-laps-2016], and the architectural lesson carries forward to Windows LAPS unchanged.

Yes, in seconds. The 32-byte AES-256-CBC key is published verbatim in `[MS-GPPREF]` section 2.2.1.1.4 of Microsoft&apos;s Open Specifications corpus [@ms-gppref-aes-key] and that publication is permanent under the Open Specifications Promise. Any residual `Groups.xml` (or five sibling carriers including the asymmetric `Printers.xml` [@rewtdance-gpp-2012]) in SYSVOL that contains a `cpassword` attribute is operationally plaintext. The 2026 answer is to find them with PingCastle&apos;s `A-PwdGPO` rule [@pingcastle-rules] and remediate -- not to expect the artefacts to expire on their own.

No. The rotation cycle is the `PasswordAgeDays` interval (default 30 days, minimum 1 on AD backup, minimum 7 on Entra backup) [@ms-laps-policy-settings]. After authentication, `PostAuthenticationActions` (default `3` = reset + sign out) fires once the `PostAuthenticationResetDelay` window (default 24 hours) has elapsed. Value `11` (Windows 11 24H2 / Server 2025+) adds termination of remaining processes; it is *not* a forced shutdown without warning -- the standard two-minute warning still applies and SMB sessions are deleted.

Yes. LAPS rotates the password on a disabled account; the account simply cannot be used to log on until it is enabled. The break-glass runbook is: enable the account, retrieve the LAPS password, perform the recovery, rotate immediately, re-disable. On Windows 11 24H2 and Windows Server 2025 and later, Microsoft&apos;s recommendation is to enable Automatic Account Management with a randomised name and `AutomaticAccountManagementEnableAccount = 0` so the managed account ships disabled-by-default with a non-predictable name [@ms-laps-account-modes]. The pattern defeats predictable-RID-500 enumeration entirely.

Microsoft Entra ID. With `BackupDirectory = 1` [@ms-laps-policy-settings], the local LAPS component posts the rotated password to the `deviceLocalCredentials` resource on the Entra device object via Microsoft Graph [@ms-graph-localcredinfo]. Retrieval is via `Get-LapsAADPassword` (a wrapper around the Graph endpoint), the Microsoft Entra portal Devices blade, or a direct `GET /directory/deviceLocalCredentials/{deviceId}` call [@ms-laps-entra-scenarios]. Read permission requires the Cloud Device Administrator or Intune Service Administrator Entra role.

No. `CanReadGMSAPassword` is the edge for **Group Managed Service Accounts** -- a different Active Directory feature with a different ACL on a different attribute (`msDS-GroupMSAMembership`). The correct LAPS edge is **`ReadLAPSPassword`**, introduced in BloodHound 2.0 on August 7, 2018 [@specterops-bh2], and the current edge documentation covers both the legacy `ms-Mcs-AdmPwd` and the modern `msLAPS-*` attribute paths [@bloodhound-edge-readlaps].
&lt;p&gt;The companion posts in this series cover Pass-the-Hash itself (#76), DPAPI internals (#20), Microsoft Entra Privileged Identity Management (#90), Active Directory tiering (#72), Microsoft Defender for Identity (#87), and BloodHound (#77). Each of those is referenced in this article at the point where the topic would otherwise demand a digression; each has its own deep treatment elsewhere.&lt;/p&gt;
&lt;p&gt;Twenty years. Eleven years of which separated Microsoft&apos;s December 2012 articulation of the architecture from the April 11, 2023 in-box default [@ms-pth-whitepaper; @tc-windows-laps-ga-2023]. Four residual attack surfaces -- delegated-decryptor compromise, the pre-rotation OPSEC tail, BYOD endpoints, and the multi-decade MS14-025 cleanup tail [@ms14-025-bulletin] -- still resist the architecture rather than fall to it. One through-line: this is what shipping the right default a decade late looks like. The right defaults are now in the box. The directory is still tier 0. Somebody still has to break the glass. The architectural game from here is not to invent a new generation; it is to make sure the one we finally have is actually deployed, audited, and clean.&lt;/p&gt;
</content:encoded><category>windows</category><category>active-directory</category><category>laps</category><category>group-policy</category><category>identity</category><category>credential-theft</category><category>entra-id</category><author>noreply@paragmali.com (Parag Mali)</author></item><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>Inside the Primary Refresh Token: The Cryptographic Seam Between Windows Logon and Microsoft Entra ID</title><link>https://paragmali.com/blog/inside-the-primary-refresh-token-the-cryptographic-seam-betw/</link><guid isPermaLink="true">https://paragmali.com/blog/inside-the-primary-refresh-token-the-cryptographic-seam-betw/</guid><description>How one TPM-bound JWT issued at first sign-in bridges Windows logon and Microsoft Entra ID -- and how Pass-the-PRT taught Microsoft to bind the derivation to the message.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
The **Primary Refresh Token (PRT)** is the cryptographic seam where a Windows logon becomes a Microsoft Entra ID transaction. It is a JWT issued by Microsoft Entra ID to the CloudAP plugin in `lsass` at first interactive sign-in on an Entra-registered, Entra-joined, or Entra-hybrid-joined device. The PRT is signed at issuance by a TPM-bound **device key** (`dkpriv`); every downstream artifact -- the `x-ms-RefreshTokenCredential` browser cookie, app-token requests via WAM, Conditional Access claim flow -- is signed by a session key returned encrypted under the device&apos;s **transport key** (`tkpub`). In 2020, Dirk-jan Mollema and Lee Christensen showed that even with TPM-bound keys, admin on the live device could mint cookies anywhere -- the Pass-the-PRT class. Microsoft closed off-device replay with **KDFv2** (CVE-2021-33779, July 2021), then layered Continuous Access Evaluation, Token Protection, and Cloud Kerberos Trust on top. On-device Cookie-on-Demand attacks remain the open residual.
&lt;h2&gt;1. Three sign-ins, one credential&lt;/h2&gt;
&lt;p&gt;A user signs into a freshly enrolled Entra-joined laptop with &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;Windows Hello for Business&lt;/a&gt;. Ten seconds later they open Outlook, which silently authenticates against Microsoft 365. An hour later they type &lt;code&gt;outlook.office.com&lt;/code&gt; into Edge -- and they are already signed in there too.&lt;/p&gt;
&lt;p&gt;Three sign-ins, one credential. The credential was issued during the Windows logon itself, and the user has never seen it.&lt;/p&gt;
&lt;p&gt;This article is about that credential -- the &lt;strong&gt;Primary Refresh Token&lt;/strong&gt; -- and about the cryptographic seam where Windows logon stops being a local NT-style event and becomes a Microsoft Entra ID transaction.&lt;/p&gt;

A device-bound JSON Web Token issued by Microsoft Entra ID to the Cloud Authentication Provider in `lsass.exe` at first interactive sign-in on a Microsoft Entra-registered, Entra-joined, or Entra-hybrid-joined device. The PRT is the artifact every other token broker on the device references to mint app access tokens, browser SSO cookies, and Conditional Access claims for the lifetime of the sign-in session [@prt-msft-learn].
&lt;p&gt;The questions worth asking are concrete. What does that token actually contain? How did it get from &lt;code&gt;lsass&lt;/code&gt; to a browser cookie without the user ever pasting it? Why is the cookie that rides in the browser called &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; when the PRT itself never leaves the device? And -- the question that will define everything in §5 and §6 -- if the credential is bound to a TPM, how did three independent researchers in the summer of 2020 mint cookies anywhere they wanted to?&lt;/p&gt;
&lt;p&gt;The plan is to answer those questions in order. We will name every load-bearing primitive in the stack. We will walk a token request end-to-end. We will explain what the July 2021 KDFv2 patch actually changed at the byte level. And we will be honest about what the PRT cannot do -- because the rest of this series is about the identity surfaces that run alongside it, not under it.&lt;/p&gt;
&lt;p&gt;Before we can read the PRT itself, we have to understand the problem it was built to solve. That means going back to 2013, before Azure AD Join was a thing.&lt;/p&gt;
&lt;h2&gt;2. The cloud-identity gap, 2011 to 2014&lt;/h2&gt;
&lt;p&gt;Windows authentication, in 2011, did not speak cloud. &lt;a href=&quot;https://paragmali.com/blog/ntlmless-the-death-of-ntlm-in-windows/&quot; rel=&quot;noopener&quot;&gt;NTLM&lt;/a&gt; resolved against a local SAM database. &lt;a href=&quot;https://paragmali.com/blog/kerberos-in-windows-the-other-half-of-ntlmless/&quot; rel=&quot;noopener&quot;&gt;Kerberos&lt;/a&gt; resolved against an on-prem Key Distribution Center. Both predate the notion of a cloud identity provider by more than a decade. When a Windows endpoint authenticated, it talked to a domain controller it could see on the network -- and if it could not see a domain controller, it talked to the local SAM and called it a day.&lt;/p&gt;
&lt;p&gt;For a cloud-only workload, that left a gap shaped like a question. Where, exactly, does the user&apos;s identity live when there is no on-prem domain to resolve it against?&lt;/p&gt;
&lt;p&gt;The first answer was OAuth. RFC 6749 had shipped in October 2012, edited by Dick Hardt while at Microsoft, with refresh tokens explicitly modeled as long-lived bearer credentials redeemed at a token endpoint for short-lived access tokens [@rfc-6749]. Microsoft&apos;s Active Directory Authentication Library -- ADAL -- took the obvious next step: every application that wanted to talk to Microsoft&apos;s cloud APIs got its own client, its own redirect, and its own refresh token. SSO was approximated by sharing the underlying password prompt or, on a domain-joined machine, by hoping Integrated Windows Authentication smuggled the right Kerberos ticket to the right endpoint.&lt;/p&gt;
&lt;p&gt;That patchwork held for a while. It also taught Microsoft two things.&lt;/p&gt;
&lt;p&gt;The first lesson was about Conditional Access. If every app maintained its own refresh-token cache and re-presented credentials independently, the policy engine could only see what each token request happened to surface. Whether the request came from a managed Surface or from an unmanaged consumer laptop was anyone&apos;s guess. The device, in other words, was invisible.&lt;/p&gt;
&lt;p&gt;The second lesson was about the user. Ten apps meant ten silent renewal pipelines, ten password prompts when those pipelines broke, and ten different broker components asking &quot;are you sure?&quot; in slightly different language. The user experience and the security posture were on the same side of the ledger: both wanted a single device-bound credential that every broker could reference.&lt;/p&gt;
&lt;p&gt;The first move was small. On 28 June 2013, Adam Hall announced &lt;strong&gt;Workplace Join&lt;/strong&gt; as part of Windows Server 2012 R2: a device-registration primitive that put an X.509 certificate from the Device Registration Service into Active Directory, so that &quot;users can register their device using Workplace Join which creates a new device object in Active Directory and installs a certificate on the device, allowing IT to take into account the user&apos;s device authentication as part of conditional access policies&quot; [@workplace-join-2013].&lt;/p&gt;
&lt;p&gt;Workplace Join taught the directory that a device existed. It did not make the Windows sign-in itself a cloud event. The artifact it produced was a long-lived certificate, not a session-scoped credential, and it lived on the on-prem AD side of the seam, not the cloud side. For the rest, Microsoft would need a credential the cloud could mint &lt;em&gt;during&lt;/em&gt; the sign-in.&lt;/p&gt;
&lt;p&gt;That credential arrived in 2015 -- but its design took another year to harden.&lt;/p&gt;
&lt;h2&gt;3. Workplace Join, Azure AD Join, and the OAuth-refresh-token patchwork&lt;/h2&gt;
&lt;p&gt;What does it cost a Windows endpoint to authenticate to ten cloud apps if it has no PRT?&lt;/p&gt;
&lt;p&gt;Counting tokens is a good way to find out. Each app maintains its own refresh-token cache. Each refresh redeems against the same &lt;code&gt;login.microsoftonline.com&lt;/code&gt; endpoint but with a different &lt;code&gt;client_id&lt;/code&gt; and a different audience claim. Each app re-asserts the device claim as a separate transaction -- if it can; an app that does not ride a broker can only surface what its own credential flow knows. The architectural failure mode is not that authentication is &lt;em&gt;bad&lt;/em&gt;; it is that authentication is &lt;em&gt;redundant&lt;/em&gt;, and the policy engine sees a hundred small claims instead of one big one.&lt;/p&gt;
&lt;p&gt;Microsoft walked out of that failure mode in three steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step one (June 2013): Workplace Join.&lt;/strong&gt; A device cert, signed by the Device Registration Service, written to a new device object in Active Directory. Adam Hall&apos;s announcement is the load-bearing primary source [@workplace-join-2013]. Nothing about a session: the certificate lives across reboots, across sign-ins, across user accounts. Microsoft now calls this state &lt;strong&gt;Microsoft Entra registered&lt;/strong&gt; -- the same primitive, renamed [@entra-devices-overview].&quot;Workplace Join&quot; was the 2013 marketing name. The same artifact is now called &quot;Microsoft Entra registered&quot; and is the device state used for personal (BYOD) devices that get conditional-access policies applied to corporate workloads. The taxonomy in §3 of the current Microsoft Learn documentation lists three states: Microsoft Entra registered, Microsoft Entra joined, and Microsoft Entra hybrid joined [@entra-devices-overview].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step two (May 2015): Azure AD Join.&lt;/strong&gt; On 28 May 2015, Alex Simons and Gary Henderson announced that Windows 10, build 1507, would let a device sign in against a cloud-only Microsoft identity at first boot. &quot;Azure AD join is optimized for users that primarily access cloud resources,&quot; the announcement reads -- a quiet way of saying that for the first time, a Windows machine did not need a domain controller on the network to give a user a sign-in surface [@techcomm-azure-ad-join-2015].&lt;/p&gt;
&lt;p&gt;This 28 May 2015 Tech Community post is the corrected primary source. An older URL in the same series (&lt;code&gt;.../ba-p/247010&lt;/code&gt;) was re-tagged by Microsoft&apos;s CMS to a 2010 RemoteFX article and now resolves to unrelated content; the 244005 post is the load-bearing technical announcement.&lt;/p&gt;
&lt;p&gt;The Azure AD Join story introduced one more component: &lt;strong&gt;CloudAP&lt;/strong&gt;, the Cloud Authentication Provider, an authentication-package framework hosted inside &lt;code&gt;lsass.exe&lt;/code&gt;. CloudAP is the LSASS-resident broker that an enterprise SSO surface talks to from inside the operating system. It is not yet a PRT engine -- in May 2015, it is mostly a routing layer for cloud sign-in primitives. The PRT itself does not exist yet.&lt;/p&gt;

A pluggable authentication-package framework introduced inside `lsass.exe` to host cloud-identity sign-in plugins. The Microsoft Entra ID plugin (`aadcloudap.dll`) is the canonical implementation; CloudAP is the LSASS-resident broker that, from Windows 10 1607 onward, owns the device-side PRT lifecycle on Entra-joined and Entra-hybrid-joined machines [@prt-msft-learn].
&lt;p&gt;&lt;strong&gt;Step three (August 2016): the first PRT.&lt;/strong&gt; Windows 10, version 1607 -- the Anniversary Update -- began rolling out on 2 August 2016 [@win10-anniv-1607]. In that build, CloudAP gained an Entra ID plugin that minted a PRT during interactive sign-in, alongside 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;TPM&lt;/a&gt;-bound key pair for proof of possession. From that moment, every other broker on the machine -- the Web Account Manager that backed native apps, Edge for browser SSO, third-party &lt;code&gt;mstsc&lt;/code&gt; flows that wanted to redirect a sign-in -- had a single artifact to reference. The architectural gap from §2 closed; the patchwork became a stack.&lt;/p&gt;
&lt;p&gt;By the time Microsoft Open Specifications publication MS-OAPXBC went public on 16 October 2015, version 1.0 -- contemporaneous with the Windows 10 1507 release, not three years later -- the protocol scaffolding was already in place [@ms-oapxbc-index]. The PRT itself was the credential the scaffolding had been waiting for.&lt;/p&gt;
&lt;p&gt;By 2016, Microsoft had a name for the missing primitive: one device-bound, session-scoped, cloud-issued credential that all brokers could reference. The Anniversary Update made it real. The next question is what that credential &lt;em&gt;is&lt;/em&gt; cryptographically -- and to answer that, we need to be precise about two key pairs that most descriptions of the PRT conflate.&lt;/p&gt;

timeline
    title PRT generations, 2013 to 2022
    2013 : Workplace Join (Windows Server 2012 R2)
         : Device cert in AD; no session credential
    2015 : Azure AD Join (Windows 10 1507)
         : CloudAP framework in lsass; no PRT yet
    2016 : First PRT (Windows 10 1607)
         : CloudAP + Entra plugin issue device-bound JWT
    2020 : Pass-the-PRT class disclosed
         : Christensen + Mollema + Syynimaa
    2021 : KDFv2 (CVE-2021-33779)
         : SHA256 of payload mixed into derivation
    2022 : CAE GA + Cloud Kerberos Trust + TROOPERS 22
         : Composition era begins
&lt;h2&gt;4. The two-key cryptographic model&lt;/h2&gt;
&lt;p&gt;Most descriptions of the PRT online say the cookie is &quot;DKey-signed.&quot; That phrase has been wrong since July 2021. Here is the actual cryptographic substrate.&lt;/p&gt;
&lt;p&gt;When a Windows device joins Microsoft Entra ID -- by way of the Out-of-Box Experience, by &lt;code&gt;dsreg&lt;/code&gt;&apos;s join command, or by the implicit registration that happens on a personal device -- the registration component generates &lt;strong&gt;two&lt;/strong&gt; key pairs on the device. One pair signs PRT &lt;em&gt;issuance&lt;/em&gt; requests. The other unwraps session keys returned &lt;em&gt;with&lt;/em&gt; the PRT. Microsoft&apos;s own documentation enumerates the two pairs the &lt;code&gt;dsreg&lt;/code&gt; component generates at device registration: Device key (&lt;code&gt;dkpub&lt;/code&gt;/&lt;code&gt;dkpriv&lt;/code&gt;) and Transport key (&lt;code&gt;tkpub&lt;/code&gt;/&lt;code&gt;tkpriv&lt;/code&gt;) [@prt-msft-learn].&lt;/p&gt;

The first of the two key pairs minted at Microsoft Entra registration. The private half (`dkpriv`) is TPM-resident on supported hardware (TPM 2.0 from Windows 10 1903 onward) and signs the JWT used to *request* a Primary Refresh Token from Microsoft Entra ID. The public half (`dkpub`) is registered with Microsoft Entra ID at join time and is what Entra ID uses to verify that the request originated from the registered device [@prt-msft-learn].

The second registration-time key pair. Entra ID encrypts the freshly minted PRT session key under `tkpub`; only `tkpriv` -- TPM-resident on supported hardware -- can unwrap it. Every downstream signing operation flows through a key derived from that session key, so the transport key is the asymmetric on-ramp to the device&apos;s symmetric proof-of-possession surface [@prt-msft-learn].

The Windows component that performs Microsoft Entra registration -- mints the device and transport key pairs, registers `dkpub`/`tkpub` with Entra ID, and produces the device certificate that backs the Microsoft Entra device object. `dsregcmd.exe` is its operator-facing interrogation tool; `dsregcmd /status` reports current state including AzureAdPrt, AzureAdPrtUpdateTime, and AzureAdPrtExpiryTime [@prt-msft-learn].
&lt;p&gt;The two-key model is not a typo, and the second-most-common reading of it is wrong. The device key signs &lt;em&gt;the request for&lt;/em&gt; a PRT. The transport key unwraps &lt;em&gt;the session key that arrives with&lt;/em&gt; a PRT. Once unwrapped, the session key signs everything from there on -- not the device key.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The device key signs PRT issuance, once per PRT mint. The transport key unwraps a &lt;em&gt;session key&lt;/em&gt;. Every downstream artifact -- the &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; browser cookie, every WAM-mediated app-token request -- is signed by a key &lt;em&gt;derived from&lt;/em&gt; that session key, not by &lt;code&gt;dkpriv&lt;/code&gt; directly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The eight-step issuance flow makes this explicit.&lt;/p&gt;

sequenceDiagram
    participant User
    participant CloudAP as CloudAP (lsass)
    participant TPM
    participant Entra as Microsoft Entra ID
    participant CA as Conditional Access
    participant WAM
    User-&amp;gt;&amp;gt;CloudAP: 1. Interactive sign-in (Hello, password, FIDO2)
    CloudAP-&amp;gt;&amp;gt;TPM: 2. Sign authorization JWT with dkpriv
    TPM--&amp;gt;&amp;gt;CloudAP: 3. Signed assertion
    CloudAP-&amp;gt;&amp;gt;Entra: 4. Issuance request (signed assertion)
    Entra-&amp;gt;&amp;gt;CA: 5. Evaluate device + user + risk claims
    CA--&amp;gt;&amp;gt;Entra: 6. Issuance permitted
    Entra--&amp;gt;&amp;gt;CloudAP: 7. PRT + session_key encrypted under tkpub
    CloudAP-&amp;gt;&amp;gt;TPM: 8. Unwrap session_key with tkpriv
    Note over CloudAP,WAM: Session key now resident -- WAM, browser SSO, and CAE all derive from it
&lt;p&gt;A user provides an interactive credential -- a Hello gesture, a password, a FIDO2 security key. The CloudAP plugin in &lt;code&gt;lsass&lt;/code&gt; constructs a JWT carrying the user&apos;s authorization material and asks the TPM to sign it with &lt;code&gt;dkpriv&lt;/code&gt;. That signed assertion goes to Microsoft Entra ID. Entra evaluates Conditional Access; if the device, the user, and the risk profile pass policy, Entra returns a PRT (a long-lived JWT) and a fresh session key encrypted under &lt;code&gt;tkpub&lt;/code&gt;. The TPM unwraps the session key with &lt;code&gt;tkpriv&lt;/code&gt;. The session key now lives on the device, in CloudAP&apos;s hot path, available for every broker to use.&lt;/p&gt;

The symmetric key Microsoft Entra ID generates per PRT mint and returns to the device encrypted under `tkpub`. After the TPM unwraps it with `tkpriv`, the session key is the *proof-of-possession key* for the PRT lifetime: every renewal request, every `x-ms-RefreshTokenCredential` cookie, and every app-token request signed via the Web Account Manager is HMAC-signed by a key *derived from* the session key via SP800-108 KDF [@prt-msft-learn] [@ms-oapxbc-jwt].
&lt;p&gt;The session key is the part the rest of this article keeps coming back to. It is the artifact that, in 2020, three independent researchers would prove the TPM was not protecting in the way Microsoft&apos;s documentation implied.&lt;/p&gt;
&lt;p&gt;Once the session key is on the device, the &lt;strong&gt;Web Account Manager (WAM)&lt;/strong&gt; -- the user-mode broker process that handles native-app token requests -- and the browser SSO surface used by Edge, Chrome, and Firefox can mint subordinate artifacts. The most interesting one is a cookie.&lt;/p&gt;

The Windows user-mode broker that mediates access-token requests from native applications to Microsoft Entra ID. WAM presents each app-token request alongside a PRT-derived signed assertion, eliminating the per-app refresh-token cache that the pre-2016 ADAL design required. WAM is the Windows analogue of the Microsoft Enterprise SSO plug-in for Apple devices [@prt-msft-learn] [@apple-sso-plugin-learn].

The HTTP cookie Edge, Chrome, and Firefox attach to requests against `login.microsoftonline.com` and a small set of Microsoft cloud surfaces. It carries a JWT signed with `alg: HS256` whose header field `kdf_ver` indicates whether the cookie used KDFv1 or KDFv2 derivation [@ms-oapxbc-jwt]. The cookie is what makes the third sign-in in the §1 hook -- the silent Edge sign-in to `outlook.office.com` -- not require a credential prompt.
&lt;p&gt;Inside that cookie, the signing key is &lt;strong&gt;derived&lt;/strong&gt; from the session key via the SP800-108 key-derivation function. The label is the constant string &lt;code&gt;AzureAD-SecureConversation&lt;/code&gt;. The context (&lt;code&gt;ctx&lt;/code&gt;) is a per-cookie value chosen by the client. The MS-OAPXBC protocol specification gives the rule verbatim: under KDFv2, &quot;the client MUST use SHA256(ctx || assertion payload) instead of ctx as the context for deriving the signing key&quot; [@ms-oapxbc-jwt]. We will come back to that sentence in §6, because it is &lt;em&gt;the&lt;/em&gt; sentence.Microsoft Learn documents TPM 2.0 as the recommended version for all Microsoft Entra device-registration scenarios on Windows 10 or newer, and states that after the Windows 10 1903 update, Microsoft Entra ID no longer uses TPM 1.2 for any of the PRT keys due to reliability issues. In practice, TPM 2.0 is the only supported configuration on Windows 10 1903 or higher [@prt-msft-learn].&lt;/p&gt;
&lt;p&gt;On supported hardware, both &lt;code&gt;dkpriv&lt;/code&gt; and &lt;code&gt;tkpriv&lt;/code&gt; are non-extractable TPM 2.0 keys. On a device with &lt;a href=&quot;https://paragmali.com/blog/pluton-a-tpm-on-silicon-microsoft-can-patch/&quot; rel=&quot;noopener&quot;&gt;Microsoft Pluton&lt;/a&gt; (a TPM 2.0 implementation embedded in the SoC), the same model applies; Pluton is a TPM 2.0 implementation, not a replacement. On non-TPM Windows -- a virtual machine without a vTPM, a desktop where the TPM is disabled, certain consumer SKUs -- DPAPI is the fallback. DPAPI-protected keys live in user-profile state and can be unwrapped with the user&apos;s credentials, which is a meaningfully weaker contract than TPM non-extractability. We will come back to that distinction in §9.&lt;/p&gt;

The shorthand &quot;the PRT cookie is DKey-signed&quot; was already imprecise before July 2021, and it became actively wrong after the KDFv2 update. The cookie is HMAC-signed with `alg: HS256`, using a symmetric key derived from the *session key* via SP800-108 KDF, not signed with the asymmetric device key. blog.3or.de&apos;s reverse-engineering captures the post-2021 mechanic precisely: &quot;Before CVE-2021-33779, the key to sign the PRT Cookie was derived from the session key using a function that only required a client-chosen `ctx` value. Although the session key and derivation process were handled inside the TPM, the derived key was managed outside the TPM&quot; [@dimi-3or-de-kdfv2]. The asymmetric device key only signs the PRT *issuance* request; everything afterwards is HMAC over a derived key.
&lt;p&gt;If both keys live in the TPM and the cookie is signed with a key derived from a TPM-resident session key, the whole architecture &lt;em&gt;should&lt;/em&gt; make Pass-the-PRT impossible. In 2020, three independent researchers proved it didn&apos;t.&lt;/p&gt;
&lt;h2&gt;5. When TPM-binding is not enough&lt;/h2&gt;
&lt;p&gt;In July 2020, two researchers, working independently, asked the same question: if the session key is in the TPM, can I still mint a PRT cookie?&lt;/p&gt;
&lt;p&gt;The answer, on the architecture Microsoft shipped at the time, was yes -- and the answer came from three angles in less than two months.&lt;/p&gt;

A Primary Refresh Token can be compared to a long-term persistent Ticket Granting Ticket (TGT) in Active Directory... the Primary Refresh Token however can be used to authenticate to any application, and is thus even more valuable. This is why Microsoft has applied extra protection to this token. -- Dirk-jan Mollema, 21 July 2020
&lt;p&gt;&lt;strong&gt;Lee Christensen at SpecterOps, mid-July 2020.&lt;/strong&gt; Christensen&apos;s blog post -- &quot;Requesting Azure AD Refresh Tokens on Azure AD-joined Machines for Browser SSO&quot; -- documented a path through a Component Object Model interface, &lt;code&gt;IProofOfPossessionCookieInfoManager.GetCookieInfoForUri&lt;/code&gt;, that returned a fully signed &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; cookie to a user-mode caller [@christensen-specterops-2020]. The CLSID is &lt;code&gt;{a9927f85-a304-4390-8b23-a75f1c668600}&lt;/code&gt;; the implementation lives in &lt;code&gt;MicrosoftAccountTokenProvider.dll&lt;/code&gt;; the workflow rides through &lt;code&gt;BrowserCore.exe&lt;/code&gt; over a named pipe. Christensen released the proof-of-concept as &lt;code&gt;RequestAADRefreshToken&lt;/code&gt; on GitHub [@gh-requestaadrefreshtoken]. An attacker -- specifically, a process running as the signed-in user -- could call the COM interface, lift the cookie, and paste it into a browser running anywhere on the planet.&lt;/p&gt;
&lt;p&gt;The COM-API path did not require admin. It did not require touching the TPM. It did not need to know anything about the session key. The operating system politely produced a signed cookie because that is what the COM API was built to do, and the contract did not distinguish the legitimate browser from the attacker process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dirk-jan Mollema, 21 July 2020.&lt;/strong&gt; A week later, Mollema published &quot;Abusing Azure AD SSO with the Primary Refresh Token&quot; on &lt;code&gt;dirkjanm.io&lt;/code&gt;. Mollema&apos;s framing was different: he wanted to understand the PRT as a forensic artifact. The blog opens with the TGT analogy quoted above and explicitly attributes parallel discovery to Christensen [@mollema-prt-2020-07]. The toolchain he documented, ROADtoken, lived inside the larger ROADtools framework that he was building for offensive Azure AD research [@gh-roadtools]. The threat model was the same as Christensen&apos;s: an attacker on the live device could mint cookies, and the TPM was not in the way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mollema, 5 August 2020.&lt;/strong&gt; This is the blog that mattered most. In &quot;Digging further into the Primary Refresh Token,&quot; Mollema reverse-engineered &lt;code&gt;aadcloudap.dll&lt;/code&gt;. He isolated the session-key handling, the cookie-construction routine, the SP800-108 derivation call, the eventual &lt;code&gt;BCryptKeyDerivation&lt;/code&gt;-then-HMAC flow. And he wrote the sentence that, in retrospect, defined the next year of Microsoft&apos;s response: &quot;despite the session key of the PRT is stored in the TPM whenever possible, this doesn&apos;t prevent us from extracting the PRT and the required information to create SSO cookies. The result of this is that regardless of whether the PRT is protected by the TPM or not, with Administrator access it is possible to extract the PRT from LSASS and use the PRT on a different device than it was issued to&quot; [@mollema-prt-2020-08].&lt;/p&gt;

despite the session key of the PRT is stored in the TPM whenever possible, this doesn&apos;t prevent us from extracting the PRT and the required information to create SSO cookies. -- Dirk-jan Mollema, 5 August 2020
&lt;p&gt;The reason is the most important thing in this article. The session key never left the TPM. But the &lt;em&gt;signing key derived from the session key&lt;/em&gt; did. The TPM dutifully performed an SP800-108 derivation -- HMAC-SHA256 with the label &lt;code&gt;AzureAD-SecureConversation&lt;/code&gt; and the client-chosen &lt;code&gt;ctx&lt;/code&gt; value -- and returned the derived key to caller memory. The TPM was protecting the root of the derivation, not the output of it. Once the derived key materialized in &lt;code&gt;lsass&lt;/code&gt;, an admin-with-debug-privilege attacker could simply read it.&lt;/p&gt;
&lt;p&gt;Around the same time, Benjamin Delpy -- the author of Mimikatz -- picked up Mollema&apos;s &quot;challenge&quot; of recovering PRT data from &lt;code&gt;lsass&lt;/code&gt;. Two days after Mollema&apos;s 5 August post, that collaboration produced the Mimikatz release tagged &lt;code&gt;2.2.0-20200807&lt;/code&gt;, which added the &lt;code&gt;sekurlsa::cloudap&lt;/code&gt; and &lt;code&gt;dpapi::cloudapkd&lt;/code&gt; modules [@gh-mimikatz]. The tag URL itself was later collapsed in GitHub&apos;s modern UI -- it returns 404 today, almost certainly because of repeated takedown requests during the Azure-PRT release period -- but a Wayback Machine snapshot from 20 September 2020 preserves the release page and proves the tag existed at the time [@wayback-mimikatz-tag].The GitHub URL &lt;code&gt;https://github.com/gentilkiwi/mimikatz/releases/tag/2.2.0-20200807&lt;/code&gt; returns HTTP 404 in the current GitHub UI; the modern releases list starts at &lt;code&gt;2.2.0-20210729&lt;/code&gt;. The Wayback snapshot at &lt;code&gt;web.archive.org/web/20200920005113/...&lt;/code&gt; preserves the release page (including the &quot;prt3&quot; animated demonstration GIF). Nestori Syynimaa&apos;s AADInternals post and Mollema&apos;s 5 August 2020 blog both reference the same tag URL, which is how we know the artifact was real [@wayback-mimikatz-tag] [@syynimaa-aadinternals-prt] [@mollema-prt-2020-08].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nestori Syynimaa and AADInternals, August through September 2020.&lt;/strong&gt; Syynimaa&apos;s AADInternals PowerShell module shipped &lt;code&gt;Get-AADIntUserPRTToken&lt;/code&gt; as part of v0.4.1 alongside the disclosure. On 29 September 2020, AADInternals&apos; blog post about the tool gained an inline update: &quot;It seems that PRT tokens must now include the &lt;code&gt;request_nonce&lt;/code&gt;. If not, Azure AD sends a redirect with &lt;code&gt;sso_nonce&lt;/code&gt; which must be added to the PRT token. This means that without access to session key, PRT tokens can&apos;t be used anymore&quot; [@syynimaa-aadinternals-prt]. That update is the first observable Microsoft mitigation: Entra ID began demanding that PRT cookies contain a server-issued nonce. It bought time. It did not solve the architectural problem.&lt;/p&gt;

sequenceDiagram
    participant Attacker
    participant LSASS
    participant TPM
    participant COM as IProofOfPossessionCookieInfoManager
    participant Entra as Microsoft Entra ID
    Note over Attacker,LSASS: Attacker has user or admin on the live device
    Attacker-&amp;gt;&amp;gt;LSASS: sekurlsa::cloudap (admin path)
    LSASS--&amp;gt;&amp;gt;Attacker: PRT + derived signing key + context
    Note over Attacker: Or, parallel user-only path:
    Attacker-&amp;gt;&amp;gt;COM: GetCookieInfoForUri(target_url)
    COM--&amp;gt;&amp;gt;Attacker: Pre-baked x-ms-RefreshTokenCredential
    Note over Attacker: Cookie is now portable
    Attacker-&amp;gt;&amp;gt;Entra: Replay cookie from an attacker-controlled host
    Entra--&amp;gt;&amp;gt;Attacker: SSO honored, access token issued
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; With admin on an Entra-joined device in summer 2020, an attacker could lift the PRT and the derived signing key from &lt;code&gt;lsass&lt;/code&gt;, mint fresh &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; cookies on any host they controlled, and pass Conditional Access checks that included the cloned &lt;code&gt;DeviceId&lt;/code&gt; claim. Even without admin, the COM-API path returned signed cookies to a user-context process. The TPM was busy doing exactly what its contract said, and that contract was insufficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The community quickly settled on a name for this class: &lt;strong&gt;Pass-the-PRT&lt;/strong&gt;. By analogy to Pass-the-Hash and Pass-the-Ticket, the attack is &quot;lift a long-lived authentication artifact from one host, present it as your own elsewhere.&quot; For a credential that the entire cloud sign-in stack was about to trust, the implications were severe.&lt;/p&gt;
&lt;p&gt;By September 2020 Microsoft had bolted a nonce onto the cookie. By July 2021 they had something architecturally different: a single SHA-256 over the cookie&apos;s full payload that killed off-device Pass-the-PRT for good.&lt;/p&gt;
&lt;h2&gt;6. KDFv2 and the death of off-device Pass-the-PRT&lt;/h2&gt;
&lt;p&gt;The fix Microsoft shipped on 13 July 2021 fits on one line.&lt;/p&gt;
&lt;p&gt;The CVE is &lt;strong&gt;CVE-2021-33779&lt;/strong&gt;. NIST&apos;s National Vulnerability Database describes it as &quot;Windows AD FS Security Feature Bypass Vulnerability&quot; and provides no further public detail [@nvd-cve-2021-33779]. Microsoft&apos;s own KDFv2 documentation ties the patch explicitly to that CVE: &quot;On July 13, 2021, updates were released for AD FS to address token replay attacks, as described in CVE-2021-33779. These updates introduce new settings to enable and control a new, Key Derivation Function (KDF) called KDFv2&quot; [@kdfv2-learn].&lt;/p&gt;

The version-2 key-derivation rule introduced for the `x-ms-RefreshTokenCredential` cookie on 13 July 2021. Under KDFv2, the SP800-108 KDF context is `SHA256(ctx || assertion_payload)` rather than the bare client-chosen `ctx` value. The JWT header field `kdf_ver` carries the value `2` to indicate that KDFv2 was used. KDFv1 is preserved for backward compatibility but is disabled by default on a service that has been moved to &quot;Enforced&quot; mode [@ms-oapxbc-jwt] [@kdfv2-learn].
&lt;p&gt;A small subtlety lives in the attribution. NVD names AD FS. The community-side coverage -- blog.3or.de, Mollema&apos;s TROOPERS 22 deck, AADInternals -- names PRT-cookie forgery. The Microsoft KDFv2 page sits in the middle: it ties the patch to CVE-2021-33779 and walks through the same derivation change that closed off-device Pass-the-PRT, but it does not use the term &quot;Pass-the-PRT&quot; on the page itself. We will keep the hedge in mind.&lt;/p&gt;

NVD&apos;s one-line description -- &quot;Windows AD FS Security Feature Bypass Vulnerability&quot; -- is authoritative for the federal CVE record [@nvd-cve-2021-33779]. The community attribution to the Pass-the-PRT class comes from independent reverse-engineering: blog.3or.de&apos;s analysis is the most precise public reading. Both can be true; KDFv2 is the rollout vehicle, and it ships into both AD FS (the on-prem federation server) and the Microsoft Entra ID PRT path. The article reads CVE-2021-33779 as &quot;the rollout vehicle for KDFv2,&quot; not as a one-to-one CVE-to-attack mapping.
&lt;p&gt;The load-bearing rule is one sentence. MS-OAPXBC §3.2.5 puts it like this: &quot;If the client chooses to use KDFv2, the client MUST use &lt;code&gt;SHA256(ctx || assertion payload)&lt;/code&gt; instead of &lt;code&gt;ctx&lt;/code&gt; as the context for deriving the signing key. The client MUST also add the JWT header field &lt;code&gt;kdf_ver&lt;/code&gt; with value set to 2 to communicate that KDFv2 was used for creating the derived signing key&quot; [@ms-oapxbc-jwt].&lt;/p&gt;
&lt;p&gt;To see why that line matters, picture what the attacker in §5 was actually copying. The attacker lifted the derived signing key out of &lt;code&gt;lsass&lt;/code&gt;. The derived signing key was, under KDFv1, a function of the session key (TPM-resident) and the client-chosen context &lt;code&gt;ctx&lt;/code&gt; (any 256 bits the attacker liked). Any cookie the attacker built using the same &lt;code&gt;ctx&lt;/code&gt; would verify against the same derived key. The attacker could pick &lt;code&gt;ctx&lt;/code&gt; first, derive the key once, and stamp out as many cookies as they wanted.&lt;/p&gt;
&lt;p&gt;Under KDFv2, the context is no longer arbitrary. The context is &lt;code&gt;SHA256(ctx || assertion_payload)&lt;/code&gt;. The &lt;code&gt;assertion_payload&lt;/code&gt; is the JWT body the cookie is trying to assert. Change a single claim in the body, and the SHA-256 hash changes, and the SP800-108 derivation produces a different key. A key derived for one cookie cannot sign any other cookie. There is nothing to precompute.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The architectural insight is the same one Kerberos learned with PA-FX-FAST and TLS learned with channel binding: a session-key derivation must be bound to the message being signed, not just to a per-session label. Before KDFv2, the derivation contract was &quot;derive a key for this session, then sign anything.&quot; After KDFv2, the contract is &quot;derive a key for this specific message.&quot; An attacker who exfiltrates the session key off-device cannot precompute a useful signing key; an attacker who exfiltrates a derived signing key for one cookie cannot reuse it for the next. Off-device Pass-the-PRT is dead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The residual is also explicit. The attacker who is still &lt;em&gt;on the device&lt;/em&gt; -- still has a process running as the user or as &lt;code&gt;SYSTEM&lt;/code&gt; -- can ask CloudAP to mint a fresh cookie. The TPM happily performs the new SHA-256-bound derivation, because that is its job; CloudAP returns the signed cookie to the calling process, because that is its job. The blog.3or.de reverse-engineering names this class precisely: &quot;This attack, referred to as Pass-the-PRT-Cookie, still works today but requires presence on the targeted device&quot; [@dimi-3or-de-kdfv2]. Mollema&apos;s TROOPERS 22 talk calls the same residual &quot;Cookie-on-Demand&quot; and walks the in-place cookie-minting flow on a fully patched Entra-joined endpoint [@troopers22-mollema-pdf] [@troopers22-abstract].&lt;/p&gt;
&lt;p&gt;The minimal cryptographic statement of the fix is small enough to write down. Let $H$ be HMAC-SHA256, $k_s$ be the session key, $\ell$ be the constant label &lt;code&gt;AzureAD-SecureConversation&lt;/code&gt;, $\mathit{ctx}$ be the per-cookie context, and $p$ be the JWT body to be signed. Under KDFv1, the derived signing key was $k_d = H(k_s, \ell \parallel \mathit{ctx})$. Under KDFv2, the derived signing key is $k_d = H(k_s, \ell \parallel \mathrm{SHA256}(\mathit{ctx} \parallel p))$. The difference is exactly the hash of the message body inside the derivation context.&lt;/p&gt;
&lt;p&gt;{`
// Illustrative; do NOT use as production crypto.
const crypto = require(&apos;crypto&apos;);&lt;/p&gt;
&lt;p&gt;function sha256(buf) { return crypto.createHash(&apos;sha256&apos;).update(buf).digest(); }
function hmac(key, data) { return crypto.createHmac(&apos;sha256&apos;, key).update(data).digest(); }&lt;/p&gt;
&lt;p&gt;function deriveKdfv2SigningKey(sessionKey, ctx, assertionPayload) {
  const label = Buffer.from(&apos;AzureAD-SecureConversation&apos;, &apos;utf8&apos;);
  const boundCtx = sha256(Buffer.concat([ctx, assertionPayload]));
  // SP800-108 KDF in counter mode is more involved; one HMAC stands in here.
  return hmac(sessionKey, Buffer.concat([label, boundCtx]));
}&lt;/p&gt;
&lt;p&gt;// The signing key is now uniquely tied to assertionPayload.
`}&lt;/p&gt;
&lt;p&gt;A side-by-side flowchart makes the structural shift legible.&lt;/p&gt;

flowchart LR
    subgraph KDFv1 [&quot;KDFv1 (pre-July 2021)&quot;]
        A1[Session key in TPM] --&amp;gt; A2[&quot;SP800-108 KDF&lt;br /&gt;label = AzureAD-SecureConversation&lt;br /&gt;context = ctx&quot;]
        A2 --&amp;gt; A3[Derived signing key]
        A3 --&amp;gt; A4[HMAC over any JWT body]
    end
    subgraph KDFv2 [&quot;KDFv2 (July 2021+)&quot;]
        B1[Session key in TPM] --&amp;gt; B2[&quot;SP800-108 KDF&lt;br /&gt;label = AzureAD-SecureConversation&lt;br /&gt;context = SHA256 of ctx || payload&quot;]
        B2 --&amp;gt; B3[Derived signing key]
        B3 --&amp;gt; B4[HMAC over the specific JWT body]
    end
&lt;p&gt;KDFv2 killed off-device replay. It did not kill the on-device signing oracle, and it did not shorten the PRT&apos;s 90-day lifetime. The next generation tackled both -- not by closing the on-device gap, which is architecturally hard, but by making issued access tokens revocable in seconds.&lt;/p&gt;
&lt;h2&gt;7. The seam: CAE, Token Protection, Cloud Kerberos Trust&lt;/h2&gt;
&lt;p&gt;By 2022 the PRT was &lt;em&gt;the&lt;/em&gt; credential. The work that remained was to make every artifact issued &lt;em&gt;from&lt;/em&gt; it -- every access token, every Kerberos TGT, every Conditional Access claim -- share the same device-binding contract.&lt;/p&gt;
&lt;p&gt;That work has three named pieces, and a quiet rename in the middle.&lt;/p&gt;
&lt;h3&gt;Continuous Access Evaluation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Continuous Access Evaluation&lt;/strong&gt; entered public preview in late 2020, a few months after Mollema&apos;s August blog. By 10 January 2022, Microsoft announced General Availability across Microsoft Entra ID; the announcement post came from Alex Simons, Corporate Vice President for Program Management in the Microsoft Identity Division [@twu-cae-ga-mirror]. CAE is the mechanism by which a &lt;em&gt;long-lived&lt;/em&gt; access token issued from a PRT can be invalidated in seconds when something critical changes.&lt;/p&gt;

An industry-standard near-real-time revocation channel for OAuth access tokens, implemented by Microsoft Entra ID as a claim-challenge protocol between Entra and CAE-aware resource providers. CAE is anchored in the OpenID Continuous Access Evaluation Profile (CAEP) [@caep-openid-spec]. CAE-aware resources reject a previously valid access token when Entra signals one of five critical events: user account deletion or disablement, password change, MFA enablement, admin token revocation, or high user-risk classification. Microsoft Learn documents an event-propagation upper bound of 15 minutes, with IP-location enforcement instantaneous [@cae-learn].
&lt;p&gt;Mechanically: a CAE-aware client requests an access token from Entra ID, and Entra issues a &lt;em&gt;long-lived&lt;/em&gt; token -- up to 28 hours rather than the conventional one hour -- with a &lt;code&gt;xms_cc&lt;/code&gt; claim signaling that the bearer understands the protocol. The resource provider serves requests against that token. When something changes -- the user gets disabled in HR, the IT admin resets the password, a sign-in trips a high-risk classification -- Entra ID fires a CAEP event. The resource provider receives the event and, on the next request, returns an HTTP 401 with a &lt;code&gt;WWW-Authenticate&lt;/code&gt; claim challenge. The client returns to Entra, presents the PRT, and asks for a fresh access token; Entra evaluates Conditional Access at that moment and either issues a new token or refuses. The user sees, at worst, a fast re-authentication; the access window for the revoked credential is on the order of seconds rather than the access token&apos;s original lifetime.&lt;/p&gt;

sequenceDiagram
    participant Admin
    participant Entra as Microsoft Entra ID
    participant Resource as Exchange Online
    participant Client
    Admin-&amp;gt;&amp;gt;Entra: Force password reset for user
    Entra--&amp;gt;&amp;gt;Resource: CAEP event: Credential Change
    Client-&amp;gt;&amp;gt;Resource: GET /mail (with long-lived token)
    Resource--&amp;gt;&amp;gt;Client: 401 WWW-Authenticate (claim challenge)
    Client-&amp;gt;&amp;gt;Entra: Refresh token + claim challenge
    Note over Entra: Re-evaluate Conditional Access against current user state
    Entra--&amp;gt;&amp;gt;Client: New short access token (or deny)
    Client-&amp;gt;&amp;gt;Resource: GET /mail (new token)
    Resource--&amp;gt;&amp;gt;Client: 200 OK
&lt;p&gt;The initial CAE deployment was constrained: only Exchange Online, SharePoint Online, and Teams understood the claim-challenge protocol at GA [@cae-learn]. Microsoft Graph followed. Other workloads still honor an access token until natural expiry, which is the open scope of the §9 caveat list.&lt;/p&gt;
&lt;h3&gt;Token Protection&lt;/h3&gt;
&lt;p&gt;If CAE is the &lt;em&gt;time&lt;/em&gt; dimension, &lt;strong&gt;Token Protection&lt;/strong&gt; is the &lt;em&gt;space&lt;/em&gt; dimension. The Conditional Access feature, also referred to as &quot;token binding,&quot; demands that an app-token request originate from a device-bound session token -- in practice, a PRT-signed assertion. The Microsoft Learn page defines it as a &quot;Conditional Access session control that attempts to reduce token replay attacks by ensuring only device bound sign-in session tokens, like Primary Refresh Tokens (PRTs), are accepted by Microsoft Entra ID when applications request access to protected resources&quot; [@token-protection-learn].&lt;/p&gt;

A Microsoft Entra Conditional Access session control that enforces device-bound sign-in for app-token requests against supported resources. Token Protection is the per-app analogue of the PRT&apos;s device-binding contract: every access token must originate from a device-bound session token. As of 2026, Token Protection is generally available on Windows for Exchange Online, SharePoint Online, Teams, Azure Virtual Desktop, and Windows 365; it is in preview on iOS/iPadOS and macOS via the Microsoft Enterprise SSO plug-in [@token-protection-learn] [@apple-sso-plugin-learn].
&lt;p&gt;The current scope is intentionally narrow. Native applications and the Microsoft Enterprise SSO plug-in for Apple devices both implement the device-bound assertion. Browsers do not. A browser visiting a Microsoft cloud resource still rides the &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; cookie path. Closing that gap is what Device Bound Session Credentials -- the cross-vendor web standard Microsoft co-designed with Google -- exists to do, and we will return to that in §10.&lt;/p&gt;
&lt;h3&gt;Cloud Kerberos Trust&lt;/h3&gt;
&lt;p&gt;The third piece bridges the cloud-mediated PRT path back to on-prem Kerberos. The mechanism is simple in framing and intricate in implementation: Microsoft Entra ID provisions a virtual &lt;code&gt;AzureADKerberos&lt;/code&gt; read-only domain controller object inside the on-prem Active Directory domain, and an Entra-signed partial Kerberos TGT issued to a Hello-for-Business-signed-in device can be exchanged at any on-prem DC for a fully-formed TGT carrying SID and authorization data.&lt;/p&gt;

A Microsoft Entra ID mechanism by which Entra ID can mint Kerberos TGTs for one or more Active Directory domains. An Entra-signed partial TGT carries the user&apos;s identity; an on-prem domain controller, holding the cryptographic shared key represented by the virtual `AzureADKerberos` RODC computer object, completes the TGT with on-prem SID and group claims. The bridge requires Windows 10 21H2 (with KB5010415+) or later, and a Windows Server 2016+ functional level on the domain controller; it shipped in April-June 2022 [@cloud-kerberos-trust-learn] [@entra-passwordless-onprem].
&lt;p&gt;The Microsoft Learn deployment guide is explicit about the AzureADKerberos object&apos;s role: &quot;When Microsoft Entra Kerberos is enabled in an Active Directory domain, an AzureADKerberos computer object is created in the domain. This object: Appears as a read only domain controller (RODC) object, but isn&apos;t associated with any physical servers; Is only used by Microsoft Entra ID to generate TGTs for the Active Directory domain&quot; [@cloud-kerberos-trust-learn]. The architectural property to notice is that the user&apos;s NTLM hash is &lt;em&gt;not&lt;/em&gt; the binding key. Microsoft Entra ID never holds the on-prem NTLM hash; the cryptographic root is the AzureADKerberos RODC&apos;s keys, which Entra and the on-prem domain controller share without involving any user-side long-term secret.&lt;/p&gt;
&lt;p&gt;Cloud Kerberos Trust is the Kerberos PKINIT pattern from RFC 4556 [@rfc-4556-pkinit], reframed: the cloud identity provider is the public-key initial authenticator, and Entra ID issues the partial TGT exactly as a PKINIT-aware KDC would.&lt;/p&gt;
&lt;h3&gt;The Azure AD to Microsoft Entra ID rename&lt;/h3&gt;
&lt;p&gt;In the middle of all this, on 11 July 2023, the brand changed. Microsoft renamed Azure Active Directory to Microsoft Entra ID and consolidated several adjacent products under the Microsoft Entra umbrella [@entra-rename-2023]. The article uses &quot;Microsoft Entra ID&quot; throughout; in primary sources from before July 2023, the same product is &quot;Azure AD.&quot; The rename is real, and it matters when citing older documentation, but it does not change the protocol surface.&lt;/p&gt;
&lt;h3&gt;The seam restated&lt;/h3&gt;
&lt;p&gt;With Continuous Access Evaluation, Token Protection, and Cloud Kerberos Trust in place, the picture from §1 fills out. Every cloud-mediated identity feature on a modern Windows endpoint either issues, refreshes, presents, or evaluates a PRT. The PRT itself is the asymmetric handshake that binds the device. CAE makes the time dimension elastic. Token Protection makes the access surface device-bound at the resource-request layer. Cloud Kerberos Trust makes the on-prem Kerberos surface reachable from a PRT-bearing device.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The PRT is the cryptographic seam: a single device-bound credential, issued at first sign-in, that every other identity artifact on the device references. CAE, Token Protection, and Cloud Kerberos Trust are not three different bindings; they are three different ways the same PRT contract reaches three different surfaces -- the revocation surface, the per-resource access-token surface, and the on-prem Kerberos surface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A small comparison matrix makes the support story explicit.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource / scenario&lt;/th&gt;
&lt;th&gt;CAE-aware&lt;/th&gt;
&lt;th&gt;Token Protection (Windows GA)&lt;/th&gt;
&lt;th&gt;Cloud Kerberos Trust&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Exchange Online&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SharePoint Online&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Teams&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Graph&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not enforced&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Virtual Desktop&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows 365&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-prem file share&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser (any Microsoft cloud)&lt;/td&gt;
&lt;td&gt;Indirect via resource&lt;/td&gt;
&lt;td&gt;No (native apps only)&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;That is what the PRT does. But four sibling articles in this series describe identity surfaces the PRT does &lt;em&gt;not&lt;/em&gt; cover. Before we celebrate the seam, we have to be honest about where it stops.&lt;/p&gt;
&lt;h2&gt;8. Where PRT is not the answer&lt;/h2&gt;
&lt;p&gt;The PRT carries device state, MFA state, and Conditional Access claims for the &lt;em&gt;cloud-mediated&lt;/em&gt; identity path. There is no clause in that sentence that mentions on-prem Kerberos, NTLM hashes, local admin authorization, or workload identities -- and that is the point.&lt;/p&gt;
&lt;p&gt;Five surfaces the PRT does not cover, in the order operators most often confuse them:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On-prem Kerberos via the on-prem KDC.&lt;/strong&gt; A Windows user signing into a domain-joined or hybrid-joined machine still mints a Kerberos TGT against the on-prem Key Distribution Center on Windows logon. The PRT path is parallel, not replacement. The user&apos;s downstream &lt;code&gt;kerberos.dll&lt;/code&gt; ticket cache is populated by Kerberos AS_REQ/AS_REP exchanges between the workstation and the on-prem DC; the PRT lives in CloudAP&apos;s memory in &lt;code&gt;lsass&lt;/code&gt; and does not influence that flow. Cloud Kerberos Trust adds a bridge from PRT to on-prem TGT for users whose primary credential is in Entra; it does not retire the on-prem Kerberos path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Credential Guard and LSAISO.&lt;/strong&gt; &lt;a href=&quot;https://paragmali.com/blog/the-empty-hash-credential-guard-the-lsaiso-trustlet-and-the-/&quot; rel=&quot;noopener&quot;&gt;Credential Guard&lt;/a&gt;, introduced on the Enterprise SKU of the original Windows 10 release in 2015, isolates NTLM hashes and Kerberos long-term keys inside the Local Security Authority Isolated Subsystem (LSAISO), which runs in Virtual Trust Level 1 (VTL1) on top of the Hyper-V hypervisor [@credential-guard-learn] [@credential-guard-itpro-2016-wayback]. Credential Guard predates the cloud-identity model entirely; its threat model is &lt;em&gt;on-prem credential theft via long-term-key extraction from &lt;code&gt;lsass&lt;/code&gt;.&lt;/em&gt; The load-bearing distinction for the threat model is this: &lt;strong&gt;PRT material does NOT live in LSAISO&lt;/strong&gt;. It lives in normal &lt;code&gt;lsass.exe&lt;/code&gt; under CloudAP. Mollema&apos;s August 2020 extraction worked because the PRT&apos;s session-key handling is in the same address space as ordinary user processes that hold debug privilege; LSAISO did not move there. Treat &quot;I have Credential Guard enabled&quot; and &quot;my PRT is hardware-isolated&quot; as independent statements.The LSAISO isolation contract is for on-prem credentials -- NTLM hashes, Kerberos &lt;code&gt;krbtgt&lt;/code&gt; keys, the kinds of long-term secrets that the 2010s-era &quot;Pass-the-Hash&quot; tooling was designed to extract. The PRT&apos;s session key is a per-PRT artifact that lives in CloudAP&apos;s memory under normal LSASS. Credential Guard protects you against a different attack class. Get it for those reasons; do not get it expecting PRT-class mitigation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adminless and local-admin removal.&lt;/strong&gt; &quot;&lt;a href=&quot;https://paragmali.com/blog/adminless-how-windows-finally-made-elevation-a-security-boun/&quot; rel=&quot;noopener&quot;&gt;Adminless&lt;/a&gt;&quot; is an authorization pattern -- removing standing local-admin rights, requiring just-in-time elevation -- not an authentication pattern. It is orthogonal to the PRT. A device can be PRT-bound and still have a thousand local admins; a device can have zero local admins and still mint PRTs. The PRT addresses &quot;who is signing in;&quot; Adminless addresses &quot;what they can do once signed in.&quot; Conflating them is a common rhetorical move in Microsoft documentation and a common source of confusion in audits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;App Identity&lt;/a&gt;, managed identities, and workload identities.&lt;/strong&gt; Workloads in Microsoft cloud environments authenticate through a separate broker path: the Azure Instance Metadata Service (IMDS) on VMs, Workload Identity Federation for cross-cloud Kubernetes flows, managed identities on Functions and App Service. None of these always involve a PRT. A managed identity is a non-human principal in Entra ID with a system-issued credential, not a device-bound JWT, and the broker path that produces its access tokens is structurally different. The App Identity sibling article addresses that surface in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remote Credential Guard versus Azure AD RDP sign-in.&lt;/strong&gt; These two are often introduced together because both involve credentials over RDP, and conflating them is the load-bearing threat-model error in this section. &lt;strong&gt;Remote Credential Guard&lt;/strong&gt; redirects Kerberos credentials over the RDP hop: the client&apos;s TGT is reachable to the remote &lt;code&gt;mstsc&lt;/code&gt; session via a CredSSP-mediated redirection mechanism, so that the remote session can fetch downstream service tickets without re-prompting. It does &lt;em&gt;not&lt;/em&gt; transport PRT material across the connection. &lt;strong&gt;Azure AD RDP sign-in&lt;/strong&gt; -- the separate scenario where the RDP host itself is Entra-joined and accepts an Entra sign-in at session establishment -- is the PRT-mediated path, and it happens at the &lt;em&gt;host&lt;/em&gt; side, not as a redirection from the client.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If your threat model says &quot;I am redirecting credentials over RDP, therefore my PRT is exposed,&quot; you are reading the Remote Credential Guard documentation wrong. Remote Credential Guard ferries Kerberos tickets between the client &lt;code&gt;mstsc&lt;/code&gt; and the remote session host; the PRT lives in the client&apos;s LSASS and does not cross the RDP wire under that feature. Azure AD RDP sign-in is the separate, host-side scenario where the remote session establishes its own PRT against Entra. The Stage 0a audit flagged this conflation as one of the most common errors in the wild, and the Microsoft Learn pages are not co-located.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The pattern across all five is the same. PRT is the cloud-mediated authentication path. Kerberos is the on-prem authentication path. Credential Guard is the on-prem long-term-credential isolation path. Adminless is the local-authorization pattern. App Identity is the workload-authentication path. Remote Credential Guard is an on-prem credential redirection over RDP. They run alongside each other on a modern Windows endpoint; they answer different questions. Mistaking the PRT for any of them is how good threat models go sideways.&lt;/p&gt;
&lt;h2&gt;9. Theoretical limits&lt;/h2&gt;
&lt;p&gt;The single most important sentence in the W3C Device Bound Session Credentials draft is also the single most important sentence about the PRT -- and it does not mention the PRT at all.&lt;/p&gt;

DBSC will not prevent temporary access to the browser session while the attacker is resident on the user&apos;s device. The private key should be stored as safely as modern operating systems allow, preventing exfiltration of the session private key, but the signing capability will likely still be available for any program running as the user on the user&apos;s device. -- W3C Web Application Security Working Group, Device Bound Session Credentials draft
&lt;p&gt;That paragraph is the architectural lower bound. Every device-bound session credential ever proposed inherits it. The PRT is no exception. Five bounded promises follow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. The on-device-attacker floor is architectural.&lt;/strong&gt; A hardware-bound key whose &lt;em&gt;signing surface&lt;/em&gt; is reachable by a same-privilege process can be used by that process for the lifetime of its presence. The TPM holds the key; the operating system mediates the signing operation; any process the operating system trusts to talk to CloudAP can ask for a signature. KDFv2 closed &lt;em&gt;off-device&lt;/em&gt; replay because the signing key is now uniquely bound to one cookie -- but the on-device process can simply ask for the next signature. The DBSC working draft is explicit that this is the floor for the entire class [@dbsc-w3c-draft]. The composition argument we will name in §10 is the practical response.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Non-TPM Windows reopens the pre-2021 attack class.&lt;/strong&gt; When the device key and transport key are protected by DPAPI rather than by a TPM 2.0, the key material can be unwrapped with the user&apos;s profile credentials. Pre-2021 Pass-the-PRT becomes available again because the attacker is no longer trying to extract a derived signing key from &lt;code&gt;lsass&lt;/code&gt; -- they are extracting the &lt;em&gt;root&lt;/em&gt; of the derivation from disk. Microsoft Learn names &quot;TPM 2.0 on Windows 10 1903 or higher&quot; as the supported configuration; everything else is best-effort [@prt-msft-learn]. TPM 2.0 is load-bearing, not optional, for the security claims this article makes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Phishing-resistance inheritance is one-shot.&lt;/strong&gt; The PRT records the authentication strength of the &lt;em&gt;issuing&lt;/em&gt; credential -- whether the user signed in with Hello for Business, a FIDO2 key, a password, or a password plus an MFA factor. The &lt;code&gt;mfa&lt;/code&gt; claim on the PRT carries this through to downstream tokens. If the user authenticated with a phishable factor at issuance, every downstream access token transitively trusts that weaker factor for the PRT lifetime. The PRT does &lt;em&gt;not&lt;/em&gt; upgrade. To enforce phishing-resistant authentication, the deployer must configure Conditional Access Authentication Strengths at the Entra ID side -- the PRT will record what arrived, but it will not refuse to mint downstream tokens because the issuing factor was weak.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. CAE coverage is not universal.&lt;/strong&gt; Continuous Access Evaluation is the time dimension of revocation -- but only for CAE-aware resources. Exchange Online, SharePoint Online, Teams, and Microsoft Graph honor the claim-challenge protocol; many other workloads still treat the access token as valid until its native expiry [@cae-learn]. If your tenant&apos;s risk surface is a CAE-unaware first- or third-party application, the deployment-time guarantee is the access token&apos;s natural lifetime, not 15 minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. The PRT lifetime is 90 days by design.&lt;/strong&gt; A device offline for more than the PRT lifetime cannot silently refresh; the user will see a re-authentication prompt the next time the device reaches Entra ID. That window is the Conditional Access trade-off: longer windows reduce friction for travelers and offline scenarios; shorter windows reduce the attacker&apos;s window after a device compromise. Microsoft chose 90 days; the deployer can tune it via Conditional Access Sign-In Frequency policies but cannot move it independently of the broader refresh-token configuration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To approximate the ideal -- a device-bound, near-real-time-revocable, phishing-resistant cross-app SSO credential -- a deployer composes: &lt;strong&gt;PRT&lt;/strong&gt; (device binding) + &lt;strong&gt;CAE&lt;/strong&gt; (near-real-time revocation) + &lt;strong&gt;Token Protection&lt;/strong&gt; (per-resource device binding for native apps) + &lt;strong&gt;Authentication Strengths&lt;/strong&gt; (Conditional Access policy that upgrades phishing resistance at issuance) + &lt;strong&gt;DBSC&lt;/strong&gt; (per-origin web defense once it is available). No single artifact closes all five gaps; composition is the deployer&apos;s job, and the gaps in any one artifact are the joints another is supposed to cover.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Four of the five limits are bounded -- TPM rollout, claim-strength policy, CAE rollout, offline cadence. They get smaller as Microsoft ships, as administrators tighten policy, as more resources become CAE-aware. One is architectural and applies to every device-bound session credential ever proposed: same-device admin equals access while the admin has it. That is the open problem the next section traces.&lt;/p&gt;
&lt;h2&gt;10. Open problems&lt;/h2&gt;
&lt;p&gt;Five open problems sit on the PRT model right now. None of them have a &quot;just ship a patch&quot; answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cookie-on-Demand on the live device.&lt;/strong&gt; The architectural defense is bounded by the §9 floor. Mollema&apos;s TROOPERS 22 talk makes the case that &lt;strong&gt;trustlet-level isolation&lt;/strong&gt; of the PRT signing path -- moving the CloudAP cookie-construction code from normal &lt;code&gt;lsass&lt;/code&gt; into an isolated user-mode environment in VTL1, the same security boundary that protects LSAISO -- would close the residual class [@troopers22-mollema-pdf]. Microsoft has not shipped that move. The cost is non-trivial: every downstream broker (WAM, the browser SSO surface, every native app that talks to CloudAP) would need to route through a trustlet-mediated signing API, and the trustlet itself would need to make policy decisions about which callers are entitled to a cookie. The benefit is real -- it removes the same-user-attacker class for the most powerful credential on the device -- but the engineering cost has not been deemed worth it as of 2026.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-vendor near-real-time revocation.&lt;/strong&gt; CAE works inside the Microsoft Entra perimeter. If a user is compromised at Entra and Microsoft revokes the session, the signal does not automatically propagate to Okta-protected resources, Google Workspace, AWS IAM Identity Center, or any other identity provider the same user happens to have a session against. The standardization vehicle exists: the OpenID Shared Signals Framework defines a cross-IdP event-receiver protocol, and the OpenID CAEP specification provides the event taxonomy [@caep-openid-spec]. The bilateral transmit/receive deployments are sparse. Stage 3 of the research pipeline found no public production cross-vendor CAE deployment that wires Entra revocation events into a non-Microsoft IdP. The standard is ready; the deployments are not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DBSC and PRT composition for browser SSO.&lt;/strong&gt; Google&apos;s Device Bound Session Credentials began general availability for Chrome 146 on Windows in late 2025, with Microsoft co-designing the standard through the W3C process [@dbsc-google-blog] [@dbsc-w3c-draft]. The Chrome developer documentation references Chrome 145 as the rollout-start build, and the Google security blog references Chrome 146 as the GA build; the version drift reflects a phased rollout, and the article uses the later figure [@dbsc-chrome-developer]. The composition question is unresolved: when a browser on Windows visits &lt;code&gt;login.microsoftonline.com&lt;/code&gt;, the request will carry both a DBSC-bound short cookie (per-origin) and an &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; cookie from the WAM attachment path. Which binding wins, and how the two bindings are composed in the resource provider&apos;s evaluation, has not been publicly documented. The Stage 3 research found no Microsoft engineering blog explaining the contract.The Chrome developer documentation page on DBSC cites &quot;Chrome 145&quot; while the Google Security Blog post about DBSC GA cites &quot;Chrome 146.&quot; The two pages are co-published by Google; the security blog is dated later in 2025 and represents the GA figure. Stage 4 flagged this as an internal-inconsistency artifact. The article uses Chrome 146 for the GA framing and notes Chrome 145 as the rollout-start build [@dbsc-chrome-developer] [@dbsc-google-blog].&lt;/p&gt;

A modern Windows Edge session against `login.microsoftonline.com` already carries `x-ms-RefreshTokenCredential`. A modern Chrome 146 session on Windows carries a DBSC-bound short cookie for the same origin. Token Protection enforces device binding for *native-app* access-token requests, not browser ones. The three bindings are not redundant -- they cover different surfaces -- but Microsoft has not published a precedence rule or a unified &quot;this is how the browser proves device binding to Entra&quot; reference, and the open question is whether the W3C DBSC draft will be the home for that contract or whether Microsoft will document the composition independently. The composition story for browser SSO is, in 2026, the single most active open problem in this space.
&lt;p&gt;&lt;strong&gt;PRT-aware Conditional Access for AI agents and workload identities.&lt;/strong&gt; As organizations deploy autonomous AI agents that act on behalf of users -- Copilot agents, Office Studio bots, third-party LangGraph-style systems -- the identity story is genuinely unsettled. Some agents authenticate as the user via delegated permissions on a PRT-mediated path. Others authenticate as their own service principal via Workload Identity Federation. Conditional Access policies designed for human users -- &quot;require compliant device, require MFA, require sign-in frequency under four hours&quot; -- do not map cleanly to either. Microsoft Entra Agent ID entered public preview at Ignite 2025 with Conditional Access extended to agent identities via custom security attributes and agent-identity-blueprint policy targeting [@entra-agent-id-conditional-access], but the precise PRT-side claim semantics for agent-on-behalf-of-user vs autonomous-agent paths are still settling. The Conditional Access for AI Agents sibling article addresses the evolving model in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PRT across RDP.&lt;/strong&gt; There is no clean &quot;redirect PRT&quot; primitive analogous to Remote Credential Guard&apos;s Kerberos redirection. Inside an RDP session to an Entra-joined host, a user can perform an Azure AD RDP sign-in that mints a &lt;em&gt;new&lt;/em&gt; PRT at the host -- but the client&apos;s PRT does not transit the RDP hop. Forensic and operational tooling that wants to know &quot;what PRT does this remote user have, and is it the same as the client&apos;s?&quot; has to query both endpoints separately. Active Microsoft work in this area is referenced in Mollema&apos;s TROOPERS 22 deck, but no public solution has shipped.&lt;/p&gt;
&lt;p&gt;These five problems share an architecture: they are all about composition. The PRT is one of several primitives that have to work together. The next section walks the practical guide for making them work in your environment today.&lt;/p&gt;
&lt;h2&gt;11. Practical guide&lt;/h2&gt;
&lt;p&gt;Here is what you actually do with the PRT this week.&lt;/p&gt;
&lt;h3&gt;Verifying PRT issuance&lt;/h3&gt;
&lt;p&gt;The operator-facing surface is &lt;code&gt;dsregcmd /status&lt;/code&gt;, which prints the PRT state under the &lt;code&gt;SSO State&lt;/code&gt; section. The three fields to read are &lt;code&gt;AzureAdPrt&lt;/code&gt; (Yes if a PRT is present), &lt;code&gt;AzureAdPrtUpdateTime&lt;/code&gt; (the timestamp of the last refresh), and &lt;code&gt;AzureAdPrtExpiryTime&lt;/code&gt; (the absolute expiry on the current PRT, by default 90 days after issuance) [@prt-msft-learn] [@dsregcmd-troubleshoot].&lt;/p&gt;
&lt;p&gt;{&lt;code&gt; // Models the section of dsregcmd /status output you care about. // On a real Windows host, you would run: dsregcmd /status | findstr AzureAdPrt const sampleOutput = \&lt;/code&gt;
+----------------------------------------------------------------------+
| SSO State                                                            |
+----------------------------------------------------------------------+
             AzureAdPrt : YES
       AzureAdPrtUpdateTime : 2026-05-12 09:31:14.000 UTC
       AzureAdPrtExpiryTime : 2026-08-10 09:31:14.000 UTC
        AzureAdPrtAuthority : login.microsoftonline.com/
              EnterprisePrt : NO
`;
const lines = sampleOutput.split(&apos;\n&apos;).filter(l =&amp;gt; l.match(/AzureAdPrt/));
console.log(lines.map(l =&amp;gt; l.trim()).join(&apos;\n&apos;));
// Healthy: AzureAdPrt=YES and AzureAdPrtUpdateTime within the last 4 hours.
`}&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;AzureAdPrt&lt;/code&gt; is &lt;code&gt;NO&lt;/code&gt; on a device that should have one, the most common causes are (a) the device is not actually Entra-joined, (b) the user has never signed in interactively since the last reboot, or (c) the device&apos;s TPM is malfunctioning and CloudAP could not complete the issuance handshake. &lt;code&gt;dsregcmd /status&lt;/code&gt; will print device-state diagnostics directly above the SSO State section that disambiguate these.&lt;/p&gt;
&lt;h3&gt;Forcing PRT renewal&lt;/h3&gt;
&lt;p&gt;The PRT refreshes silently every four hours, driven by CloudAP -- this is the renewal cadence Microsoft Learn documents as the device-side refresh schedule, not a Conditional Access policy [@prt-msft-learn]. To force an out-of-band renewal, the supported path is to sign the user out and back in with a Hello-for-Business gesture or a strong credential. A locked-and-unlocked session does &lt;em&gt;not&lt;/em&gt; generally force a new PRT mint; CloudAP treats unlock as a continuation event, not a fresh issuance.&lt;/p&gt;
&lt;h3&gt;Hunting PRT-mediated sign-ins in Entra logs&lt;/h3&gt;
&lt;p&gt;In the Microsoft Entra audit and sign-in logs, the load-bearing fields are &lt;code&gt;authenticationDetails&lt;/code&gt;, &lt;code&gt;authenticationProcessingDetails&lt;/code&gt;, and the &lt;code&gt;IsCompliantDevice&lt;/code&gt; and &lt;code&gt;DeviceDetail&lt;/code&gt; claims attached to the sign-in event. A sign-in that rode the PRT path will surface a &lt;code&gt;PRT&lt;/code&gt; indicator in &lt;code&gt;authenticationProcessingDetails&lt;/code&gt;. In Microsoft Defender XDR&apos;s advanced-hunting tables, the corresponding views are &lt;code&gt;IdentityLogonEvents&lt;/code&gt; (for on-prem and federated paths) and &lt;code&gt;AADSignInEventsBeta&lt;/code&gt; (for native Entra sign-in events) [@defender-xdr-schema]. The latter is the table to query when looking for unusual &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt;-driven sign-ins -- specifically, sign-ins from device-claim-bearing tokens whose &lt;code&gt;DeviceId&lt;/code&gt; does not match the device&apos;s &lt;code&gt;DeviceId&lt;/code&gt; in Intune.&lt;/p&gt;
&lt;h3&gt;Conditional Access patterns&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What it enforces&lt;/th&gt;
&lt;th&gt;What it cannot enforce&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Require compliant device&lt;/td&gt;
&lt;td&gt;Sign-in only from devices Intune (or a partner MDM) reports as compliant&lt;/td&gt;
&lt;td&gt;Whether the compliance signal is fresh; an attacker who can spoof an Intune compliance attestation passes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Require Microsoft Entra hybrid joined device&lt;/td&gt;
&lt;td&gt;Sign-in only from hybrid-joined devices&lt;/td&gt;
&lt;td&gt;Personal Entra-registered devices that meet compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Require MFA at sign-in&lt;/td&gt;
&lt;td&gt;A fresh MFA factor at PRT issuance&lt;/td&gt;
&lt;td&gt;Whether the MFA factor is phishing-resistant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication Strengths (FIDO2-only)&lt;/td&gt;
&lt;td&gt;Phishing-resistant credential at issuance, propagated as a strong &lt;code&gt;mfa&lt;/code&gt; claim into the PRT&lt;/td&gt;
&lt;td&gt;Downstream phishability through cookie theft (KDFv2 fix applies; on-device residual remains)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Protection for sign-in tokens&lt;/td&gt;
&lt;td&gt;Device-bound assertion required for app-token requests&lt;/td&gt;
&lt;td&gt;Browser sessions (DBSC is the per-origin counterpart)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign-in Frequency = 4 hours&lt;/td&gt;
&lt;td&gt;Re-authentication every four hours&lt;/td&gt;
&lt;td&gt;The 90-day PRT lifetime independent of sign-in cadence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The right policy stack for most enterprises is: require compliant device (or hybrid-joined), require Authentication Strengths for privileged users, require Token Protection where the resource supports it, and set a Sign-In Frequency policy that matches your risk appetite. CAE is on by default on modern tenants and does not need explicit opt-in.&lt;/p&gt;
&lt;h3&gt;CAE enablement and tenant configuration&lt;/h3&gt;
&lt;p&gt;CAE was made the default for all Entra tenants at GA on 10 January 2022; the announcement explicitly noted that Microsoft &quot;auto-enabled it for all tenants&quot; [@twu-cae-ga-mirror]. Microsoft Outlook, Microsoft Teams, and Office on Windows are CAE-aware clients [@cae-learn]; third-party apps that want to participate need to implement the claim-challenge protocol. Microsoft Graph clients gain CAE participation by including &lt;code&gt;cp1&lt;/code&gt; in the requested client capabilities [@cae-client-capabilities]. If your tenant is a CAE outlier, the cause is almost always a custom OIDC application that has not implemented the claim challenge.&lt;/p&gt;
&lt;h3&gt;Forensic indicators&lt;/h3&gt;
&lt;p&gt;Three signals deserve hunting attention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Anomalous &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; cookie origins.&lt;/strong&gt; A sign-in where the cookie&apos;s IP geolocation does not match the device&apos;s last known location -- particularly across time zones -- is a candidate Pass-the-PRT-Cookie signal even after KDFv2, because the on-device class survives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Device-claim-bearing tokens whose &lt;code&gt;DeviceId&lt;/code&gt; does not match Intune state.&lt;/strong&gt; An attacker who lifted a PRT off-device cannot mint cookies post-KDFv2, but a cloned &lt;code&gt;DeviceId&lt;/code&gt; claim in a token request is a strong off-the-rails signal in older logs and a useful retrospective hunt for July 2021 and earlier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;lsass&lt;/code&gt; broker-process anomalies.&lt;/strong&gt; Mimikatz-class memory-reading tools typically attach to &lt;code&gt;lsass&lt;/code&gt; with debug privileges. The current EDR generation (Microsoft Defender for Endpoint, CrowdStrike Falcon, SentinelOne) detects the canonical access patterns; deploy that telemetry, then validate the alert-rule coverage with &lt;code&gt;Get-MpComputerStatus&lt;/code&gt; and the EDR-specific equivalents.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What NOT to do&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The single biggest operational mistake is to disable the broker because something else is broken. WAM, CloudAP, and the browser SSO surface are not optional add-ons; they are the cryptographic floor your Conditional Access policies are built on. If a particular app is breaking on PRT-mediated sign-in, the right move is to diagnose the broker integration, not to suppress it. Likewise, do not suppress Conditional Access in lieu of trusting the PRT -- the PRT carries claims that Conditional Access evaluates; disabling Conditional Access keeps the claims but throws away the policy engine.&lt;/p&gt;
&lt;/blockquote&gt;

Open an elevated command prompt. Run `dsregcmd /status`. Confirm `AzureAdJoined : YES`, `DeviceId` is populated, and `AzureAdPrt : YES` with a recent `AzureAdPrtUpdateTime`. Then in PowerShell, run `Get-CimInstance -ClassName Win32_Tpm` and confirm the TPM is present, ready, and at spec version 2.0. Finally, in the Entra ID portal, search for the device by `DeviceId` and confirm the registration state, the OS version, and the compliance posture. Those three checks rule out 90% of &quot;is my PRT working?&quot; questions.
&lt;p&gt;That is the PRT -- what it is, how it broke, how Microsoft fixed it, where it stops. Now the FAQ.&lt;/p&gt;
&lt;h2&gt;12. FAQ and closing&lt;/h2&gt;

No. They are different protocols, issued by different authorities, with different lifetimes. A Kerberos TGT is issued by an on-prem Key Distribution Center, lives 10 hours by default, and rides the AS_REQ/AS_REP protocol. A PRT is issued by Microsoft Entra ID, lives 90 days by default, and rides the MS-OAPXBC protocol over HTTPS. Cloud Kerberos Trust *issues a TGT to a PRT holder* via the Microsoft Entra Kerberos partial-TGT mechanism [@cloud-kerberos-trust-learn], but the two artifacts are distinct and serve different protocol clients.

No. The PRT is the cloud-mediated authentication path. On-prem Kerberos still flows through the on-prem KDC for resources protected by the on-prem Active Directory domain. NTLM remains in use for legacy applications until those applications migrate. The PRT, Cloud Kerberos Trust, and the in-progress &quot;NTLM-less&quot; effort together describe a path that *reduces* reliance on NTLM, but they do not delete the on-prem authentication surface on day one.

Not since July 2021. The asymmetric device key (`dkpriv`) signs the PRT *issuance* request -- a single asymmetric signature per PRT mint. The `x-ms-RefreshTokenCredential` cookie, by contrast, is HMAC-signed with `alg: HS256` using a symmetric key derived from the PRT *session key* via the SP800-108 KDF. Under KDFv2, the derivation context binds the cookie&apos;s full payload via `SHA256(ctx || assertion_payload)` [@ms-oapxbc-jwt] [@dimi-3or-de-kdfv2].

No. Dirk-jan Mollema&apos;s seminal PRT-cookie extraction work appeared in two blog posts on `dirkjanm.io` -- 21 July 2020 and 5 August 2020 [@mollema-prt-2020-07] [@mollema-prt-2020-08]. His 2022 conference talk on the same body of research was at TROOPERS 22 in Heidelberg in June 2022, not at DEF CON 30 [@troopers22-abstract]. Mollema&apos;s DEF CON history covers DC 27 (2019), DC 32 (2024), and DC 33 (2025); he did not present at DC 30 (2022) [@dirkjanm-talks-index]. The &quot;DEF CON 2022&quot; anchor that occasionally appears in summaries of the PRT-attack story is a memory error.

Yes. Conditional Access evaluates each *token request*, including app-token requests via the Web Account Manager and `x-ms-RefreshTokenCredential` cookie redemptions at `login.microsoftonline.com`. The PRT carries device-state, MFA, and risk claims; Conditional Access uses those claims plus the resource and request context to allow or deny each request. CAE additionally revokes already-issued long-lived access tokens in near real time when critical events fire [@cae-learn].

No. Microsoft Pluton *is* a TPM 2.0 implementation -- the same TPM 2.0 contract, embedded in the SoC rather than as a discrete chip. The PRT two-key model is unchanged. `dkpriv` and `tkpriv` are TPM 2.0 keys on Pluton just as they are on a discrete TPM 2.0; CloudAP does not branch on TPM provenance in its issuance path.

All three device states issue PRTs at first interactive sign-in. The differences are about device-management posture and which Conditional Access claims attach. **Microsoft Entra registered** is the personal-device / BYOD state -- the device has a cloud identity but is not the primary management surface; the PRT exists but the device is not necessarily compliant in the management sense. **Microsoft Entra joined** is the cloud-primary state -- the device&apos;s primary identity authority is Entra ID. **Microsoft Entra hybrid joined** is the dual state -- the device has both an on-prem AD computer object and an Entra ID device object; both authentication paths are active in parallel. Microsoft documents hybrid join as &quot;an interim step on the road to Microsoft Entra join&quot; for organizations migrating away from on-prem AD [@entra-devices-overview].
&lt;p&gt;The PRT is not a replacement for Kerberos, NTLM, or Credential Guard. It is the cryptographic seam where Windows logon becomes a Microsoft Entra ID transaction -- and the rest of this series is about what runs alongside it: Hello for Business as the issuing credential, WebAuthn and FIDO2 as the per-relying-party authenticator class, Cloud Kerberos Trust as the on-prem bridge, Credential Guard as the on-prem-credential isolation path, Adminless as the local-authorization pattern, App Identity as the workload broker. Each of those articles starts from a question this one raises, and each closes on a question that connects back. The seam is the part you can name when somebody asks how the three sign-ins from §1 are secretly one event.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;entra-id-and-the-primary-refresh-token-how-azure-ad-sign-on-bridges-windows-logo&quot; keyTerms={[
  { term: &quot;Primary Refresh Token (PRT)&quot;, definition: &quot;Device-bound JWT issued by Microsoft Entra ID to CloudAP at first interactive sign-in; cryptographic seam between Windows logon and Entra-mediated SSO.&quot; },
  { term: &quot;CloudAP&quot;, definition: &quot;Cloud Authentication Provider plugin framework in lsass.exe; the Entra ID plugin owns the device-side PRT lifecycle.&quot; },
  { term: &quot;Device key (dkpub/dkpriv)&quot;, definition: &quot;TPM-bound key pair that signs PRT issuance requests; registered with Entra ID at join time.&quot; },
  { term: &quot;Transport key (tkpub/tkpriv)&quot;, definition: &quot;TPM-bound key pair Entra ID uses to wrap session keys; only tkpriv can unwrap them on-device.&quot; },
  { term: &quot;Session key&quot;, definition: &quot;Symmetric proof-of-possession key for the PRT lifetime; signs cookies and app-token requests via SP800-108 KDF derivation.&quot; },
  { term: &quot;x-ms-RefreshTokenCredential&quot;, definition: &quot;HMAC-signed JWT cookie that carries PRT-derived authentication to login.microsoftonline.com from supported browsers.&quot; },
  { term: &quot;KDFv2&quot;, definition: &quot;Post-CVE-2021-33779 derivation rule that mixes SHA256(ctx || payload) into the cookie&apos;s signing-key derivation, closing off-device replay.&quot; },
  { term: &quot;Continuous Access Evaluation (CAE)&quot;, definition: &quot;Near-real-time revocation channel for OAuth access tokens; 15-minute event-propagation upper bound; CAEP-anchored claim-challenge protocol.&quot; },
  { term: &quot;Token Protection&quot;, definition: &quot;Conditional Access session control that requires device-bound assertions for app-token requests; the per-app analogue of PRT device binding.&quot; },
  { term: &quot;Cloud Kerberos Trust&quot;, definition: &quot;Bridge that lets a PRT-bearing device receive on-prem Kerberos TGTs from Entra ID via the AzureADKerberos virtual RODC object.&quot; }
]} questions={[
  { q: &quot;Why is &apos;the PRT cookie is DKey-signed&apos; wrong?&quot;, a: &quot;The device key signs the asymmetric PRT issuance request once per PRT mint. Cookies are HMAC-signed with a symmetric key derived from the session key via SP800-108 KDF; under KDFv2 the derivation context is SHA256(ctx || assertion_payload).&quot; },
  { q: &quot;What did CVE-2021-33779 fix, in one sentence?&quot;, a: &quot;It introduced KDFv2, which binds the cookie&apos;s full payload into the SP800-108 derivation context, so a key derived for one cookie cannot sign another -- killing off-device Pass-the-PRT.&quot; },
  { q: &quot;What does the on-device-attacker floor mean for the PRT?&quot;, a: &quot;A same-privilege attacker on the live device can ask CloudAP to mint a fresh cookie; the TPM signs it, because that is its job. Off-device replay is closed; on-device Cookie-on-Demand is the architectural residual.&quot; },
  { q: &quot;Where does the PRT NOT apply?&quot;, a: &quot;On-prem Kerberos via the on-prem KDC, Credential Guard / LSAISO (NTLM/Kerberos long-term keys), Adminless (authorization), App Identity / workload identities, and Remote Credential Guard (which redirects Kerberos, not PRT).&quot; },
  { q: &quot;How does CAE revoke an in-flight access token?&quot;, a: &quot;Entra fires a CAEP event on a critical change (user deletion, password reset, MFA enable, admin revocation, high user risk). The CAE-aware resource provider issues an HTTP 401 with a claim challenge on the next request; the client re-presents the PRT and Entra evaluates Conditional Access fresh, issuing a new token or denying.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>entra-id</category><category>azure-ad</category><category>windows-authentication</category><category>primary-refresh-token</category><category>tpm</category><category>conditional-access</category><category>identity</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>