<?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: entra-id</title><description>Posts tagged entra-id.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sun, 07 Jun 2026 04:13:14 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/entra-id/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>Pass-the-Hash to Pass-the-PRT: Twenty-Nine Years of Windows Credential Replay in One Family Tree</title><link>https://paragmali.com/blog/pass-the-hash-to-pass-the-prt-twenty-nine-years-of-windows-c/</link><guid isPermaLink="true">https://paragmali.com/blog/pass-the-hash-to-pass-the-prt-twenty-nine-years-of-windows-c/</guid><description>Pass-the-Hash, Pass-the-Ticket, Overpass-the-Hash, Pass-the-Certificate, and Pass-the-PRT are one architectural lineage. Each defense bought years; none closed the family.</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>
Twenty-nine years of Windows credential-replay attacks -- Pass-the-Hash, Pass-the-Ticket, Overpass-the-Hash, Pass-the-Certificate, Pass-the-PRT -- are a single lineage, not five techniques. Each generation finds the next long-term authentication artefact that lives outside the latest Microsoft isolation boundary, then commoditises extraction in tooling that runs anywhere with local administrator. Credential Guard (2015) and KB5014754 (2022) bought years but not closure; Pass-the-PRT (Mollema + Delpy, 2020) already defeats both because the Primary Refresh Token lives in the CloudAP plug-in, which is not inside any current isolation scope. The next decade of Windows credential theft turns on whether Microsoft extends hypervisor-based isolation to CloudAP before commodity offensive tooling makes the attack universal.
&lt;h2&gt;1. Two Afternoons, Twenty-Nine Years Apart&lt;/h2&gt;
&lt;p&gt;On the afternoon of Tuesday, April 8, 1997, between 5:27 p.m. and 8:57 p.m. -- a window we can narrow to about three and a half hours from the file timestamps preserved in the patch he posted -- a researcher named Paul Ashton sat down with the Samba source tree and made the smallest possible change to &lt;code&gt;smbclient&lt;/code&gt;.The bracketing mtimes &lt;code&gt;Tue Apr  8 17:27:29 1997&lt;/code&gt; and &lt;code&gt;Tue Apr  8 20:57:43 1997&lt;/code&gt; are preserved verbatim in the unified diff&apos;s &lt;code&gt;***&lt;/code&gt; and &lt;code&gt;---&lt;/code&gt; header lines on Exploit-DB advisory 19197 [@ashton-exploitdb-19197]. You can still download the diff today and confirm the timestamps yourself. Where the unpatched client computed a network response from a typed-in password, his version read the password&apos;s LM hash from &lt;code&gt;smbpasswd&lt;/code&gt; on disk and fed it straight to the same encryption primitive, skipping the password entirely.&lt;/p&gt;
&lt;p&gt;He posted the diff to NTBugtraq the same evening with a five-line advisory: &quot;A modified SMB client can mount shares on an SMB host by passing the username and corresponding LanMan hash of an account that is authorized to access the host and share. The modified SMB client removes the need for the user to &apos;decrypt&apos; the password hash into its clear-text equivalent.&quot; [@ashton-exploitdb-19197]&lt;/p&gt;
&lt;p&gt;Twenty-nine years later, every Windows credential-replay attack in commodity offensive tooling is a direct descendant of that afternoon.&lt;/p&gt;
&lt;p&gt;Fast-forward to 2026. A Windows 11 23H2 laptop, hardened to Microsoft&apos;s published baseline. &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; on. KB5014754 strong certificate mapping in full enforcement. Conditional Access enabled, with Token Protection where supported. An attacker has local admin -- the same starting position the 1997 attack assumed.&lt;/p&gt;
&lt;p&gt;Two commands run on that machine, in the same paragraph. Mimikatz &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; returns empty NT hash and TGT buffers; Credential Guard has done its job. Then Mimikatz &lt;code&gt;dpapi::cloudapkd /unprotect&lt;/code&gt; returns a valid Primary Refresh Token session key and proof-of-possession material [@mollema-prt-digging]. On a &lt;em&gt;different&lt;/em&gt; machine across the internet, the attacker pastes that material into Dirk-jan Mollema&apos;s &lt;code&gt;roadtx prt&lt;/code&gt;, mints an &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; cookie, and authenticates to Entra ID as the laptop&apos;s user [@mollema-prt-abusing] [@roadtools-github]. Every Microsoft defense shipped in 2015, 2022, and 2024 is running. The attack still wins.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The empty buffer from &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; is the artefact of twenty-nine years of architectural lessons. The PRT extraction from &lt;code&gt;dpapi::cloudapkd&lt;/code&gt; is the architecture of the &lt;em&gt;next&lt;/em&gt; five-to-ten years. Both scenes are the same attack class. The credential changed; the protocol that consumes it changed; the long-term storage location changed; the lineage did not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You will meet seven people in this article. Paul Ashton (1997, the patch). Hernan Ochoa (2008, the toolkit that put the technique inside Windows itself). Benjamin Delpy (2011, Mimikatz; and the Kerberos generations that followed). Sean Metcalf (2014, who named Overpass-the-Hash and wrote the practitioner reference that taught a generation of red and blue teams).&lt;/p&gt;
&lt;p&gt;Will Schroeder and Lee Christensen (2021, &quot;Certified Pre-Owned,&quot; the AD CS catalog that became Pass-the-Certificate). Oliver Lyak (2022, Certifried, the CVE that forced Microsoft to ship KB5014754). And Dirk-jan Mollema (2020, the Primary Refresh Token research this article argues is the most consequential credential-theft work since 2008). The cast is small. The lineage they built is the load-bearing structure of every Windows penetration test in 2026.&lt;/p&gt;
&lt;p&gt;How is it possible that the same attack works in 1997 and 2026? The answer is structural, not coincidental -- and once you see it, you cannot unsee it.&lt;/p&gt;
&lt;h2&gt;2. The Architectural Property the Family Shares&lt;/h2&gt;
&lt;p&gt;NTLM authentication never asks for the password as a string. It asks for a function of the hash. The hash &lt;em&gt;is&lt;/em&gt; the password.&lt;/p&gt;
&lt;p&gt;That sentence is the article&apos;s load-bearing claim, and the rest of this section is its proof.&lt;/p&gt;
&lt;p&gt;The Microsoft specification for the NTLM protocol -- &lt;code&gt;[MS-NLMP]&lt;/code&gt;, sections 3.3.1 and 3.3.2 -- writes the response computation in pseudocode. For NTLMv1, the server sends an 8-byte challenge; the client computes &lt;code&gt;NtChallengeResponse = DESL(ResponseKeyNT, challenge)&lt;/code&gt;, where &lt;code&gt;ResponseKeyNT = NTOWFv1(password) = MD4(UNICODE(password))&lt;/code&gt; [@ms-nlmp-3-3-1]. &lt;code&gt;DESL&lt;/code&gt; is a variant of DES that pads the 16-byte NT hash to 21 bytes with five zero bytes, splits the result into three 7-byte sub-keys, runs DES on the 8-byte challenge under each sub-key, and concatenates the three 8-byte ciphertexts to form a 24-byte response.&lt;/p&gt;
&lt;p&gt;NTLMv2 is more elaborate -- the response key is &lt;code&gt;NTOWFv2 = HMAC_MD5(MD4(UNICODE(password)), UNICODE(Uppercase(User) + UserDom))&lt;/code&gt;, and the proof string is &lt;code&gt;HMAC_MD5&lt;/code&gt; of the challenge concatenated with a target-info structure -- but the structural property is identical: the cleartext password appears in exactly one place in the entire protocol, the input to the hash function on the client. The verifier performs the same computation against the stored NT hash from the SAM or NTDS.dit, and compares. Neither side ever transmits the password [@ms-nlmp-3-3-2].&lt;/p&gt;
&lt;p&gt;This is what Microsoft means when its institutional documentation says Pass-the-Hash &quot;cannot be patched at the protocol level.&quot; There is nothing to patch.The same property holds for any challenge-response protocol whose verifier stores a determinable function of the password rather than the password itself: Kerberos with stored long-term keys, CHAP with shared secrets, OAuth client_credentials with shared secrets, every HMAC-based proof-of-possession scheme.&lt;/p&gt;
&lt;p&gt;The protocol takes a stored hash and produces a response. Swap the user&apos;s hash for the attacker&apos;s hash, and the protocol still produces a valid response, signed by the substituted key. The bug is not a bug; it is a documented property.&lt;/p&gt;

A family of Windows authentication protocols (NTLMv1 and NTLMv2) in which a server sends a random challenge and the client returns a response computed by applying a keyed cryptographic primitive (DES or HMAC-MD5) to that challenge under a key derived from the user&apos;s password. The verifier holds the same key and recomputes the response to confirm. The cleartext password is never transmitted [@ms-nlmp-3-3-1] [@ms-nlmp-3-3-2].

The 16-byte MD4 of the user&apos;s password as UTF-16 little-endian (`MD4(UNICODE(Passwd))` in the NLMP pseudocode). Unsalted by design, because NT was originally specified for an offline domain controller that has to verify against a fixed reference value. The NT hash is the long-term symmetric Windows authentication secret for every account, stored locally in the SAM and centrally in the NTDS.dit Active Directory database [@ms-nlmp-3-3-1].

The technique of authenticating to a service that uses NTLM (or any protocol descended from the same family) by feeding a stolen NT hash directly to the response-construction function, instead of typing a password the function would then hash. The terminology and the first working demonstration are due to Paul Ashton, NTBugtraq, April 1997 [@ashton-exploitdb-19197].

sequenceDiagram
    participant Client
    participant Server
    participant Verifier as SAM or NTDS.dit
    Client-&amp;gt;&amp;gt;Server: NTLM_NEGOTIATE
    Server-&amp;gt;&amp;gt;Client: NTLM_CHALLENGE with 8-byte nonce
    Note over Client: ResponseKeyNT equals NTOWFv1 of stored NT hash
    Note over Client: NtChallengeResponse equals DESL of ResponseKeyNT and nonce
    Client-&amp;gt;&amp;gt;Server: NTLM_AUTHENTICATE with response
    Server-&amp;gt;&amp;gt;Verifier: Look up stored NT hash for user
    Verifier--&amp;gt;&amp;gt;Server: Stored NT hash
    Note over Server: Recompute DESL of stored hash and nonce
    Server-&amp;gt;&amp;gt;Client: Authentication succeeds if responses match
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The hash is the password. Any long-term authentication artefact reachable by the process that uses it is replayable -- and every credential type the rest of this article discusses (Kerberos TGT, certificate private key, Primary Refresh Token session key) is a different instance of this same property. Defenses can isolate one artefact at a time; the property is intrinsic to the architecture.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ashton&apos;s 1997 patch was the protocol-disclosure proof. He swapped a single function call -- &lt;code&gt;SMBencrypt(pass, cryptkey, pword)&lt;/code&gt; became &lt;code&gt;E_P24(p21, cryptkey, pword)&lt;/code&gt;, where &lt;code&gt;p21&lt;/code&gt; is the user&apos;s LM hash read directly from &lt;code&gt;smbpasswd&lt;/code&gt; -- and Samba&apos;s &lt;code&gt;smbclient&lt;/code&gt; authenticated to NT 3.51 and NT 4.0 file servers without ever knowing the user&apos;s password [@ashton-exploitdb-19197]. You can read the patch in five minutes. It is also, in a precise sense, the first proof that NTLM&apos;s response computation is hash-equivalent: if substituting the hash works, then mathematically the hash is what the protocol wanted all along.&lt;/p&gt;
&lt;p&gt;And then nothing happened for eleven years.&lt;/p&gt;
&lt;p&gt;That gap deserves its own explanation, because the eleven-year interregnum is the cleanest failure mode in the lineage.&lt;/p&gt;
&lt;p&gt;Wikipedia&apos;s modern summary of the pre-2008 limitation reads: &quot;even after performing NTLM authentication successfully using the pass the hash technique, tools like Samba&apos;s SMB client might not have implemented the functionality the attacker might want to use. This meant that it was difficult to attack Windows programs that use DCOM or RPC. Also, because attackers were restricted to using third-party clients when carrying out attacks, it was not possible to use built-in Windows applications, like Net.exe or the Active Directory Users and Computers tool amongst others, because they asked the attacker or user to enter the cleartext password to authenticate, and not the corresponding password hash value.&quot; [@wikipedia-pass-the-hash]&lt;/p&gt;

Inside Microsoft the 1997 patch was treated as confirming a known property of LSASS-resident credentials, not as a new attack class. The institutional position was that any compromise yielding the hash already implied SYSTEM-equivalent access, and that the realistic chain was &quot;exfiltrate the hash and crack it offline,&quot; not &quot;replay the hash.&quot; The architectural counter-claim -- that *replaying* the hash from inside a Windows process bypasses every native-tool obstacle -- took a decade to land in the practitioner literature. The 2012 Duckwall + Campbell Black Hat USA paper named the lag in its title: &quot;Still Passing the Hash 15 Years Later.&quot; [@duckwall-campbell-bh2012]
&lt;p&gt;If the obstacle is &quot;built-in Windows tools ask for cleartext,&quot; the architectural answer is to put the substituted hash &lt;em&gt;inside&lt;/em&gt; the Windows process that those tools rely on. That insight took eleven years to operationalise. The person who operationalised it was Hernan Ochoa, in 2008.&lt;/p&gt;
&lt;h2&gt;3. From Patch to Toolkit: The Windows-Native Pivot&lt;/h2&gt;
&lt;p&gt;By 2008, Ashton&apos;s 1997 patch had been sitting on NTBugtraq for eleven years. Hernan Ochoa had a different idea: instead of patching the client, patch the &lt;em&gt;credential cache&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The artefact Ochoa shipped at CanSecWest 2008 and Black Hat USA 2008 was called the &lt;em&gt;Pass-the-Hash Toolkit&lt;/em&gt;, distributed through Core Security Technologies&apos; open-source projects page [@corelabs-pshtoolkit-wayback]. It contained two executables. &lt;code&gt;IAM.EXE&lt;/code&gt; opened the LSASS process with &lt;code&gt;PROCESS_VM_WRITE&lt;/code&gt;, located the cached credential block for the current interactive logon session, and overwrote the NT hash and username fields with attacker-supplied values. &lt;code&gt;PTH.EXE&lt;/code&gt; spawned a target process under a substituted hash.&lt;/p&gt;
&lt;p&gt;Once the substitution was in place, every native Windows SSO consumer -- &lt;code&gt;net.exe&lt;/code&gt;, &lt;code&gt;wmic&lt;/code&gt;, &lt;code&gt;mstsc&lt;/code&gt; once Restricted Admin RDP shipped years later, SMB, RPC, DCOM -- transparently picked up the attacker-supplied hash, because the OS handed them what it believed were the legitimate user&apos;s credentials.&lt;/p&gt;
&lt;p&gt;Wikipedia summarises the architectural pivot in one paragraph: &quot;It allowed the user name, domain name, and password hashes cached in memory by the Local Security Authority to be changed at runtime &lt;em&gt;after&lt;/em&gt; a user was authenticated -- this made it possible to &apos;pass the hash&apos; using standard Windows applications, and thereby to undermine fundamental authentication mechanisms built into the operating system.&quot; [@wikipedia-pass-the-hash] The eleven-year limitation was gone. Pass-the-Hash was now a Windows-native attack that worked against any tool that read its credentials from LSASS -- which in practice meant &lt;em&gt;every&lt;/em&gt; Windows tool.&lt;/p&gt;

The user-mode Windows process (`lsass.exe`) that handles interactive logon, owns the Security Reference Monitor&apos;s policy decisions, and -- relevant to this article -- caches the in-memory credential material that supports Single Sign-On for the duration of each logon session: NT hashes for NTLM, Kerberos TGTs and session keys, certificate handles, and (since Azure AD / Entra ID device join) Primary Refresh Token material in the CloudAP plug-in. Every credential-replay technique in this article reaches its target by reading LSASS in some form.
&lt;p&gt;The 2012 retrospective is where the security industry stopped pretending Pass-the-Hash was solved. Alva Duckwall and Christopher Campbell shipped a Black Hat USA 2012 paper titled, unambiguously, &quot;Still Passing the Hash 15 Years Later.&quot; [@duckwall-campbell-bh2012] The title is the load-bearing pull-quote: it named Ashton 1997 as the origin, Ochoa 2008 as the Windows-native pivot, and the industry&apos;s continued failure to ship a structural fix as the central fact. From this point onwards Microsoft itself acknowledged Pass-the-Hash as a structural property of NTLM rather than a fixable bug.&lt;/p&gt;
&lt;p&gt;Hernan Ochoa&apos;s Windows Credentials Editor (WCE), released a year after the Pass-the-Hash Toolkit, developed the same LSASS-injection primitive on a separate code base. Two independent implementations converging on the same memory-access pattern in the same window is the clearest indication that the architectural insight -- &quot;the credential is sitting in a process you can write to&quot; -- was overdetermined once anyone went looking for it.&lt;/p&gt;
&lt;p&gt;What did Ashton&apos;s 1997 patch leave on the table? The other long-term credentials that LSASS held. The NT hash was the first. There would be more.&lt;/p&gt;
&lt;p&gt;If you can read the NT hash from LSASS, you can read the Kerberos TGT from LSASS. The same memory-access primitive that animates &lt;code&gt;IAM.EXE&lt;/code&gt; is one commit away from animating &lt;code&gt;sekurlsa::tickets&lt;/code&gt;. That commit shipped in May 2011. Its author was a twenty-five-year-old French programmer named Benjamin Delpy.&lt;/p&gt;
&lt;h2&gt;4. Mimikatz and the Kerberos Turn&lt;/h2&gt;
&lt;p&gt;In May 2011, Benjamin Delpy posted his first public release of a program he had been writing as a side project to learn C. He was twenty-five, working as an IT manager at an institution he has never publicly named. Andy Greenberg&apos;s Wired profile records the date: &quot;He released it publicly in May 2011, but as a closed source program.&quot; [@wired-greenberg-mimikatz] Wikipedia corroborates: &quot;He released the first version of the software in May 2011 as closed source software.&quot; [@wikipedia-mimikatz] The program was called Mimikatz.&lt;/p&gt;
&lt;p&gt;What made Mimikatz architecturally different from Ochoa&apos;s toolkit was that it was &lt;em&gt;modular&lt;/em&gt;. The credential-extraction primitives lived in named command groups: &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; dumped NT hashes from LSASS; &lt;code&gt;sekurlsa::tickets&lt;/code&gt; dumped Kerberos tickets from LSASS; &lt;code&gt;kerberos::ptt&lt;/code&gt; injected a stolen ticket into the current Kerberos cache via the documented &lt;code&gt;LsaCallAuthenticationPackage&lt;/code&gt; API with the &lt;code&gt;KerbSubmitTicketMessage&lt;/code&gt; message [@ms-lsa-call-auth-package]; &lt;code&gt;lsadump::dcsync&lt;/code&gt; (added August 2015, in collaboration with Vincent Le Toux) impersonated a domain controller and asked another DC for the krbtgt hash via the IDL_DRSGetNCChanges replication RPC [@adsec-dcsync-p1729].&lt;/p&gt;
&lt;p&gt;Same LSASS, different artefact, different protocol surface. The architectural property section 2 named had two artefacts to work with on Windows: the NT hash, and the Kerberos TGT.&lt;/p&gt;
&lt;p&gt;This is &lt;strong&gt;Pass-the-Ticket&lt;/strong&gt; (Generation 2). The stolen TGT plus its session key authenticates the holder as the original principal for the ticket&apos;s lifetime, which on a default AD deployment is ten hours, renewable for seven days. Time complexity per replay: O(1). The TGT session key is the load-bearing piece -- without it, the ticket is opaque encrypted bytes that the holder cannot decrypt, sign, or present back to the KDC. Mimikatz&apos;s &lt;code&gt;sekurlsa::tickets /export&lt;/code&gt; writes the ticket as a &lt;code&gt;.kirbi&lt;/code&gt; file on disk; &lt;code&gt;kerberos::ptt &amp;lt;file&amp;gt;&lt;/code&gt; re-injects on any machine where the user has a Kerberos credentials cache.&lt;/p&gt;

The long-lived Kerberos credential issued by the KDC&apos;s Authentication Service (AS-REP) in response to a successful AS-REQ. The TGT is encrypted under the KDC&apos;s own krbtgt-account long-term key and contains a session key that the client uses to subsequently request service tickets from the Ticket Granting Service (TGS). Specification: RFC 4120, section 3 [@rfc-4120]. On a Windows Active Directory deployment the default TGT lifetime is 10 hours with renewal up to 7 days.

The technique of extracting a Kerberos TGT (and its session key) from one machine&apos;s LSASS-resident Kerberos cache and injecting it into another machine&apos;s cache, so that subsequent service-ticket requests authenticate as the ticket&apos;s original principal. Tool of record: Mimikatz `sekurlsa::tickets` + `kerberos::ptt`; equivalent functionality in Rubeus and Impacket.

sequenceDiagram
    participant Victim as Victim host
    participant Attacker as Attacker host
    participant KDC
    Note over Victim: User logged in, TGT cached in LSASS Kerberos package
    Attacker-&amp;gt;&amp;gt;Victim: mimikatz sekurlsa::tickets export
    Victim--&amp;gt;&amp;gt;Attacker: TGT.kirbi (ticket plus session key)
    Note over Attacker: mimikatz kerberos::ptt TGT.kirbi
    Attacker-&amp;gt;&amp;gt;KDC: TGS-REQ presenting injected TGT
    KDC--&amp;gt;&amp;gt;Attacker: TGS-REP service ticket
    Attacker-&amp;gt;&amp;gt;Attacker: Authenticate to any Kerberos service as the victim
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A common shorthand says that Microsoft&apos;s Credential Guard isolated NT hashes, so attackers shifted to TGTs. That arrow runs backwards in time. Pass-the-Ticket predates Credential Guard by years -- the Mimikatz Kerberos primitives developed between the May 2011 closed-source release and the April 6, 2014 open-source commit (the earliest verifiable source-level evidence for &lt;code&gt;sekurlsa::tickets&lt;/code&gt; and &lt;code&gt;kerberos::ptt&lt;/code&gt;), and were presented in detail at Black Hat USA 2014 by Duckwall and Delpy [@infocondb-bh2014-duckwall] [@duckwall-delpy-bh2014-wp]. Pass-the-Ticket exists because TGTs are also in LSASS, not as a defensive response. The shift to a new artefact happened because the &lt;em&gt;architectural property&lt;/em&gt; of credential extraction generalised, not because Credential Guard pushed attackers there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The third generation followed shortly. &lt;strong&gt;Overpass-the-Hash&lt;/strong&gt; observes that for the RC4-HMAC Kerberos encryption type -- the Windows default from Windows 2000 through November 2022 -- the user&apos;s long-term Kerberos key is the unchanged NT hash.&lt;/p&gt;
&lt;p&gt;RFC 4757, authored by K. Jaganathan, L. Zhu, and J. Brezak of Microsoft and published as informational in December 2006, specifies the RC4-HMAC enctype&apos;s long-term key as the existing NT hash without modification [@rfc-4757]. An attacker who holds the NT hash can drive a legitimate Kerberos AS-REQ to the KDC, encrypt the timestamp pre-auth blob with the NT hash as the RC4-HMAC key, and receive a real TGT signed by the real krbtgt.&lt;/p&gt;
&lt;p&gt;The economic effect is large. Pass-the-Hash gets you NTLM-based services -- SMB, RPC, and any protocol over them. Overpass-the-Hash gets you the entire Kerberos surface: Kerberos-only services, services that require Kerberos for delegation, services with NTLM disabled at the GPO level. Same NT hash. Different downstream protocol. Strictly larger attack surface.&lt;/p&gt;

The technique of presenting a stolen NT hash to the KDC as the user&apos;s long-term RC4-HMAC Kerberos key (per RFC 4757 [@rfc-4757]), obtaining a real TGT signed by the real krbtgt, and operating as a real Kerberos client for the ticket&apos;s lifetime. Tool of record: Mimikatz `sekurlsa::pth /user: /domain: /ntlm: /run:` and Rubeus `asktgt /user: /rc4:`. Per Sean Metcalf&apos;s adsecurity.org reference, the technique is named &quot;over&quot; because the hash is promoted one notch up the protocol stack from NTLM into Kerberos [@adsec-mimikatz-p556] [@adsec-kerberos-p2293].

sequenceDiagram
    participant Attacker
    participant KDC
    participant Service as Kerberos service
    Note over Attacker: Holds NT hash for user (e.g. from sekurlsa::logonpasswords)
    Attacker-&amp;gt;&amp;gt;KDC: AS-REQ with PA-ENC-TIMESTAMP encrypted under RC4-HMAC(NT hash)
    KDC-&amp;gt;&amp;gt;KDC: Verify PA-ENC-TIMESTAMP decrypts cleanly
    KDC--&amp;gt;&amp;gt;Attacker: AS-REP with real TGT signed by krbtgt
    Attacker-&amp;gt;&amp;gt;KDC: TGS-REQ for Service
    KDC--&amp;gt;&amp;gt;Attacker: TGS-REP service ticket
    Attacker-&amp;gt;&amp;gt;Service: AP-REQ authenticate as user
    Service--&amp;gt;&amp;gt;Attacker: Access granted
&lt;p&gt;The naming has its own story. The Mimikatz capability is Delpy&apos;s; the term &quot;Overpass-the-Hash&quot; and the taxonomic framing that distinguishes it from straight Pass-the-Hash spread through the practitioner community via Sean Metcalf&apos;s adsecurity.org reference [@adsec-mimikatz-p556] and the Duckwall + Delpy Black Hat USA 2014 talk and whitepaper [@infocondb-bh2014-duckwall] [@duckwall-delpy-bh2014-wp]. The earliest archived snapshot of the adsecurity.org reference is October 1, 2014; the talk timestamp is August 7, 2014. The two sources are essentially contemporaneous, and Metcalf&apos;s later &quot;Red vs. Blue&quot; Black Hat USA 2015 whitepaper consolidates the practitioner taxonomy [@metcalf-bh2015-red-vs-blue].&lt;/p&gt;
&lt;p&gt;The &quot;Overpass&quot; coinage is a deliberate semantic argument that the technique is one notch &lt;em&gt;above&lt;/em&gt; Pass-the-Hash on the protocol stack: the NT hash, which began life as an NTLM response key, is being promoted into Kerberos as a long-term encryption key. The naming credit is socially distributed -- Metcalf, Delpy, Duckwall, and Mimikatz&apos;s own command group all carry traces of it -- so this article uses Metcalf&apos;s reference as the canonical practitioner explainer rather than as a single inventor citation.&lt;/p&gt;
&lt;p&gt;The DigiNotar incident in September 2011 is the first publicly attributed criminal use of Mimikatz, four months after Delpy&apos;s first public release. The Dutch certificate authority DigiNotar -- founded 1998, acquired by VASCO in January 2011, hacked in June 2011, declared bankrupt in September 2011 [@wikipedia-diginotar] -- was used to issue hundreds of fraudulent certificates that were then used in man-in-the-middle attacks on Iranian Gmail users [@wikipedia-diginotar] [@fox-it-operation-black-tulip].&lt;/p&gt;
&lt;p&gt;Greenberg&apos;s Wired profile records that Delpy was told by the breach investigators that Mimikatz had been used during the intrusion [@wired-greenberg-mimikatz]. The single-source attribution warrants a hedge -- Greenberg&apos;s source is Delpy himself, quoting investigators -- but the underlying breach timeline is solid.&lt;/p&gt;

The decision to open-source Mimikatz on April 6, 2014 is dated by the GitHub repository banner: `mimikatz 2.0 alpha (x86) release &quot;Kiwi en C&quot; (Apr  6 2014 22:02:03)` [@mimikatz-github]. The precipitating event, as Delpy told Wired, was a trip to Moscow: he returned to his hotel room to find a stranger at his laptop; a second man approached him in the lobby that evening and demanded source code on a USB stick. He decided defenders needed the source as much as the attackers already did, and pushed it to GitHub when he got home [@wired-greenberg-mimikatz].
&lt;p&gt;By 2014, the credential-replay family had three generations -- Pass-the-Hash, Pass-the-Ticket, Overpass-the-Hash -- and Microsoft&apos;s only documented response was a forty-page PDF. The next section is what that PDF said, and why documentation alone cannot end an attack class.&lt;/p&gt;
&lt;h2&gt;5. Documentation Is Not Defense&lt;/h2&gt;
&lt;p&gt;By December 2012, Microsoft had a problem. Duckwall and Campbell had just shipped a Black Hat USA paper titled &quot;Still Passing the Hash 15 Years Later&quot; [@duckwall-campbell-bh2012]. Mimikatz was eighteen months old. The institutional position that Pass-the-Hash was a &quot;post-compromise issue&quot; -- the line Microsoft had held since 1997 -- was no longer survivable in public.&lt;/p&gt;
&lt;p&gt;The institutional response came in two waves. &lt;em&gt;Mitigating Pass-the-Hash Attacks and Other Credential Theft&lt;/em&gt;, version 1, shipped in late 2012 (most practitioner secondaries place it in December 2012; no primary Microsoft URL with a verifiable v1 timestamp survives today).&lt;/p&gt;
&lt;p&gt;Version 2 followed in July 2014, extending the v1 playbook with the new defensive surfaces that shipped in Windows 8.1 and Windows Server 2012 R2: &lt;a href=&quot;https://paragmali.com/blog/who-is-allowed-to-log-in-where-the-kdc-side-answer-to-creden/&quot; rel=&quot;noopener&quot;&gt;Protected Users&lt;/a&gt; as a deployable security group, Restricted Admin RDP as a default-available feature, LSA Protection (RunAsPPL) as a registry-toggleable defense, and Authentication Policies and Silos as KDC-side restrictions [@ms-download-mitigating-pth-v2]. The two whitepapers are the closest thing the industry got to an institutional Microsoft acknowledgment that Pass-the-Hash was a load-bearing operational problem requiring a defensive playbook rather than a patch.&lt;/p&gt;
&lt;p&gt;What did the playbook recommend? Three orthogonal stopgaps, each with a published bypass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Protected Users&lt;/strong&gt; (Windows Server 2012 R2). A security group whose membership bans, on the DC side, NTLM authentication, DES and RC4 Kerberos pre-authentication, and Kerberos unconstrained delegation; and, on the device side, NTLM caching of the user&apos;s plaintext credentials or NTOWF and Kerberos DES/RC4 long-term keys. Member TGTs are capped at 240 minutes (four hours) with no renewal [@ms-protected-users]. Documented bypasses: requires explicit opt-in per account, breaks any service that depended on unconstrained delegation, does not apply to computer accounts or service accounts by default, and has no effect on Kerberos AES-key extraction from LSASS (since AES keys are not banned; only RC4 is).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Restricted Admin RDP&lt;/strong&gt; (introduced in Windows 8.1 / Server 2012 R2 RTM, October 2013; backported to Windows 7 / Server 2008 R2 / Windows 8 / Server 2012 by KB2871997 on May 13, 2014 [@ms-kb2871997-may2014]). An opt-in RDP mode that authenticates to the target without sending credentials, so a compromised target cannot harvest the RDP user&apos;s hash from its own LSASS. Documented bypass: opt-in per session, applies only to RDP, leaves SMB, WMI, and RPC unprotected. And it &lt;em&gt;enables&lt;/em&gt; Pass-the-Hash for RDP -- the BloodHound &lt;code&gt;CanRDP&lt;/code&gt; edge documents the abuse path with the exact Mimikatz command for injecting a stolen NT hash into &lt;code&gt;mstsc.exe /restrictedadmin&lt;/code&gt; [@bloodhound-canrdp].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LSA Protection / RunAsPPL&lt;/strong&gt; (Windows 8.1). A registry toggle that marks LSASS as a &lt;a href=&quot;https://paragmali.com/blog/protected-process-light-when-the-administrator-isnt-enough/&quot; rel=&quot;noopener&quot;&gt;Protected Process Light&lt;/a&gt;, so non-PPL processes (including unsigned admin tools) cannot open it with &lt;code&gt;PROCESS_VM_READ&lt;/code&gt;. Documented bypass: any signed kernel driver -- including loadable third-party drivers -- can still read PPL memory, and an attacker with local admin can load such a driver. The itm4n analysis includes the verbatim Mimikatz output where &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; returns access-denied against a PPL-marked LSASS, and shows that an attacker who loads a signed driver via the BYOVD pattern (&quot;bring your own vulnerable driver&quot;) or escalates to kernel mode bypasses the marking. itm4n&apos;s framing -- &quot;Credential Guard and LSA Protection are actually complementary&quot; [@itm4n-lsass-runasppl] -- is also the prediction: PPL is part of the answer, but only when paired with the architectural pivot still to come.&lt;/p&gt;

A Windows Server 2012 R2 security group whose membership applies a set of restrictions, enforced jointly by the device and the domain controller, that block the most commonly extracted long-term credential material: no NTLM, no Kerberos RC4 or DES pre-auth, no unconstrained delegation, no NT-hash caching, and a 240-minute TGT lifetime with no renewal [@ms-protected-users].
&lt;p&gt;The structural point is this. Documentation tells administrators &lt;em&gt;what to do&lt;/em&gt;. It does not prevent the underlying LSASS-resident credential extraction. Every defense documented in v1 and v2 of the Mitigating-PtH whitepapers is bypassable, with a known and published technique, on any system where the attacker already has local administrator -- and local administrator is exactly what Pass-the-Hash exploitation &lt;em&gt;already implies&lt;/em&gt;. The defender&apos;s win condition is to keep the attacker from ever getting to local admin in the first place; once they have it, every documented mitigation is a speed bump rather than a wall.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The 2012-2014 era&apos;s load-bearing failure mode was assuming that telling administrators where credentials &lt;em&gt;should&lt;/em&gt; live would prevent extraction from where they &lt;em&gt;do&lt;/em&gt; live. Protected Users, Restricted Admin RDP, RunAsPPL, and Authentication Silos are all useful, and stacked together they raise the cost of post-admin exploitation. None of them moves the credential out of the address space the attacker can read.&lt;/p&gt;
&lt;/blockquote&gt;

A common secondary characterisation cites a &quot;v3 2017&quot; of the whitepaper alongside v1 and v2. That document does not exist in Microsoft Download Center ID 36036; the page lists Version 2.0; the 2023 Wayback snapshot of the same Download Center page records Date Published 7/7/2014, while the live page now shows a 2024 republication date for the same Version 2.0 PDF without a version bump [@ms-download-mitigating-pth-v2]. The Download Center page carries v2 metadata only -- v1&apos;s late-2012 date is sourced through contemporary practitioner literature rather than a primary Microsoft timestamp. After 2014 the post-v2 institutional documentation moves to the Microsoft Learn Credential Guard page rather than to a third whitepaper revision -- a structural choice, because by 2015 the architectural answer has shifted from prose to code.
&lt;p&gt;By mid-2014 Microsoft&apos;s institutional position was that the protocol-level fix was unavailable and the architectural answer would need to &lt;em&gt;relocate the credentials&lt;/em&gt;. If credentials cannot stay in LSASS where every admin process can read them, the credentials have to be moved to a place admin processes cannot read. That insight produces Credential Guard.&lt;/p&gt;
&lt;h2&gt;6. Credential Guard and the Architectural Pivot&lt;/h2&gt;
&lt;p&gt;On July 29, 2015, Microsoft shipped Windows 10 Enterprise [@ms-lifecycle-w10-enterprise]. Hidden in the RTM build was the first defense in the credential-replay lineage that wasn&apos;t documentation: hardware-rooted isolation. They called it Credential Guard.&lt;/p&gt;
&lt;p&gt;The architecture is worth unpacking carefully, because every later generation of the family is best read as &quot;what does this attack do to the assumptions Credential Guard makes?&quot;&lt;/p&gt;
&lt;p&gt;Credential Guard runs on top of Virtualization-Based Security. The Windows hypervisor partitions user mode into two virtual trust levels. VTL0 is the normal user partition: normal user-mode processes, including the normal LSASS, and the normal kernel. VTL1 is the isolated user partition: a small set of &lt;em&gt;trustlets&lt;/em&gt;, signed user-mode processes the hypervisor protects from VTL0 inspection. Credential Guard&apos;s trustlet is LSAISO (&quot;LSA Isolated&quot;), a stripped-down clone of the LSA credential cache holding the material Microsoft wants out of VTL0. Hypervisor-enforced Code Integrity (HVCI) below enforces W^X on the VTL0 kernel, blocking kernel-mode bypasses that would otherwise read VTL1 memory directly.&lt;/p&gt;

The Windows architecture that runs a Type-1 hypervisor below the normal Windows kernel and partitions user mode into VTL0 (the normal partition) and VTL1 (the isolated partition). VTL1 hosts trustlets that the hypervisor protects from VTL0 inspection, even from kernel-mode VTL0 code. VBS is the substrate for Credential Guard, HVCI, the System Guard secure-launch chain, and the secure kernel.

The Windows feature that relocates NT hashes, Kerberos TGT session keys, and &quot;credentials stored by applications as domain credentials&quot; from the in-VTL0 LSASS to the in-VTL1 LSAISO trustlet, so that the credential cache is unreadable from any VTL0 process or driver. Shipped in Windows 10 RTM (July 2015); default-enabled on hardware-eligible domain-joined non-DC systems in Windows 11 22H2 (September 2022) [@ms-learn-credential-guard].

The isolated-user-mode LSA process (`lsaiso.exe`) that holds Credential Guard&apos;s protected credential material. Runs in VTL1, unreadable from VTL0 kernel or user processes. Communicates with the VTL0 LSASS through a small RPC surface for authorised authentication operations only.
&lt;p&gt;What does Credential Guard isolate? The Microsoft Learn page is unambiguous: &quot;Credential Guard prevents credential theft attacks by protecting NTLM password hashes, Kerberos Ticket Granting Tickets (TGTs), and credentials stored by applications as domain credentials.&quot; [@ms-learn-credential-guard] Those three categories are also the three categories the previous three generations of the family targeted. Pass-the-Hash hits NTLM password hashes. Pass-the-Ticket hits Kerberos TGTs. Overpass-the-Hash hits NTLM password hashes promoted into Kerberos. Credential Guard moves all three out of VTL0 LSASS into VTL1 LSAISO. On a hardware-eligible domain-joined Windows 10/11 system with Credential Guard enabled, all three attacks return empty buffers.&lt;/p&gt;
&lt;p&gt;The institutional importance of the change is that under Microsoft&apos;s own &lt;em&gt;Windows Security Servicing Criteria&lt;/em&gt;, Credential Guard is a &lt;em&gt;security boundary&lt;/em&gt; -- which means a bypass is a CVE-class vulnerability rather than a documentation gap.&lt;/p&gt;
&lt;p&gt;The criteria&apos;s load-bearing definitions: &quot;A security boundary provides a logical separation between the code and data of security domains with different levels of trust&quot; and &quot;Does the vulnerability violate the goal or intent of a security boundary or a security feature?&quot; [@msrc-windows-servicing-criteria] Pre-2015 Pass-the-Hash defenses were documentation; Credential Guard is the first defense the criteria treats as CVE-class under the boundary &quot;admin -&amp;gt; VBS (LSAISO trustlet).&quot;&lt;/p&gt;

flowchart TD
    subgraph VTL0[VTL0 normal partition]
        A[User processes]
        B[LSASS]
        K[VTL0 kernel]
    end
    subgraph VTL1[VTL1 isolated partition]
        L[LSAISO trustlet]
        SK[Secure kernel]
    end
    H[Hypervisor]
    A --&amp;gt; B
    K --&amp;gt; B
    B -- authorised RPC only --&amp;gt; L
    H --&amp;gt; VTL0
    H --&amp;gt; VTL1
    SK --&amp;gt; L
    K -. blocked by HVCI .-&amp;gt; L
&lt;p&gt;What does Credential Guard &lt;em&gt;not&lt;/em&gt; isolate? This is the load-bearing question for the rest of the article. The same Microsoft Learn page enumerates four caveats, each verbatim.&lt;/p&gt;
&lt;p&gt;First, the Active Directory database and the SAM. &quot;Credential Guard doesn&apos;t provide protections for the Active Directory database or the Security Accounts Manager (SAM).&quot; [@ms-learn-credential-guard] This is the &lt;a href=&quot;https://paragmali.com/blog/two-checkmarks-and-the-keys-to-the-kingdom-how-active-direct/&quot; rel=&quot;noopener&quot;&gt;DCSync&lt;/a&gt; gap: an attacker with the right replication privileges can ask a DC to hand over every hash in the directory, and Credential Guard cannot intervene because the data is being released through a legitimate, authorised API rather than being read from LSASS.&lt;/p&gt;
&lt;p&gt;Second, domain controllers. &quot;Enabling Credential Guard on domain controllers isn&apos;t recommended. Credential Guard doesn&apos;t provide any added security to domain controllers.&quot; [@ms-learn-credential-guard] The KDC must read the krbtgt account&apos;s long-term key in cleartext to issue tickets; the architectural exception is intrinsic to Kerberos rather than a Microsoft oversight.&lt;/p&gt;
&lt;p&gt;Third, application credentials outside the &quot;domain credentials&quot; scope. Certificate private keys held by CryptoAPI key containers, third-party authentication package secrets, and -- the one this article eventually argues is the most consequential -- the Primary Refresh Token material held by the CloudAP authentication plug-in, are all out of scope by construction.&lt;/p&gt;
&lt;p&gt;Fourth, and most importantly, the institutional acknowledgment of the supersession pattern. Microsoft Learn reproduces it verbatim on the same page, the prophecy the rest of this article spends its time documenting being fulfilled:&lt;/p&gt;

While Credential Guard is a powerful mitigation, persistent threat attacks will likely shift to new attack techniques, and you should also incorporate other security strategies and architectures. -- Microsoft Learn, *Credential Guard overview* [@ms-learn-credential-guard]
&lt;p&gt;That sentence, written about the 2015 Credential Guard architecture, accurately predicts the 2021-2022 shift to Pass-the-Certificate and the 2020-present shift to Pass-the-PRT. It is Microsoft&apos;s own structural prediction that the family will continue to evolve to the next artefact Credential Guard&apos;s verbatim scope does not cover. The rest of this article reads as the unfolding of that prediction.&lt;/p&gt;

The Kerberos KDC must read the krbtgt account&apos;s long-term key to encrypt the TGT issued in every AS-REP. That key has to be available to the LSA process in cleartext, on every DC, on every ticket issuance, by protocol. Putting krbtgt behind LSAISO would mean issuing every TGT through an inter-trust-level RPC call -- a non-trivial performance penalty on every authentication in an Active Directory forest -- and would not actually close the architectural gap, because the trustlet itself would still need to do the cleartext work that LSASS does today. The exception is honest about an architectural reality rather than concealing it.
&lt;p&gt;PPL and Credential Guard are &lt;em&gt;complementary&lt;/em&gt;, not alternatives. itm4n&apos;s analysis [@itm4n-lsass-runasppl] makes the case carefully: RunAsPPL raises the bar from &quot;any admin process can read LSASS&quot; to &quot;any signed driver can read LSASS,&quot; and Credential Guard closes the signed-driver bypass with hardware-rooted hypervisor isolation. They stack. The 2026 best-practice Windows endpoint has both turned on.&lt;/p&gt;
&lt;p&gt;The default-enablement window shows how long this took to land. Credential Guard shipped enabled-by-policy in Windows 10 RTM in 2015, but did not become &lt;em&gt;default-enabled on hardware-eligible domain-joined non-DC systems&lt;/em&gt; until Windows 11 22H2 in September 2022 [@ms-learn-credential-guard]. Seven years of uneven deployment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Four residuals from the Microsoft Learn page: the Active Directory database and the SAM are out of scope; domain controllers are out of scope by recommendation; application credentials outside the &quot;domain credentials&quot; category (certificates, CloudAP material, third-party authentication packages) are out of scope by construction; and persistent threats are &lt;em&gt;expected&lt;/em&gt; to shift to new attack techniques. Each residual maps to a later generation of this article: AD database -&amp;gt; DCSync; certificates -&amp;gt; Pass-the-Certificate; CloudAP -&amp;gt; Pass-the-PRT.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each new credential type needs its own isolation boundary. Credential Guard isolates NT hashes and TGT session keys. It does not isolate certificate private keys, because in 2015 nobody was replaying certificates at scale. And it does not isolate the Primary Refresh Token, because in 2015 the Primary Refresh Token did not yet exist.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Each new credential type needs its own isolation boundary. The pattern is reusable but does not transfer automatically -- and the gap between &quot;what fits in the boundary&quot; and &quot;what credentials Windows actually uses&quot; is exactly the territory where the next attack generation grows.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;7. Pass-the-Certificate: The Predictable Response&lt;/h2&gt;
&lt;p&gt;If the NT hash is isolated and RC4-HMAC is banned, what is the next long-term credential Windows accepts? The answer was hiding in plain sight: every Active-Directory-integrated enterprise had been running Microsoft&apos;s PKI since 2008, and almost every PKI deployment had at least one template-level catastrophe.&lt;/p&gt;
&lt;p&gt;On June 17, 2021, Will Schroeder and Lee Christensen posted &quot;Certified Pre-Owned&quot; on Medium, with the accompanying 143-page whitepaper [@specterops-certified-pre-owned] [@specterops-certified-pre-owned-pdf]. The post named ESC1 through ESC8 in a single document, with paired DETECT and PREVENT recommendations, and shipped three pieces of tooling at the same Black Hat USA 2021 cycle: Certify (offensive enrollment), ForgeCert (golden-certificate forging using a stolen CA private key), and PSPKIAudit (defensive enumeration). The Medium post&apos;s tone was unsubtle:&lt;/p&gt;

Of note, nearly every environment with AD CS that we&apos;ve examined for domain escalation misconfigurations has been vulnerable. It&apos;s hard for us to overstate what a big deal these issues are. -- Will Schroeder and Lee Christensen, *Certified Pre-Owned* [@specterops-certified-pre-owned]
&lt;p&gt;The &lt;a href=&quot;https://paragmali.com/blog/certified-pre-owned-ad-cs-and-active-directorys-second-trust/&quot; rel=&quot;noopener&quot;&gt;ESC catalog&lt;/a&gt; organises certificate misconfigurations by the abuse primitive they enable. ESC1 is the canonical example: a published certificate template that allows the enrollee to supply the Subject Alternative Name, contains a client-authentication Extended Key Usage, has permissive enrollment rights, and has no effective approval gates.&lt;/p&gt;
&lt;p&gt;An attacker who can enroll for such a template requests a certificate naming a victim principal -- say, the domain administrator -- in the SAN. The certificate&apos;s private key is now the attacker&apos;s. PKINIT-authenticate to the KDC with that certificate, and the KDC issues a TGT for the named principal. Domain escalation, in three commands.&lt;/p&gt;

Microsoft&apos;s enterprise PKI. Issues X.509 certificates from administrator-defined templates that pin a certificate&apos;s permitted uses (Extended Key Usages), its enrollment authorisation rules, its subject and SAN generation policy, and its revocation behaviour. Ships as a Windows Server role; deployed in essentially every Active-Directory-integrated enterprise.

Kerberos pre-authentication using a certificate&apos;s private key in place of a long-term symmetric key. Specified by RFC 4556 (L. Zhu and B. Tung, Microsoft and Aerospace, June 2006) [@rfc-4556]. The certificate&apos;s UPN SAN (or its dNSHostName for computer accounts) maps the certificate to the principal whose TGT the KDC will issue. PKINIT is the protocol surface most commonly exercised by Pass-the-Certificate against domain controllers that support certificate-based authentication.

The Windows TLS implementation. Supports TLS client-certificate authentication, which authenticated LDAPS uses. When a domain controller does not support PKINIT (Schroeder + Christensen documented this case in the original catalog; AlmondOffSec built tooling for it), an attacker can authenticate to LDAPS over Schannel with a stolen client certificate and perform high-privilege LDAP operations without traversing the KDC.

The technique of authenticating to Active Directory with a stolen X.509 certificate&apos;s private key, via PKINIT to the KDC or via Schannel client-certificate authentication to LDAPS. Named in this form by Yannick Méheut&apos;s PassTheCert tool and blog post (May 2022) [@almondoffsec-passthecert-github] [@almondoffsec-passthecert-blog], though the technique class was catalogued by Schroeder and Christensen eleven months earlier [@specterops-certified-pre-owned]. Tool of record: Certify (C#), Certipy (Python, ESC1-ESC16 [@certipy-wiki-privesc]), and Rubeus PKINIT mode.

sequenceDiagram
    participant Atk as Attacker (user)
    participant CA as Enterprise CA
    participant KDC
    Atk-&amp;gt;&amp;gt;CA: Enrol for template ESC1, SAN field set to Domain Administrator
    CA--&amp;gt;&amp;gt;Atk: X.509 certificate plus private key
    Note over Atk: Now holds a certificate naming the victim principal
    Atk-&amp;gt;&amp;gt;KDC: AS-REQ with PKINIT pre-auth using the stolen private key
    KDC-&amp;gt;&amp;gt;KDC: Validate certificate, map SAN to victim principal
    KDC--&amp;gt;&amp;gt;Atk: AS-REP with TGT for victim principal
    Atk-&amp;gt;&amp;gt;KDC: TGS-REQ for any service
    KDC--&amp;gt;&amp;gt;Atk: TGS-REP service ticket
&lt;p&gt;The CVE-class case lands on May 10, 2022. Oliver Lyak of IFCR discloses Certifried, CVE-2022-26923, an Active Directory Domain Services elevation-of-privilege vulnerability in which the combination of three Microsoft defaults -- &lt;code&gt;ms-DS-MachineAccountQuota = 10&lt;/code&gt; (any authenticated user can add up to 10 computer accounts to the domain), the default Machine template (which a computer account can enroll for), and the KDC&apos;s permissive &lt;code&gt;dNSHostName&lt;/code&gt;-to-SAN binding logic -- lets any authenticated user obtain a certificate for any computer account in the forest, including domain controllers.&lt;/p&gt;
&lt;p&gt;PKINIT-authenticate as a domain controller, and the KDC issues you a TGT for the DC; from there, DCSync extracts the krbtgt key and the domain is yours. Domain escalation from any authenticated user, with the only required misconfiguration being &lt;em&gt;Microsoft&apos;s defaults&lt;/em&gt; [@nvd-cve-2022-26923] [@semperis-cve-2022-26923].&lt;/p&gt;
&lt;p&gt;The defensive response shipped the same day. Microsoft published KB5014754 on May 10, 2022 -- coordinated disclosure, with the patch shipping in the same window as the CVE -- introducing a new X.509 extension &lt;code&gt;szOID_NTDS_CA_SECURITY_EXT&lt;/code&gt; (OID &lt;code&gt;1.3.6.1.4.1.311.25.2&lt;/code&gt;) that carries the requesting principal&apos;s SID at certificate issuance.&lt;/p&gt;
&lt;p&gt;The KDC&apos;s new strong-mapping logic refuses certificates that fail one of four conditions: the SID extension is present and matches; an issuer-serial mapping is present; a Subject Key Identifier mapping is present; or a SHA1-public-key mapping is present. The KB&apos;s load-bearing sentence: &quot;In Full Enforcement mode, if a certificate fails the strong (secure) mapping criteria (see Certificate mappings), authentication will be denied.&quot; [@ms-kb5014754]&lt;/p&gt;
&lt;p&gt;The KB5014754 change-log preserves a forensic artefact of the coordinated-disclosure timeline that is easy to miss. The current change-log row reads, verbatim: &quot;9/10/2025 - Corrected the Enforcement mode date from September 10, 2025, to September 9, 2025.&quot; [@ms-kb5014754] An off-by-one date correction, captured in the public KB. The kind of detail that only shows up when a small team has had to ship a date repeatedly against a multi-year audit-to-enforcement schedule.&lt;/p&gt;
&lt;p&gt;The enforcement timeline tells you how long even a CVE-class fix took to drive through deployment. Audit mode (May 10, 2022). Enforcement mode with a registry escape that admins could use to revert to compatibility (February 11, 2025). Final cutover with no escape (September 9, 2025) [@ms-kb5014754]. Three years and four months between the patch and the day Microsoft stopped accepting non-strong certificate mappings. Faster than the Credential Guard default-enablement window, but still measured in years.&lt;/p&gt;
&lt;p&gt;The naming history deserves a disambiguation. The &lt;em&gt;catalog&lt;/em&gt; -- ESC1 through ESC8, the full taxonomy of AD CS misconfigurations -- is Schroeder and Christensen, June 2021 [@specterops-certified-pre-owned]. The &lt;em&gt;wire-level technique name&lt;/em&gt; &quot;Pass-the-Certificate&quot; is popularised by AlmondOffSec&apos;s PassTheCert PoC (Yannick Méheut, May 4, 2022), which targets LDAP/S via Schannel client-cert authentication when PKINIT is unavailable, as a fallback path for environments where domain controllers do not support certificate-based Kerberos pre-authentication [@almondoffsec-passthecert-github] [@almondoffsec-passthecert-blog]. The blog post documents the &lt;code&gt;KDC_ERR_PADATA_TYPE_NOSUPP&lt;/code&gt; error path that diverts the PKINIT-blocked attacker into Schannel.&lt;/p&gt;
&lt;p&gt;The AlmondOffSec blog post acknowledges the social attribution of the term: &quot;Note for Googlers: this tool extends the notion of Pass the Certificate, thus dubbed by @_nwodtuhs in his Twitter thread on AD CS and PKINIT.&quot; [@almondoffsec-passthecert-blog] The technique name is socially attributed; the catalog framing is editorial.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A common shorthand says that KB5014754 bound NTOWFs to Kerberos, and that this is what forced attackers to shift to certificates. That arrow runs backwards in time. KB5014754 is the &lt;em&gt;response&lt;/em&gt; to Certifried, not the cause of Pass-the-Certificate. The technique class was catalogued by Schroeder and Christensen in June 2021, eleven months before KB5014754 shipped, and the PassTheCert tool that gave the technique its wire-level name appeared six days before Certifried&apos;s disclosure. The shift to certificates happened because certificates were the next long-term credential type Credential Guard did not isolate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What does KB5014754 actually close? Three specific CVEs in the Certifried family: CVE-2022-26923 (the original SID-spoof Certifried disclosure), CVE-2022-26931 (UPN / sAMAccountName collision spoof), and CVE-2022-34691 (the certificate-pre-dating-account-creation case) [@ms-kb5014754]. What does it &lt;em&gt;not&lt;/em&gt; close? The broader ESC2 through ESC8 catalog, which is administrative hardening rather than CVE-class control. And it does not close ESC9 through ESC16, which were enumerated &lt;em&gt;after&lt;/em&gt; KB5014754 shipped and include cases like the &lt;code&gt;CT_FLAG_NO_SECURITY_EXTENSION&lt;/code&gt; template flag that &lt;em&gt;exempts&lt;/em&gt; a template from the very SID extension the patch introduced [@specterops-certs-patches-2022] [@certipy-wiki-privesc].&lt;/p&gt;
&lt;p&gt;The current state of the catalog: as of the 2025 Certipy 5.x documentation, ESC1 through ESC16 is the practitioner enumeration, with each technique characterised by a template-level, ACL-level, CA-administrator-level, NTLM-relay-level, SID-extension-level, or mapping-level abuse primitive [@certipy-wiki-privesc]. Microsoft Defender for Identity&apos;s certificates posture assessment tracks nine distinct ESC numbers as of the 2025 documentation -- ten posture assessments, because ESC4 owner and ESC4 ACL are tracked as separate sub-cases (ESC1, ESC2, ESC3, ESC4 owner, ESC4 ACL, ESC6 preview, ESC7, ESC8, ESC11, ESC15) [@ms-defender-id-certs]. Same pattern as Pass-the-Hash in 2012-2014: documentation tells administrators what to do; the structural exposure is downstream of how each enterprise built its templates years earlier.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ESC ID&lt;/th&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Closed by KB5014754&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ESC1&lt;/td&gt;
&lt;td&gt;Template -- enrollee supplies SAN, client-auth EKU, permissive enrollment&lt;/td&gt;
&lt;td&gt;Partial: SID extension binds requester at issuance; ESC1 still works if the SID extension is absent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC2&lt;/td&gt;
&lt;td&gt;Template -- enrollee supplies SAN, Any-Purpose or no EKU&lt;/td&gt;
&lt;td&gt;No -- administrative hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC3&lt;/td&gt;
&lt;td&gt;Template -- Certificate Request Agent enrollment-agent abuse&lt;/td&gt;
&lt;td&gt;No -- administrative hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC4&lt;/td&gt;
&lt;td&gt;ACL -- writeable template configuration&lt;/td&gt;
&lt;td&gt;No -- administrative hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC6&lt;/td&gt;
&lt;td&gt;CA -- &lt;code&gt;EDITF_ATTRIBUTESUBJECTALTNAME2&lt;/code&gt; flag set on the CA&lt;/td&gt;
&lt;td&gt;No -- CA-level hardening (was MS22-23, separately patched)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC8&lt;/td&gt;
&lt;td&gt;NTLM relay -- HTTP enrolment endpoints reachable from low-privilege contexts&lt;/td&gt;
&lt;td&gt;No -- relay-defence hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC9&lt;/td&gt;
&lt;td&gt;Template -- &lt;code&gt;CT_FLAG_NO_SECURITY_EXTENSION&lt;/code&gt; exempts template from the SID extension&lt;/td&gt;
&lt;td&gt;No -- by design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC11&lt;/td&gt;
&lt;td&gt;NTLM relay -- ICPR RPC endpoint without sign / seal&lt;/td&gt;
&lt;td&gt;No -- relay-defence hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ESC16&lt;/td&gt;
&lt;td&gt;CA -- security-extension disabled at the CA level&lt;/td&gt;
&lt;td&gt;No -- CA-level hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Table 1. A representative slice of the ESC1-ESC16 catalog showing what KB5014754 closes and what remains administrative hardening [@specterops-certify-wiki] [@certipy-wiki-privesc] [@specterops-certs-patches-2022].&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;KB5014754 is a CVE-class fix for one sub-case. The broader ADCS catalog is administrative hardening. And the &lt;em&gt;next&lt;/em&gt; credential type -- the one that defeats Credential Guard, Protected Users, and KB5014754 simultaneously -- was already shipping in commodity Mimikatz code by August 2020.&lt;/p&gt;
&lt;h2&gt;8. Pass-the-PRT: The CloudAP Frontier&lt;/h2&gt;
&lt;p&gt;By August 2020, Microsoft had two architectural defenses against credential replay that the security industry actually trusted: Credential Guard for local Active Directory credentials, and (eighteen months later) KB5014754 for the certificate-replay class. Then a Dutch security researcher named Dirk-jan Mollema published a 21-minute read that broke both, in the same paragraph, by stealing a different credential type.&lt;/p&gt;
&lt;p&gt;The credential is the &lt;a href=&quot;https://paragmali.com/blog/inside-the-primary-refresh-token-the-cryptographic-seam-betw/&quot; rel=&quot;noopener&quot;&gt;Primary Refresh Token&lt;/a&gt;. The two foundational write-ups are Mollema&apos;s &quot;Abusing Azure AD SSO with the Primary Refresh Token&quot; [@mollema-prt-abusing] and its follow-on &quot;Digging further into the Primary Refresh Token&quot; [@mollema-prt-digging], both posted in August 2020. The second post is the single most-cited primary source in the fifth generation of the family. Read it once and you understand why Pass-the-PRT is structurally different from everything that came before.&lt;/p&gt;
&lt;p&gt;A PRT is a JSON Web Token refresh token issued by Microsoft Entra ID (formerly Azure AD) to Entra-joined or Hybrid-joined Windows devices, paired with a session key (HMAC-SHA256 secret) and bound to a device key registered at device join.&lt;/p&gt;
&lt;p&gt;The Microsoft Entra documentation describes the artefact precisely: &quot;A Primary Refresh Token (PRT) is a key artifact of Microsoft Entra authentication ... Once issued, a PRT is valid for 90 days and is continuously renewed as long as the user actively uses the device.&quot; [@ms-entra-concept-prt] On Windows the PRT is renewed every four hours during sign-in. The device-key registration binds the PRT to the device that owns it -- and is what an attacker has to work around to use a stolen PRT on a different device.&lt;/p&gt;

The Microsoft Entra-issued long-lived refresh token for SSO on Entra-joined or Hybrid-joined Windows devices. Carries a session key (HMAC-SHA256) used to sign per-request `x-ms-RefreshTokenCredential` cookies, and binds to a device transport key registered at device join. Default lifetime is 90 days with sliding renewal as long as the user actively uses the device; an inactivity timeout governs when an idle PRT must be re-acquired [@ms-entra-concept-prt]. The PRT is the load-bearing artefact for Single Sign-On to every Entra-integrated resource the device&apos;s user can reach.
&lt;p&gt;The PRT default lifetime is 90 days per the Microsoft Entra documentation, with renewal every four hours during Windows sign-in [@ms-entra-concept-prt]. The 14-day figure that sometimes appears in secondary references is the inactivity timeout on certain device states, not the PRT lifetime itself; this article uses the Microsoft Entra documentation&apos;s value to avoid the conflation.&lt;/p&gt;
&lt;p&gt;Where the PRT &lt;em&gt;lives&lt;/em&gt; is what makes the rest of the architecture work -- and what makes it vulnerable. The PRT is &lt;em&gt;hybrid&lt;/em&gt;: issued and revoked cloud-side by Entra ID, stored and used client-side via the &lt;strong&gt;CloudAP&lt;/strong&gt; authentication plug-in, which is loaded into LSASS like any other Windows authentication package.&lt;/p&gt;
&lt;p&gt;The load-bearing structural fact is that CloudAP is &lt;em&gt;in LSASS&lt;/em&gt;, not behind the LSAISO trustlet. Credential Guard&apos;s classical isolation does not extend to the CloudAP plug-in&apos;s working memory, because Credential Guard&apos;s scope is the three credential categories its design predates -- NT hashes, Kerberos TGTs, and &quot;domain credentials&quot; -- and the PRT is none of those [@mollema-prt-abusing].&lt;/p&gt;

The Windows authentication package (`cloudap.dll`, loaded into LSASS) that handles authentication against Microsoft Entra ID for Entra-joined and Hybrid-joined devices. Holds the device&apos;s Primary Refresh Token, its session key, and the derived material used to sign per-request PRT cookies. Sits inside LSASS in VTL0, *not* inside the LSAISO trustlet in VTL1; Credential Guard does not currently extend its isolation to CloudAP&apos;s working memory.
&lt;p&gt;The mechanism, as Mollema and Delpy developed it through the second half of 2020, runs as follows. Mimikatz &lt;code&gt;dpapi::cloudapkd /unprotect&lt;/code&gt; extracts the PRT (the encrypted-by-Entra refresh-token blob) and the session key from CloudAP&apos;s working memory.&lt;/p&gt;
&lt;p&gt;The attacker constructs an &lt;code&gt;x-ms-RefreshTokenCredential&lt;/code&gt; JWT carrying the PRT in the &lt;code&gt;refresh_token&lt;/code&gt; claim, &lt;code&gt;is_primary: true&lt;/code&gt;, and a &lt;code&gt;request_nonce&lt;/code&gt; obtained by an unauthenticated POST against the Entra ID v1 token endpoint at &lt;code&gt;https://login.microsoftonline.com/common/oauth2/token&lt;/code&gt; with form-encoded body &lt;code&gt;grant_type=srv_challenge&lt;/code&gt; (the server-challenge nonce pattern used by the ROADtools &lt;code&gt;roadtx prt&lt;/code&gt; reference implementation; the response is a JSON object with a &lt;code&gt;Nonce&lt;/code&gt; field). The signature is HMAC-SHA256 over the JWT under the session key. The completed cookie is presented to &lt;code&gt;login.microsoftonline.com&lt;/code&gt; from any machine, and Entra ID returns access and refresh tokens for any resource the original user can reach. Mollema&apos;s second post describes the collaboration that built the tooling:&lt;/p&gt;

Around the same time Benjamin Delpy took up my &apos;challenge&apos; of recovering PRT data from `lsass` with mimikatz. We combined forces and ended up with tooling that is not only able to extract the PRT and associated cryptographic keys (such as the session key) from memory, but can also use these keys to create new SSO cookies or modify existing ones. -- Dirk-jan Mollema, *Digging further into the Primary Refresh Token* [@mollema-prt-digging]
&lt;p&gt;The operational tooling closed quickly. Mollema&apos;s &lt;code&gt;roadtx prt&lt;/code&gt; (part of ROADtools [@roadtools-github]) automates the full chain end-to-end -- extract the material, mint the cookie, complete the OAuth dance, hand the attacker an access token. The Mimikatz &lt;code&gt;dpapi::cloudapkd&lt;/code&gt; command landed in the open-source repository the same window. Pass-the-PRT moved from research artefact to commodity tooling in months, not years.&lt;/p&gt;

sequenceDiagram
    participant Victim as Victim device (Entra-joined)
    participant Attacker as Attacker device
    participant Entra as login.microsoftonline.com
    Note over Victim: PRT plus session key held by CloudAP in LSASS
    Attacker-&amp;gt;&amp;gt;Victim: mimikatz dpapi::cloudapkd /unprotect
    Victim--&amp;gt;&amp;gt;Attacker: PRT (encrypted blob) plus session key
    Attacker-&amp;gt;&amp;gt;Entra: POST /common/oauth2/token grant_type=srv_challenge (unauthenticated)
    Entra--&amp;gt;&amp;gt;Attacker: request_nonce
    Note over Attacker: Build x-ms-RefreshTokenCredential JWT
    Note over Attacker: Sign HMAC-SHA256 with extracted session key
    Attacker-&amp;gt;&amp;gt;Entra: POST /token with PRT cookie
    Entra--&amp;gt;&amp;gt;Attacker: Access and refresh tokens
    Attacker-&amp;gt;&amp;gt;Attacker: Authenticate to any Entra resource as victim user
&lt;p&gt;Now the analytical core. Pass-the-PRT defeats three Microsoft defenses &lt;em&gt;simultaneously&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;Credential Guard&lt;/strong&gt; is out of scope. The CloudAP material is not an NT hash, not a Kerberos TGT, and not &quot;credentials stored by applications as domain credentials&quot; in the verbatim sense the Credential Guard documentation uses. Credential Guard&apos;s VBS-based isolation does not extend to CloudAP. The defense was designed in 2015 against the three credential types the family had then; the PRT is a credential type the family had not yet evolved into [@ms-learn-credential-guard].&lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;KB5014754&lt;/strong&gt; is out of scope. The PRT cookie does not traverse the KDC&apos;s certificate-mapping logic at all; it is a JWT signed by an HMAC and authenticated at the Entra ID token endpoint. The strong certificate mapping that Microsoft drove through five years of audit-to-enforcement timeline has no relevance to a credential that never touches the KDC [@ms-kb5014754].&lt;/p&gt;
&lt;p&gt;Third, &lt;strong&gt;Protected Users&lt;/strong&gt; is out of scope. Protected Users is an Active-Directory-only construct, enforced on Windows Server domain controllers and on AD-joined member devices. Entra ID is a separate identity provider with separate enforcement; the 240-minute TGT cap, the NTLM ban, and the RC4 ban that Protected Users enforces simply do not apply [@ms-protected-users].&lt;/p&gt;
&lt;p&gt;The TPM-sealing finding is where the architectural pattern becomes most precise. Microsoft began sealing the PRT session key to a &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM-bound key&lt;/a&gt; on TPM-2.0-eligible hardware -- a defense that, in principle, makes the raw session key cryptographically non-exportable. Mollema&apos;s finding in the August 2020 second post is that the seal does not close the attack, because CloudAP holds &lt;em&gt;derived&lt;/em&gt; PRT-cookie-signing material in its own working memory in LSASS, and the attacker only needs the derived material:&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. 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. -- Dirk-jan Mollema, *Digging further into the Primary Refresh Token* [@mollema-prt-digging]
&lt;p&gt;The structural reason the standard hardware-rooted defense pattern does not transfer: the attacker does not need the raw session key out of the TPM. They need only the in-memory derived material CloudAP itself uses to sign the cookies, and that derived material lives in the same address space Credential Guard does not isolate.&lt;/p&gt;
&lt;p&gt;The TPM seals the key. CloudAP uses the key. Whatever CloudAP can read, an attacker with administrator and a memory-access primitive can also read. The defense pattern that worked for NT hashes (move them out of the address space) has not been applied to CloudAP -- and until it is, the TPM seal is a speed bump rather than a wall.&lt;/p&gt;
&lt;p&gt;{`
// Pedagogical demonstration of the JWT structure used in Pass-the-PRT
// cookie minting. Uses placeholder values throughout; no real PRT material.&lt;/p&gt;
&lt;p&gt;const base64url = (buf) =&amp;gt; Buffer.from(buf).toString(&apos;base64&apos;)
  .replace(/=+$/, &apos;&apos;).replace(/\+/g, &apos;-&apos;).replace(/\//g, &apos;_&apos;);&lt;/p&gt;
&lt;p&gt;const header = { alg: &apos;HS256&apos;, ctx: &apos;AAAAAAAA&apos; };
const payload = {
  // The PRT itself, an opaque refresh-token string Entra issued to the
  // device. In a real attack this comes from mimikatz dpapi::cloudapkd.
  refresh_token: &apos;AQABAAAAAAA...redacted...&apos;,
  // Marks this cookie as a primary refresh token cookie.
  is_primary: &apos;true&apos;,
  // Fresh nonce from an unauthenticated POST against the v1 token endpoint
  // at login.microsoftonline.com/common/oauth2/token with form body
  // grant_type=srv_challenge (returns JSON with Nonce field; the canonical
  // server-challenge pattern used by ROADtools roadtx prt).
  request_nonce: &apos;AwABAAEAAAAC...&apos;,
  iat: Math.floor(Date.now() / 1000),
};&lt;/p&gt;
&lt;p&gt;// HMAC-SHA256 over the JWT under the session key recovered from CloudAP.
// Placeholder key for demonstration only.
const sessionKey = Buffer.alloc(32); // 32 bytes of zeros (fake)
const crypto = require(&apos;crypto&apos;);&lt;/p&gt;
&lt;p&gt;const h = base64url(JSON.stringify(header));
const p = base64url(JSON.stringify(payload));
const sig = base64url(
  crypto.createHmac(&apos;sha256&apos;, sessionKey).update(h + &apos;.&apos; + p).digest()
);&lt;/p&gt;
&lt;p&gt;console.log(&apos;Header segment:    &apos; + h);
console.log(&apos;Payload segment:   &apos; + p);
console.log(&apos;Signature segment: &apos; + sig);
console.log();
console.log(&apos;Full PRT cookie: &apos; + h + &apos;.&apos; + p + &apos;.&apos; + sig);
// In a real attack the attacker would now POST this as the
// x-ms-RefreshTokenCredential cookie to login.microsoftonline.com.
`}&lt;/p&gt;
&lt;p&gt;The current partial mitigations are worth enumerating, because none of them closes the gap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Token Protection&lt;/strong&gt; (a &lt;a href=&quot;https://paragmali.com/blog/who-decided-this-token-is-good-a-field-guide-to-conditional-/&quot; rel=&quot;noopener&quot;&gt;Conditional Access&lt;/a&gt; session control) attempts to ensure that only device-bound sign-in session tokens are accepted at the Entra ID token endpoint for protected resources. The Microsoft Learn page is explicit about both the design intent and the deployment limits: &quot;Token Protection is a 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; [@ms-entra-token-protection] As of the current documentation the &lt;em&gt;supported resources&lt;/em&gt; are five named applications: Exchange Online, SharePoint Online, Microsoft Teams, Azure Virtual Desktop, and Windows 365. Browser applications are out of scope; &quot;Token Protection currently supports native applications only. Browser-based applications are not supported.&quot; [@ms-entra-token-protection] Most Entra-integrated SaaS is unbound.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous Access Evaluation&lt;/strong&gt; (CAE) shortens the window during which a stolen PRT is operationally usable, by allowing the token endpoint to revoke tokens within minutes of a triggering signal (password change, risk-based detection, conditional-access policy update) [@ms-entra-cae]. CAE is evaluation-time, not isolation. It shortens the window between extraction and detection-driven revocation; it does not prevent extraction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hybrid-joined PRT renewal binding&lt;/strong&gt; partially closes the cross-tenant case for hybrid Azure AD Join configurations, but does not address the same-tenant Pass-the-PRT case that Mollema&apos;s original 2020 posts described [@ms-entra-hybrid-join-plan].&lt;/p&gt;
&lt;p&gt;The institutional acknowledgment of the supersession pattern is the verbatim Microsoft Learn sentence already quoted in section 6 [@ms-learn-credential-guard]: written about the 2015 Credential Guard architecture, it accurately predicts the 2020 Pass-the-PRT shift. The credential-replay family has reached the point where &lt;em&gt;every Microsoft defense&lt;/em&gt; in the on-prem stack runs in parallel against an attack the on-prem stack cannot reach.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Pass-the-PRT defeats Credential Guard, KB5014754, and Protected Users simultaneously because each defense was designed around a different long-term artefact, and the PRT is none of them. The architectural property -- a long-term authentication artefact reachable from the using process is replayable -- is unchanged. The artefact moved.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Six years after Mollema&apos;s disclosure, the TPM-resilience finding still holds. The CloudAP plug-in is still in LSASS. Credential Guard still does not extend its boundary. Pass-the-PRT remains the operational frontier in 2026.&lt;/p&gt;
&lt;h2&gt;9. The 5x5 Matrix and the Irregular Cadence&lt;/h2&gt;
&lt;p&gt;Five generations of attack. Five generations of defense. They map onto each other unevenly; the gaps are not five years.&lt;/p&gt;
&lt;p&gt;The matrix below consolidates the lineage at a glance. Rows are the attack generations (in the order they entered the practitioner literature). Columns are the defense generations (in the order they shipped). Each cell records whether that defense closes that attack on a fully-deployed hardware-eligible 2026 Windows 11 endpoint with the control turned on. &quot;Closed&quot; means the attack returns empty buffers or fails authentication; &quot;Partial&quot; means the defense increases attacker cost or closes one sub-case; &quot;Open&quot; means the defense&apos;s design scope does not include that attack.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack \ Defense&lt;/th&gt;
&lt;th&gt;Mitigating-PtH whitepapers (2012/2014)&lt;/th&gt;
&lt;th&gt;Protected Users + RunAsPPL + Restricted Admin (2013-2014)&lt;/th&gt;
&lt;th&gt;Credential Guard / LSAISO (2015)&lt;/th&gt;
&lt;th&gt;KB5014754 strong mapping (2022)&lt;/th&gt;
&lt;th&gt;Token Protection + CAE (2023-2025)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Pass-the-Hash (Ashton 1997, Ochoa 2008)&lt;/td&gt;
&lt;td&gt;Open (documentation)&lt;/td&gt;
&lt;td&gt;Partial (Protected Users members)&lt;/td&gt;
&lt;td&gt;Closed (on enabled endpoints)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pass-the-Ticket (Delpy 2011, Duckwall+Delpy 2014)&lt;/td&gt;
&lt;td&gt;Open (documentation)&lt;/td&gt;
&lt;td&gt;Partial (4-hour TGT cap for Protected Users)&lt;/td&gt;
&lt;td&gt;Closed (TGT session key in LSAISO)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overpass-the-Hash (Delpy / Metcalf 2014)&lt;/td&gt;
&lt;td&gt;Open (documentation)&lt;/td&gt;
&lt;td&gt;Partial (RC4 banned for Protected Users)&lt;/td&gt;
&lt;td&gt;Closed (NT hash in LSAISO)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pass-the-Certificate (Schroeder + Christensen 2021, Méheut 2022)&lt;/td&gt;
&lt;td&gt;Open (documentation)&lt;/td&gt;
&lt;td&gt;Open (cert keys outside scope)&lt;/td&gt;
&lt;td&gt;Open (cert keys outside scope)&lt;/td&gt;
&lt;td&gt;Partial (closes Certifried sub-case; ESC2-ESC16 remain)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pass-the-PRT (Mollema + Delpy 2020)&lt;/td&gt;
&lt;td&gt;Open (Entra ID is separate IDP)&lt;/td&gt;
&lt;td&gt;Open (Entra ID is separate IDP)&lt;/td&gt;
&lt;td&gt;Open (CloudAP not in LSAISO)&lt;/td&gt;
&lt;td&gt;Open (not in scope)&lt;/td&gt;
&lt;td&gt;Partial (5 named resources; browser apps out of scope)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Table 2. The 5x5 attack/defense matrix. The union of every cell in the rightmost column of &quot;Closed&quot; entries is the set of attacks Microsoft&apos;s published 2026 defenses close on hardware-eligible non-DC endpoints with every control turned on; that set is precisely the first three rows.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The matrix makes the structure visible. No single defense closes all attacks, and no single attack is closed by all defenses. The union of every defense closes Pass-the-Hash, Pass-the-Ticket, and Overpass-the-Hash on hardware-eligible non-DC Windows 10/11 systems with all controls enabled. It partially closes Pass-the-Certificate (for the Certifried sub-case) and partially closes Pass-the-PRT (for five named resources). Both of the most recent generations remain operationally open against any deployment that does not run those specific controls -- which is most deployments.&lt;/p&gt;
&lt;p&gt;The cadence is just as uneven as the matrix. The original input that prompted this article claimed &quot;every Windows defense against credential replay buys about five years before the attack class evolves to the next credential type.&quot; Memorable. Also wrong. The actual timeline produces gaps from eleven months to eleven years, with one negative interval:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1997 -&amp;gt; 2008&lt;/strong&gt; (eleven years) for the Samba-patch -&amp;gt; Windows-native pivot. Pass-the-Hash existed for over a decade as a Unix-side novelty before Ochoa&apos;s LSASS-injection insight made it Windows-native.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2008 -&amp;gt; 2011&lt;/strong&gt; (three years) for the Mimikatz Pass-the-Ticket extension. The same memory-access primitive that animated &lt;code&gt;IAM.EXE&lt;/code&gt; was retargeted at a different artefact.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2012/2014 -&amp;gt; 2015&lt;/strong&gt; (one to three years) for the Mitigating-PtH whitepapers -&amp;gt; Credential Guard pivot. Documentation took a year and a half to ship; the architectural counter took another.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2021 -&amp;gt; 2022&lt;/strong&gt; (eleven months) for the AD CS catalog -&amp;gt; KB5014754 response. Coordinated disclosure compressed this gap; Certifried&apos;s CVE-class status forced a CVE-class response.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2020 -&amp;gt; 2025+&lt;/strong&gt; (open-ended) for Pass-the-PRT with no Credential-Guard-equivalent shipped. As of the Windows 11 25H2 cycle there is no public roadmap for VBS-class isolation of CloudAP material.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most striking gap is the 2020/2021 &lt;em&gt;negative&lt;/em&gt; interval. Pass-the-PRT (Mollema, August 2020) and the AD CS catalog (Schroeder + Christensen, June 2021) are siblings rather than sequential; Pass-the-PRT predates Pass-the-Certificate as a &lt;em&gt;named technique&lt;/em&gt; by ten months, even though the article treats them as Generation 4 and Generation 5 in narrative order. The Generation N -&amp;gt; N+1 framing is &lt;em&gt;taxonomic&lt;/em&gt;, not strictly chronological. The reader needs this distinction to read the lineage accurately: the attack class evolves along the architectural property, not along the calendar.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &quot;every Windows defense buys five years&quot; framing is what you see if you select the cleanest pairings (Mitigating-PtH 2012/2014 to Credential Guard 2015 plus an artificial 2020-targeted &quot;next attack&quot;). When you look at the actual intervals, you see eleven years (1997-2008), three years (2008-2011), eleven months (2021-2022), and an open-ended interval (2020 onwards). The pattern is the architectural property persisting across artefact changes, not a calendar drumbeat.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The storage-class progression is the cleanest way to see the property hold across the lineage. Each row names the long-term artefact, where it lives, and which defense moved or shielded that storage class.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Long-term artefact&lt;/th&gt;
&lt;th&gt;Storage location&lt;/th&gt;
&lt;th&gt;Defense that isolated it&lt;/th&gt;
&lt;th&gt;Status 2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1A (1997 Samba)&lt;/td&gt;
&lt;td&gt;NT hash (and LM hash)&lt;/td&gt;
&lt;td&gt;Local SAM file on disk&lt;/td&gt;
&lt;td&gt;&quot;Do not store LAN Manager hash&quot; policy (Vista default-on); SAM hash extraction still works&lt;/td&gt;
&lt;td&gt;LM hash retired; NT hash extraction still works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1B (2008 Windows-native)&lt;/td&gt;
&lt;td&gt;NT hash&lt;/td&gt;
&lt;td&gt;LSASS credential cache&lt;/td&gt;
&lt;td&gt;Credential Guard relocates to LSAISO&lt;/td&gt;
&lt;td&gt;Closed on Credential-Guard-enabled endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 (2011 Mimikatz)&lt;/td&gt;
&lt;td&gt;Kerberos TGT plus session key&lt;/td&gt;
&lt;td&gt;LSASS Kerberos package&lt;/td&gt;
&lt;td&gt;Credential Guard relocates to LSAISO&lt;/td&gt;
&lt;td&gt;Closed on Credential-Guard-enabled endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 (2014)&lt;/td&gt;
&lt;td&gt;NT hash promoted to RC4-HMAC Kerberos key&lt;/td&gt;
&lt;td&gt;LSASS, same buffer as Pass-the-Hash&lt;/td&gt;
&lt;td&gt;Credential Guard relocates to LSAISO; KB5021131 makes AES the default&lt;/td&gt;
&lt;td&gt;Closed on Credential-Guard-enabled endpoints; RC4 deprecated in favour of AES [@ms-kb5021131]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 (2021 AD CS catalog)&lt;/td&gt;
&lt;td&gt;X.509 certificate private key&lt;/td&gt;
&lt;td&gt;CryptoAPI key container, TPM, or smart card&lt;/td&gt;
&lt;td&gt;TPM-resident or VSC-resident keys are cryptographically non-exportable; KB5014754 binds certificates to SIDs at issuance&lt;/td&gt;
&lt;td&gt;Partial; ESC2-ESC16 misconfigurations remain administrative hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 (2020 Pass-the-PRT)&lt;/td&gt;
&lt;td&gt;PRT session key plus derived signing material&lt;/td&gt;
&lt;td&gt;CloudAP plug-in in LSASS (session key optionally TPM-sealed)&lt;/td&gt;
&lt;td&gt;None deployed; Token Protection partially shields five resources&lt;/td&gt;
&lt;td&gt;Open&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Table 3. Storage-class progression. Each attack generation targets the next long-term artefact whose storage location is not isolated by the previous generation&apos;s defense.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The matrix and the storage-class table jointly produce the structural prediction: each generation shifts to the next available long-term artefact whose storage class the latest defense does not isolate. The graph-based formalisation of these storage-class transitions is the BloodHound edge catalog -- the &lt;code&gt;HasSession&lt;/code&gt;, &lt;code&gt;AdminTo&lt;/code&gt;, and &lt;code&gt;CanRDP&lt;/code&gt; family that operationalises &quot;which principal can reach which credential from where&quot; as a queryable property of an enterprise&apos;s directory [@bloodhound-edges]. The pattern predicts a Generation 6 outside whatever isolation scope arrives next.&lt;/p&gt;
&lt;p&gt;The most credible candidate today is &lt;strong&gt;Pass-the-DeviceKey&lt;/strong&gt;: extraction or abuse of the device transport key the PRT binds to, or of the CloudAP-derived material the cookie-signing process produces from it [@mollema-prt-phishing]. Mollema&apos;s 2023-2025 continuation work documents the underlying device-transport-key primitives in detail; the September 2025 Actor-tokens disclosure shows that cross-tenant abuse of related Entra-side material is also operational in the wild [@mollema-actor-tokens] [@mollema-federated-credentials].&lt;/p&gt;

flowchart TD
    A1[Pass-the-Hash 1A Samba&lt;br /&gt;Ashton 1997]
    A2[Pass-the-Hash 1B Windows-native&lt;br /&gt;Ochoa 2008]
    A3[Pass-the-Ticket&lt;br /&gt;Delpy 2011]
    A4[Overpass-the-Hash&lt;br /&gt;Delpy / Metcalf 2014]
    A5[Pass-the-Certificate&lt;br /&gt;Schroeder + Christensen 2021]
    A6[Pass-the-PRT&lt;br /&gt;Mollema + Delpy 2020]
    A7[Pass-the-DeviceKey forecast]
    D1[Mitigating-PtH whitepapers&lt;br /&gt;v1 2012, v2 2014]
    D2[Protected Users + RunAsPPL + Restricted Admin&lt;br /&gt;2013-2014]
    D3[Credential Guard / LSAISO&lt;br /&gt;2015, default 2022]
    D4[KB5014754 strong mapping&lt;br /&gt;2022, enforced 2025]
    D5[Token Protection + CAE&lt;br /&gt;2023-2025]
    D6[CloudAP isolation forecast]
    A1 --&amp;gt; A2
    A2 --&amp;gt; A3
    A3 --&amp;gt; A4
    A4 --&amp;gt; A5
    A4 --&amp;gt; A6
    A6 --&amp;gt; A7
    D1 --&amp;gt; D2
    D2 --&amp;gt; D3
    D3 --&amp;gt; D4
    D4 --&amp;gt; D5
    D5 -.- D6
    A2 -.- D1
    A2 -.- D2
    A3 -.- D3
    A4 -.- D3
    A5 -.- D4
    A6 -.- D5
    A7 -.- D6
&lt;p&gt;If the pattern holds, Generation 6 is already in research literature. Mollema&apos;s 2023-2025 continuation work [@mollema-prt-phishing] [@mollema-federated-credentials] [@mollema-actor-tokens] documents the device-transport-key extraction primitives. The only things missing are the name and the commodity tool. The historical pattern says we probably get both before VBS-class CloudAP isolation ships.&lt;/p&gt;
&lt;h2&gt;10. Open Problems and the 2026-2030 Forecast&lt;/h2&gt;
&lt;p&gt;The credential-replay family has six load-bearing open problems in 2026. Each is structural rather than mathematical; the cryptographic primitives that would close them already exist.&lt;/p&gt;
&lt;p&gt;The architectural lower bound -- the only configuration that closes the family in principle -- is the union of three things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Universal hardware-rooted non-extractable keys&lt;/strong&gt;: every long-term authentication artefact lives in a TPM, secure enclave, FIDO2 authenticator, or smart card, with key attestation, and is never released to software memory. &lt;strong&gt;Universal protocol-layer token binding&lt;/strong&gt;: every issued token (Kerberos service ticket, OAuth refresh token, OIDC ID token, SAML assertion) is cryptographically bound to the device that requested it, and a verifier rejects any presentation from a non-bound device. &lt;strong&gt;Universal continuous evaluation&lt;/strong&gt;: every protected resource queries the issuer in near-real-time and revokes within minutes of a triggering signal. Each component is deployed &lt;em&gt;somewhere&lt;/em&gt;; none is deployed &lt;em&gt;everywhere&lt;/em&gt;; no single vendor controls all three layers.&lt;/p&gt;
&lt;p&gt;The five concrete open problems flow from the lower bound.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The CloudAP isolation problem.&lt;/strong&gt; When does Microsoft extend VBS-class isolation to the CloudAP plug-in&apos;s working memory in LSASS? No public roadmap as of 2026. Until it ships, Pass-the-PRT remains operationally open against every Entra-joined Windows endpoint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The token-binding adoption problem.&lt;/strong&gt; Token Protection&apos;s verbatim 2026 scope is the five named resources enumerated in section 8 [@ms-entra-token-protection], which covers approximately five percent of typical Entra-integrated SaaS surface area; every other Entra-integrated resource accepts unbound tokens. The OAuth working group&apos;s RFC 9449 (DPoP, September 2023) standardises proof-of-possession at the OAuth layer [@rfc-9449], but adoption across SaaS providers and enterprise applications is uneven.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Pass-the-DeviceKey forecast.&lt;/strong&gt; Mollema&apos;s 2023-2025 continuation work exercises device-transport-key extraction primitives, federated-credential persistence on Entra applications, and cross-tenant Actor-token abuse [@mollema-prt-phishing] [@mollema-federated-credentials] [@mollema-actor-tokens]. The pattern of every previous generation predicts that whichever of these primitives commoditises first will be the next named &quot;Pass-the-X&quot; technique.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The ESC9-ESC16 hardening problem.&lt;/strong&gt; The AD CS catalog has grown from 8 entries (June 2021) to 16 (current Certipy and Certify wikis [@certipy-wiki-privesc] [@specterops-certify-wiki]); most additions are misconfiguration-class rather than CVE-class. ESC9 specifically describes the &lt;code&gt;CT_FLAG_NO_SECURITY_EXTENSION&lt;/code&gt; template flag that &lt;em&gt;exempts&lt;/em&gt; a template from the very SID extension KB5014754 introduced -- so administrators who turn that flag on for legacy compatibility reasons silently re-enable the Certifried-class abuse path on those templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hardware-backed identity ubiquity.&lt;/strong&gt; When does the union of Pluton + FIDO2 + virtual smart cards + TPM key attestation eliminate the long-term software-extractable artefact class? Human interactive sign-in to Entra ID can already be fully passwordless on supported hardware. The long tail of service accounts, scheduled tasks, on-prem AD workflows, and legacy applications resists migration; the migration is a years-long enterprise project, not a feature flag.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The non-Microsoft sibling lineages.&lt;/strong&gt; The credential-replay family is not Windows-specific. Okta session-cookie theft, Google IDP refresh-token reuse, Apple ASWebAuthSession token replay, and AWS STS session-token theft all face the same architectural property. An enterprise running Microsoft plus Okta plus Google inherits the union of every vendor&apos;s residual replay surface. The family generalises beyond Microsoft because the architectural property generalises beyond Microsoft.&lt;/p&gt;

Okta&apos;s `sessionToken` and OAuth `refresh_token` artefacts live on the device that requested them, and have been used in commodity offensive tooling since at least 2022. Google&apos;s IDP refresh tokens face the same exposure surface on managed Chromebooks. Apple&apos;s ASWebAuthSession tokens are device-bound at the platform level, which closes the cross-device replay case but not the same-device extraction case. AWS STS session tokens are not device-bound at all. The credential-replay family is a property of long-term software-extractable authentication artefacts in general; this article is Windows-specific only because Windows has the longest documented lineage.
&lt;p&gt;The institutional position is that the protocol-level fix is unavailable -- Microsoft&apos;s framing of Pass-the-Hash as a structural property of NTLM generalises directly to every later generation. A universal fix would require replacing every long-term software-extractable artefact globally with hardware-bound primitives, with mandatory token binding at every issuer and every resource server, with continuous evaluation everywhere. Each step is incrementally closable; the union has not yet closed for any deployment.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Universal hardware-rooted non-extractable keys, universal protocol-layer token binding, universal continuous evaluation. Each component is deployed somewhere; none is deployed everywhere. No single vendor controls all three layers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The architectural property the family shares has held for twenty-nine years; the defensive lineage will not close it without making &lt;em&gt;every&lt;/em&gt; long-term artefact live in hardware-rooted isolation that exceeds the host&apos;s privilege. Whether that happens in the next five years, the next ten, or the next twenty-five, is the open question the next chapter of this lineage will answer.&lt;/p&gt;
&lt;h2&gt;11. The 2026 Defender Playbook&lt;/h2&gt;
&lt;p&gt;Architectural humility does not mean defensive passivity. The 2026 estate is defensible against generations 1 through 3 and partially against generation 4; the playbook is to deploy every available control while reading Mollema&apos;s 2025 posts to know what&apos;s coming for generation 5 and beyond.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Credential Guard everywhere it can run.&lt;/strong&gt; Hardware-eligible non-DC Windows 10/11 endpoints, with the four-residual disclosure (AD database, DCs, certificate keys, CloudAP) documented for the SOC so that detection engineering does not assume Credential Guard covers categories it explicitly excludes [@ms-learn-credential-guard].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LSA Protection (RunAsPPL), UEFI-anchored&lt;/strong&gt; stacked underneath, per itm4n&apos;s &quot;complementary&quot; framing [@itm4n-lsass-runasppl]. The UEFI-anchored variant resists the registry-based bypass that a kernel-mode attacker can otherwise apply at boot.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication Silos and Protected Users for Tier-0 accounts.&lt;/strong&gt; Expect to encounter unconstrained-delegation breakage on legacy services and budget remediation; the 240-minute TGT cap is the lever that prevents long-lived Tier-0 ticket reuse [@ms-protected-users].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;KB5014754 strong-mapping enforcement&lt;/strong&gt; -- fully on by the September 9, 2025 cutover -- plus an annual certificate-template audit cycle against the ESC1-ESC16 catalog using Certipy or PSPKIAudit [@ms-kb5014754] [@certipy-wiki-privesc]. The audit is the load-bearing control because the strong-mapping fix only closes Certifried-class abuses; the template misconfigurations Schroeder and Christensen catalogued are still administrative responsibility.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conditional Access with Token Protection where supported&lt;/strong&gt; -- the five resources Microsoft Learn enumerates [@ms-entra-token-protection]. Device-bound sign-ins for privileged accounts; FIDO2 for human interactive sign-in. Know that the long tail of Entra-integrated SaaS does not enforce binding, and that a stolen PRT used against an unbound resource will still authenticate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PRT-extraction telemetry.&lt;/strong&gt; Detect CloudAP-plug-in token access from non-CloudAP processes; tie to Endpoint DLP; alert on out-of-band access to &lt;code&gt;cloudap.dll&lt;/code&gt;-owned regions of LSASS memory. Mollema&apos;s &lt;code&gt;roadtx&lt;/code&gt; and BARK produce signal patterns worth modelling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mental model: assume the PRT is the next NT hash.&lt;/strong&gt; Architect today as if Credential Guard for CloudAP shipped tomorrow -- which means TPM-attested device joins as standard, FIDO2 for every human sign-in, hardware-backed identity for service accounts wherever the vendor supports it, and conditional access policies that treat unmanaged or non-attested devices as untrusted by default.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

Open PowerShell as administrator and run:&lt;p&gt;&lt;code&gt;Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard | Format-List&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The result of interest is &lt;code&gt;SecurityServicesRunning&lt;/code&gt;. A value of &lt;code&gt;1&lt;/code&gt; in that list means Credential Guard is actively running (per the Win32_DeviceGuard documentation: &lt;code&gt;1 = Credential Guard&lt;/code&gt;, &lt;code&gt;2 = HVCI&lt;/code&gt;, &lt;code&gt;3 = System Guard secure launch&lt;/code&gt;, etc.). &lt;code&gt;SecurityServicesConfigured&lt;/code&gt; tells you what the policy intends; &lt;code&gt;SecurityServicesRunning&lt;/code&gt; tells you what the hypervisor is actually enforcing right now. The two values disagree more often than you would expect, usually because the hardware did not meet a prerequisite at boot.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The minimum-viable layer: Credential Guard on every hardware-eligible non-DC endpoint, KB5014754 enforcement-mode certificate strong mapping with an annual ESC catalog audit, and PRT-extraction telemetry tied to a real detection workflow. The first two are commodity Microsoft features that close real attack classes today; the third is the only meaningful signal you can get on the attack class that none of the published defenses currently closes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;None of this closes Pass-the-PRT. All of it shortens the dwell time.&lt;/p&gt;
&lt;h2&gt;12. Frequently Asked Questions&lt;/h2&gt;


No. The Primary Refresh Token sits in the CloudAP plug-in, which is outside Credential Guard&apos;s verbatim three-credential scope -- see section 6 (&quot;What does Credential Guard isolate?&quot;) and section 8 (&quot;Pass-the-PRT defeats three Microsoft defenses simultaneously&quot;) for the full mechanism.

No. The 1997 Ashton patch and the 2008 Ochoa Windows-native pivot are both pre-Mimikatz; see section 1 and section 3 for the full origin story. Mimikatz is the dominant *tool* (May 2011 first release) but it is not the *origin* of Pass-the-Hash.

No. The PRT is *hybrid* -- issued and revoked cloud-side by Entra ID, but stored and used client-side via the CloudAP plug-in inside LSASS. See section 8 (&quot;Where the PRT *lives*&quot;) for why this hybrid architecture is what makes Pass-the-PRT operationally tractable today.

No. It closed the three Certifried-class CVEs (CVE-2022-26923, CVE-2022-26931, CVE-2022-34691) but not the broader ESC2 through ESC16 catalog. See section 7 (&quot;What does KB5014754 actually close?&quot;) and Table 1 for the per-template breakdown.

For human interactive sign-in to Entra ID, mostly, if the entire enterprise migrates -- the FIDO2 authenticator holds a non-extractable private key in hardware, and the resulting authentication is bound to that key. For service accounts, scheduled tasks, on-prem Kerberos workflows, hybrid identity scenarios, and the long tail of legacy applications, no -- those paths still rely on long-term software-extractable artefacts (passwords, hashes, keys) by construction. The architectural counter is universal hardware-rooted non-extractable keys plus universal token binding plus universal continuous evaluation; the operational reality is partial coverage.

No public v3. See section 5 (&quot;The Mitigating-PtH v3 that never shipped&quot;) for the source-by-source disambiguation against Microsoft Download Center ID 36036.

&lt;h2&gt;13. The Pattern That Outlived Six Defenses&lt;/h2&gt;
&lt;p&gt;The 1997 patch and the 2026 attack are the same attack because the architectural property the family shares is unchanged. The artefact moved; the property did not.&lt;/p&gt;
&lt;p&gt;A long-term authentication artefact reachable by the using process is replayable. The NT hash sat in LSASS on Windows NT 4.0 and replayed against SMB. The Kerberos TGT sat in LSASS on Windows Server 2003 and replayed against Kerberos services. The NT hash sat in LSASS on Windows Server 2008 and replayed against the KDC&apos;s RC4-HMAC authentication path as a real Kerberos client.&lt;/p&gt;
&lt;p&gt;The X.509 certificate private key sat in a CryptoAPI key container on Windows Server 2012 R2 and replayed against PKINIT-supporting domain controllers as the principal in the SAN. The Primary Refresh Token sits in the CloudAP plug-in inside LSASS on Windows 11 23H2 today, and replays against Entra ID as the device&apos;s user from any machine that holds the extracted session key.&lt;/p&gt;
&lt;p&gt;Each defense relocated the artefact to a harder-to-reach storage class. The &quot;Do not store LAN Manager hash&quot; policy retired LM. RunAsPPL marked LSASS as a Protected Process Light. Credential Guard moved NT hashes and TGT session keys out of LSASS in VTL0 into the LSAISO trustlet in VTL1. KB5014754 bound certificates to SIDs at issuance, so that a certificate without the SID extension fails strong mapping at the KDC. Token Protection bound PRTs to devices, so that a stolen PRT used against a supported resource from a non-bound device fails.&lt;/p&gt;
&lt;p&gt;Each defense was real. Each closed a generation. The family did not close.&lt;/p&gt;
&lt;p&gt;The reason the family does not close is structural. Every generation finds the next long-term artefact whose storage class the latest defense did not isolate. Pass-the-Hash worked because the NT hash was reachable. Pass-the-Ticket worked because the TGT was reachable. Overpass-the-Hash worked because the NT hash was reachable &lt;em&gt;and&lt;/em&gt; the KDC accepted RC4-HMAC. Pass-the-Certificate worked because certificate templates were misconfigured and the SID extension did not exist. Pass-the-PRT works because CloudAP is in LSASS in VTL0 and Token Protection covers five resources.&lt;/p&gt;
&lt;p&gt;The architectural lower bound -- universal hardware-rooted non-extractable keys plus universal token binding plus universal continuous evaluation -- is the only configuration that closes the family, and it is not deployed anywhere as a complete stack.&lt;/p&gt;
&lt;p&gt;The playbook in the previous section is what to do today. The forecast in section 10 is what to architect for next. The closing observation is the one this article exists to register: when you read about the next named &quot;Pass-the-X&quot; technique, you already know what it will look like. A long-term authentication artefact, reachable from the process that holds it, replayed from a different machine, defeating the latest defense because that defense was designed for a different artefact.&lt;/p&gt;
&lt;p&gt;Generation 6 is already in research literature. The only thing missing is the name.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;pass-the-hash-to-pass-the-prt&quot; keyTerms={[
  { term: &quot;NT hash&quot;, definition: &quot;16-byte MD4 of the user&apos;s password as UTF-16 little-endian; the long-term Windows authentication secret since the early NT releases, unsalted by design.&quot; },
  { term: &quot;NTLM challenge-response&quot;, definition: &quot;Family of Windows authentication protocols (NTLMv1 and NTLMv2) in which the server sends a random challenge and the client returns a keyed cryptographic response computed under a key derived from the user&apos;s password; the password is never transmitted.&quot; },
  { term: &quot;Pass-the-Hash&quot;, definition: &quot;Authenticating with a stolen NT hash by feeding it directly to the protocol&apos;s response-construction function instead of typing a password; Paul Ashton, NTBugtraq, April 1997.&quot; },
  { term: &quot;LSASS&quot;, definition: &quot;Local Security Authority Subsystem Service; the user-mode Windows process that caches in-memory credential material (hashes, tickets, certificate handles, PRT material) for the duration of each logon session.&quot; },
  { term: &quot;Kerberos TGT&quot;, definition: &quot;Ticket Granting Ticket: the long-lived Kerberos credential issued by the KDC&apos;s Authentication Service, encrypted under the krbtgt long-term key, carrying a session key for subsequent service-ticket requests.&quot; },
  { term: &quot;Pass-the-Ticket&quot;, definition: &quot;Extracting a Kerberos TGT (and its session key) from one machine&apos;s LSASS-resident Kerberos cache and injecting it into another machine&apos;s cache.&quot; },
  { term: &quot;Overpass-the-Hash&quot;, definition: &quot;Presenting a stolen NT hash to the KDC as the user&apos;s long-term RC4-HMAC Kerberos key (per RFC 4757) to obtain a real TGT signed by the real krbtgt.&quot; },
  { term: &quot;Credential Guard&quot;, definition: &quot;Windows feature that relocates NT hashes, Kerberos TGT session keys, and &apos;credentials stored by applications as domain credentials&apos; from LSASS in VTL0 to the LSAISO trustlet in VTL1, isolated by the Windows hypervisor.&quot; },
  { term: &quot;LSAISO trustlet&quot;, definition: &quot;The isolated-user-mode LSA process (lsaiso.exe) that holds Credential Guard&apos;s protected credential material in VTL1; unreadable from any VTL0 process or driver.&quot; },
  { term: &quot;PKINIT&quot;, definition: &quot;Kerberos pre-authentication using a certificate&apos;s private key in place of a long-term symmetric key (RFC 4556); the SAN of the certificate maps to the principal whose TGT the KDC will issue.&quot; },
  { term: &quot;Pass-the-Certificate&quot;, definition: &quot;Authenticating to Active Directory with a stolen X.509 certificate&apos;s private key via PKINIT to the KDC or Schannel client-cert authentication to LDAPS.&quot; },
  { term: &quot;szOID_NTDS_CA_SECURITY_EXT&quot;, definition: &quot;X.509 extension introduced by KB5014754 (OID 1.3.6.1.4.1.311.25.2) that carries the requesting principal&apos;s SID at certificate issuance; the basis of KDC strong certificate mapping.&quot; },
  { term: &quot;Primary Refresh Token (PRT)&quot;, definition: &quot;Microsoft Entra-issued long-lived refresh token for SSO on Entra-joined or Hybrid-joined Windows devices; carries a session key (HMAC-SHA256) and binds to a device transport key; default 90-day lifetime with sliding renewal.&quot; },
  { term: &quot;CloudAP&quot;, definition: &quot;Cloud Authentication Provider; the Windows authentication package (cloudap.dll) loaded into LSASS that holds Microsoft Entra credential material including the PRT; not currently inside Credential Guard&apos;s isolation scope.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>active-directory</category><category>kerberos</category><category>credential-theft</category><category>credential-guard</category><category>entra-id</category><category>pass-the-hash</category><category>pass-the-prt</category><category>windows-security</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Who Decided This Token Is Good? A Field Guide to Conditional Access and Entra ID Protection</title><link>https://paragmali.com/blog/who-decided-this-token-is-good-a-field-guide-to-conditional-/</link><guid isPermaLink="true">https://paragmali.com/blog/who-decided-this-token-is-good-a-field-guide-to-conditional-/</guid><description>A wire-level tour of Microsoft Entra Conditional Access, Identity Protection, and Continuous Access Evaluation, plus the five things they cannot do.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Conditional Access is Microsoft&apos;s Zero Trust policy engine, not a feature.** Every interactive sign-in to a licensed Microsoft 365 tenant flows through three planes: a signal plane (Entra ID Protection&apos;s machine-learning risk scoring), a policy plane (Conditional Access&apos;s JSON rule evaluator), and a session plane (Continuous Access Evaluation&apos;s event-driven revocation channel). This article assembles the wire format of all three -- the `riskDetection` resource on Microsoft Graph, the `conditionalAccessPolicy` schema, the `cp1` client capability that opts a client into 28-hour tokens, and the `401 + insufficient_claims` claims challenge -- into one end-to-end picture, then names the five things this architecture fundamentally cannot do.
&lt;h2&gt;1. Who decided this token is good?&lt;/h2&gt;
&lt;p&gt;It is 09:02 on a Tuesday in Lisbon. Alice opens Outlook on a managed laptop in a hotel and the reading pane populates with mail in under a second. She did not type a password. She did not approve a push. She did not touch a hardware key.&lt;/p&gt;
&lt;p&gt;Who decided that was fine?&lt;/p&gt;
&lt;p&gt;The question is harder than it looks. Alice&apos;s password lives in a token cache from yesterday&apos;s sign-in at the office. Outlook&apos;s client silently acquires a fresh access token from Entra. That request may match a Conditional Access policy. The policy may consult an Identity Protection risk score. The result is either an access token or a refusal. Exchange Online receives the token, validates it, and may yet revoke it mid-session because something changed in the last sixty seconds. Bytes return to Alice.&lt;/p&gt;

Microsoft Entra ID&apos;s policy engine for evaluating sign-in attempts. A Conditional Access policy is a JSON object that matches a set of users, cloud apps, and conditions (network location, device state, sign-in risk, user risk, client app, platform) against a set of grants (block, require MFA, require compliant device, require Authentication Strength, and so on). Policies are evaluated after first-factor authentication; a block grant in any matching policy overrides all allow grants [@ms-ca-overview].

The machine-learning signal plane that scores sign-ins and users for risk. ID Protection emits `riskDetection` events tagged with `riskEventType` (anonymized IP, leaked credentials, password spray, atypical travel, and roughly two dozen others), `riskLevel` (low, medium, high), `riskState`, and `detectionTimingType` (realtime, nearRealtime, or offline). Available only on Microsoft Entra ID P2 [@ms-id-protection-overview].

The session plane. CAE is an event-driven channel between Microsoft Entra and CAE-aware resource APIs (Exchange Online, SharePoint Online, Teams, Microsoft Graph). When a critical event fires -- account disabled, password reset, high user risk, network location change -- the resource API returns `HTTP 401` with a `WWW-Authenticate: Bearer error=&quot;insufficient_claims&quot;` challenge. The client replays the embedded claims to Entra and acquires a fresh token. In exchange for this channel, CAE tokens live up to 28 hours [@ms-cae-concept].
&lt;p&gt;Every component in this chain is individually documented on Microsoft Learn. The Conditional Access policy schema is on the Graph reference [@ms-graph-capolicy]. The &lt;code&gt;riskDetection&lt;/code&gt; resource is on the Graph reference too [@ms-graph-riskdetection]. The &lt;code&gt;cp1&lt;/code&gt; client capability is in the claims-challenge document [@ms-claims-challenge]. The &quot;up to 15 minutes&quot; propagation ceiling for CAE non-IP events is in the CAE concept document [@ms-cae-concept].&lt;/p&gt;
&lt;p&gt;But the chain is not assembled anywhere. That is what this article does.&lt;/p&gt;
&lt;p&gt;This article is for the architect or the detection engineer who already knows what a JWT is, what a service principal is, and what an MDM does. If you have ever stared at a Sign-in log entry that reads &quot;Conditional Access: Success&quot; and wondered what &lt;em&gt;exactly&lt;/em&gt; the policy engine concluded, this is for you.&lt;/p&gt;
&lt;p&gt;Three moments of insight are coming. First, why MFA without context fails not because MFA is weak but because the &lt;em&gt;unit&lt;/em&gt; is wrong (Section 3). Second, why the architectural breakthrough was a &lt;em&gt;separation&lt;/em&gt; and not a new algorithm (Section 5). Third, why the system has limits that no engineering will fix (Section 8).&lt;/p&gt;
&lt;p&gt;How did the industry end up with a token-issuance and claims-challenge model? The answer begins in 1975, with a paper that did not mention identity once.&lt;/p&gt;
&lt;h2&gt;2. From perimeter to identity boundary&lt;/h2&gt;
&lt;p&gt;In September 1975, Jerome Saltzer and Michael Schroeder published an eight-principle paper on operating-system protection that nobody at MIT thought of as a paper about cloud identity [@saltzer-schroeder-1975]. Half a century later, two of those eight -- &lt;em&gt;complete mediation&lt;/em&gt; and &lt;em&gt;least privilege&lt;/em&gt; -- are the implicit theorems every Conditional Access policy evaluates against. Where did the industry go in between?&lt;/p&gt;
&lt;h3&gt;Saltzer and Schroeder: the unstated theorems&lt;/h3&gt;
&lt;p&gt;Complete mediation says &quot;every access to every object must be checked for authority.&quot; Least privilege says &quot;every program and every user of the system should operate using the least set of privileges necessary to complete the job.&quot; These are stated as design &lt;em&gt;principles&lt;/em&gt;, not theorems. But they function as theorems for anyone building an access-control system: violate either of them and you have, by construction, a vulnerability. Conditional Access does not derive the principles. It re-states them as a JSON schema and a runtime evaluator.&lt;/p&gt;
&lt;h3&gt;Jericho Forum: the perimeter dissolves&lt;/h3&gt;
&lt;p&gt;In 2003, David Lacey of the Royal Mail and a loose affiliation of corporate CISOs began arguing, against the prevailing castle-and-moat consensus, that the corporate network perimeter could no longer be relied on as the trust boundary. The Jericho Forum formally launched under the Open Group umbrella in January 2004 [@wikipedia-jericho-forum]. They coined the term &quot;de-perimeterisation&quot; to describe what their member firms were already living: data and identity travelling outside the firewall faster than the firewall could be moved.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s own retrospective puts the quote precisely: the Jericho Forum &quot;promoted a new concept of security called de-perimeterisation that focused on how to protect enterprise data flowing in and out of your enterprise network boundary instead of striving to convince users and the business to keep it on the corporate network&quot; [@simos-2020-jericho]. The first sentence of Microsoft Learn&apos;s CA overview today is a direct descendant: &quot;modern security extends beyond an organization&apos;s network perimeter&quot; [@ms-ca-overview].&lt;/p&gt;
&lt;h3&gt;Kindervag: the name&lt;/h3&gt;
&lt;p&gt;John Kindervag, then a principal analyst at Forrester Research, gave the model its marketable name in a September 2010 report titled &quot;No More Chewy Centers: Introducing the Zero Trust Model of Information Security&quot; [@kindervag-2010-zero-trust]. Three tenets: all resources are accessed securely regardless of location; access control is on strict need-to-know and strictly enforced; all traffic is inspected and logged.&lt;/p&gt;
&lt;p&gt;The label stuck. Microsoft Learn now calls CA &quot;Microsoft&apos;s Zero Trust policy engine&quot; in its first sentence [@ms-ca-overview]. The lineage from Kindervag&apos;s 14-page Forrester report to that sentence is direct.&lt;/p&gt;
&lt;p&gt;The original Kindervag PDF is gated behind Forrester&apos;s paywall. The widely cited copy on &lt;code&gt;ndm.net&lt;/code&gt; redirects to an unrelated managed-IT-services company; the only reliably accessible mirror is the Wayback Machine snapshot. Treat the lineage as well documented and the URL as a curiosity of how academic ideas survive the open web.&lt;/p&gt;
&lt;h3&gt;BeyondCorp: the alternative&lt;/h3&gt;
&lt;p&gt;In December 2014, Rory Ward and Betsy Beyer published &quot;BeyondCorp: A New Approach to Enterprise Security&quot; in USENIX &lt;code&gt;;login:&lt;/code&gt; [@ward-beyer-2014-beyondcorp]. The paper described Google&apos;s internal Zero Trust deployment: every request authenticated and authorized by an access proxy, no implicit network trust, device inventory and user identity as the inputs to access decisions. A follow-up in 2016 documented the production rollout [@osborn-2016-beyondcorp].&lt;/p&gt;
&lt;p&gt;This is the architectural fork Section 7 returns to. BeyondCorp puts the policy engine in the data path, as a reverse proxy that sees every HTTP request. CA puts the policy engine at &lt;em&gt;token issuance&lt;/em&gt; and re-evaluates via &lt;em&gt;claims challenges&lt;/em&gt;. Both work. They are not interchangeable.&lt;/p&gt;
&lt;h3&gt;NIST SP 800-207: the vocabulary&lt;/h3&gt;
&lt;p&gt;In August 2020, NIST published Special Publication 800-207, &lt;em&gt;Zero Trust Architecture&lt;/em&gt; [@nist-sp-800-207-2020]. It codified the U.S. federal reference architecture: a Policy Engine that decides, a Policy Administrator that effects the decision, and a Policy Enforcement Point that intercepts the access.&lt;/p&gt;
&lt;p&gt;That trio is the vocabulary the Microsoft Learn CA documentation now uses. In the SP 800-207 mapping, Conditional Access is the Policy Engine and Policy Administrator; Exchange Online, SharePoint Online, Teams, and Microsoft Graph are the Policy Enforcement Points; Entra ID Protection is the trust algorithm that feeds the Policy Engine.&lt;/p&gt;

If you ever have to map Conditional Access to SP 800-207 for a compliance review, the cleanest correspondences are: PE = the CA evaluator inside Entra; PA = Entra&apos;s token issuer (because the decision is effected by issuing or refusing a token); PEP = the resource API (Exchange, SharePoint, Graph) that validates the token, plus, for CAE-aware resources, the same API enforcing claims-challenge revocation mid-session. ID Protection is the &quot;trust algorithm&quot; input to the PE.
&lt;p&gt;The doctrine was settled by 2020. But Microsoft had already been trying to build a perimeter on identity for six years, starting in 2014 with a much smaller idea.&lt;/p&gt;
&lt;h2&gt;3. Per-user MFA and the limits of binary controls&lt;/h2&gt;
&lt;p&gt;In 2014, Microsoft&apos;s only cloud-era access control was a per-user toggle that said &lt;em&gt;MFA: yes&lt;/em&gt; or &lt;em&gt;MFA: no&lt;/em&gt;. The toggle worked. It was a real improvement over passwords alone. It also produced the most exploited security failure of the next decade: MFA fatigue [@weinert-2023-managed-policies].&lt;/p&gt;
&lt;p&gt;How does a control improve security &lt;em&gt;and&lt;/em&gt; create a new attack class at the same time?&lt;/p&gt;
&lt;h3&gt;The per-user MFA state machine&lt;/h3&gt;
&lt;p&gt;Per-user MFA lives on the user object as a tri-state: &lt;code&gt;Disabled&lt;/code&gt;, &lt;code&gt;Enabled&lt;/code&gt;, or &lt;code&gt;Enforced&lt;/code&gt;. Microsoft Learn now says the quiet part out loud: &quot;The best way to protect users with Microsoft Entra MFA is to create a Conditional Access policy&quot; and &quot;Don&apos;t enable or enforce per-user Microsoft Entra multifactor authentication if you use Conditional Access policies&quot; [@ms-howto-mfa-userstates]. That guidance carries a generation of operational pain inside it. Mixing the two surfaces, in practice, produces unpredictable prompts: a CA policy says &quot;no MFA required for this location,&quot; the per-user state says &quot;always MFA,&quot; and the user gets prompted twice.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft&apos;s explicit guidance is to pick one surface. If you have Entra ID P1 or higher, use Conditional Access. The per-user state should remain &lt;code&gt;Disabled&lt;/code&gt; for those accounts. Mixed configurations produce both false-positive prompts and, occasionally, false-negative skips [@ms-howto-mfa-userstates].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Trusted IP rules: one-dimensional context&lt;/h3&gt;
&lt;p&gt;Office 365 added a second knob in the same era: &quot;trusted IPs.&quot; Sign-ins from a configured public IP range would skip the MFA challenge [@ms-ca-network]. The idea was that &quot;on the corporate network&quot; meant &quot;more trustworthy.&quot; This was reasonable in 2014. By 2017, it was already eroded by full-tunnel VPNs (every employee egresses through the corporate /16 from home), split-tunnel VPNs (some traffic does, some does not), and the realisation that &quot;corporate network&quot; had stopped being a useful synonym for &quot;trusted.&quot; Trusted IP is one-dimensional context, and one dimension was not enough.&lt;/p&gt;
&lt;h3&gt;Security Defaults: the Free-SKU descendant&lt;/h3&gt;
&lt;p&gt;Since 22 October 2019, every new Entra ID tenant has Security Defaults turned on by default at creation [@ms-security-defaults]. Security Defaults is a tenant-wide on/off switch that requires MFA for all admin roles, MFA for users when they show risk, blocks legacy authentication, and forces MFA registration. Microsoft&apos;s number on the impact is striking: &quot;more than 99.9% of those common identity-related attacks are stopped by using multifactor authentication and blocking legacy authentication&quot; [@ms-security-defaults].&lt;/p&gt;
&lt;p&gt;For Entra ID Free tenants in 2026, Security Defaults is still the only available baseline. There is no per-app policy, no per-risk gating, no Conditional Access. This is the licensing reality Section 10 returns to.&lt;/p&gt;
&lt;p&gt;Active Directory Federation Services -- AD FS -- is the on-prem federation product that ran the access-control story before any of this. It is still operational in many tenants. It is no longer Microsoft&apos;s strategic identity provider; the Microsoft Learn AD FS overview now opens with the explicit guidance &quot;Instead of upgrading to the latest version of AD FS, Microsoft highly recommends migrating to Microsoft Entra ID&quot; [@ms-ad-fs-overview]. AD FS claim rules functioned as a kind of policy engine, but they evaluated only at federation time and they had no concept of risk.&lt;/p&gt;
&lt;h3&gt;The four failure modes of the binary toggle&lt;/h3&gt;
&lt;p&gt;The first-generation controls -- per-user MFA, trusted IPs, Security Defaults -- share four documented limits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No expression of context.&lt;/strong&gt; The toggle is either on or off. It cannot say &quot;MFA from a new country but not from the office.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trusted IP is thin context.&lt;/strong&gt; A public IP range is one bit of information; modern attacks include matching network egress.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No per-app policy.&lt;/strong&gt; The toggle applies to all apps the user accesses. You cannot say &quot;MFA for the admin portal, not for Outlook.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No exclusion semantics for break-glass accounts.&lt;/strong&gt; Emergency-access accounts need to be reachable when everything else has failed. The binary toggle either includes them or excludes them; it does not let you say &quot;exclude these accounts but log every sign-in as a high-priority alert.&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;MFA fatigue: when a control becomes a credential&lt;/h3&gt;
&lt;p&gt;The canonical failure of the binary toggle is push-bombing. The attacker has the password. The system requires MFA. The user gets four &quot;approve sign-in?&quot; notifications during a morning meeting. One gets a thumbs-up by reflex. The system did exactly what it was configured to do.&lt;/p&gt;
&lt;p&gt;The attack works because the control has no concept of &lt;em&gt;whether this is a normal sign-in&lt;/em&gt;. The same flow runs whether the request originates from the user&apos;s office WiFi or an anonymizing proxy in another country. The MFA challenge carries no risk-weighted information; the user has no signal that this prompt is different from yesterday&apos;s prompt. Fatigue is the consequence. Microsoft&apos;s own Entra blog catalogued the attack pattern and the operational mitigations in the wake of the 2022 incident cluster [@ms-techcom-mfa-fatigue].&lt;/p&gt;

Focusing on password rules, rather than things that can really help -- like multi-factor authentication (MFA), or great threat detection -- is just a distraction. -- Alex Weinert, Microsoft Identity, July 2019 [@weinert-2019-password]
&lt;p&gt;Weinert&apos;s 2019 piece is now infamous in the identity community for its title alone -- &quot;Your Pa$$word doesn&apos;t matter.&quot; The argument was that a password&apos;s composition rules carry no information that helps the system tell a real user from an attacker; what does carry information is &lt;em&gt;context&lt;/em&gt;. The system needed a place to put that context.&lt;/p&gt;
&lt;p&gt;If &lt;em&gt;MFA yes/no&lt;/em&gt; cannot express context, the next step is obvious: make context the input. But to make context the input, the system needs a place to &lt;em&gt;put&lt;/em&gt; it. The history of CA from 2015 forward is the history of giving context a home.&lt;/p&gt;
&lt;h2&gt;4. Generation by generation&lt;/h2&gt;
&lt;p&gt;The next eight years produced six generations of access control, each one closing a specific failure of the previous one. They look like product launches in a marketing chronology. They are something more interesting: a sequence of negative results, each followed by a positive engineering response.&lt;/p&gt;

timeline
    title Conditional Access timeline
    2014 : Gen 1 per-user MFA and trusted IPs
    2015 : CA enters public preview
    2016 : Gen 2 Conditional Access general availability
    2016 : ID Protection enters preview
    2018 : Gen 3 risk-based CA conditions broadly available
    2020 : CAE enters preview
    2022 : Gen 4 Continuous Access Evaluation general availability
    2023 : Gen 5 CA for workload identities
    2023 : Gen 6 Microsoft-managed policies and Authentication Strengths
    2026 : CA for AI agent identities
&lt;p&gt;The 2026 milestone -- Conditional Access for AI agent identities -- is itself still emerging; Microsoft&apos;s current framing in the Conditional Access Optimization Agent announcement names it explicitly as a frontier rather than a finished generation [@ms-techcom-ca-optimization-agent]. Section 9.1 returns to the open problems.&lt;/p&gt;
&lt;h3&gt;Gen 1 (2014 to 2016): per-user MFA&lt;/h3&gt;
&lt;p&gt;Documented in Section 3. The control has no concept of context. The failure motivates Gen 2.&lt;/p&gt;
&lt;h3&gt;Gen 2 (September 2016 GA): Conditional Access with static rules&lt;/h3&gt;
&lt;p&gt;The September 27, 2016 CloudBlogs post announcing CA general availability framed it as &quot;Protect your data at the front door&quot; -- the &quot;front door&quot; framing that Microsoft documentation still uses [@ms-techcom-ca-frontdoor-2016]. The policy schema (users + cloud apps + conditions to grants) was introduced in the 2015 preview [@ms-techcom-ca-preview-2015] and survived essentially unchanged into 2016 GA.&lt;/p&gt;
&lt;p&gt;Gen 2 closed Gen 1&apos;s failure mode: context now had a home. A policy could match on network location, on the app being accessed, on the user&apos;s group membership, on the device platform. It could express &quot;block country X&quot; or &quot;require MFA when not on the corporate network.&quot;&lt;/p&gt;
&lt;p&gt;The remaining documented limit: no risk feed. The engine could express &lt;em&gt;what to check for&lt;/em&gt; but not &lt;em&gt;whether this specific sign-in looks suspicious&lt;/em&gt;. A policy could block credential-stuffing attempts only if you happened to know in advance which IPs to deny. Motivated Gen 3.&lt;/p&gt;
&lt;h3&gt;Gen 3 (2017 to 2018): risk-based fusion&lt;/h3&gt;
&lt;p&gt;Identity Protection had been generating risk signals since its March 2016 preview. Through 2017 and 2018, two new condition keys appeared in the CA policy schema: &lt;code&gt;signInRiskLevels&lt;/code&gt; and &lt;code&gt;userRiskLevels&lt;/code&gt;. Both take values from the set &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;. The risk feed plugged into the policy plane through exactly two keys. The legacy ID-Protection-side risk policies (which were a parallel policy surface inside ID Protection itself) are now retiring on 1 October 2026; the canonical surface is CA [@ms-id-protection-policies].&lt;/p&gt;
&lt;p&gt;The remaining limit: pre-issuance only. The CA evaluator runs at sign-in time. Once a token is issued, the policy plane has no way to undo the decision until the token expires. Microsoft&apos;s own retrospective is honest about what they tried first: &quot;Microsoft experimented with the &apos;blunt object&apos; approach of reduced token lifetimes but found they degrade user experiences and reliability without eliminating risks&quot; [@ms-cae-concept]. A one-hour token cuts the worst-case revocation latency to an hour, but it also means a user with intermittent connectivity gets prompted every hour, and a mobile app with retry storms can hammer the IdP. The trade-off was unacceptable. Motivated Gen 4.&lt;/p&gt;
&lt;h3&gt;Gen 4 (January 2022 GA): Continuous Access Evaluation&lt;/h3&gt;
&lt;p&gt;CAE inverted the trade-off. Instead of shortening the token, lengthen it -- up to 28 hours [@ms-cae-concept]. Then add a side channel: when a critical event fires (account disabled, password reset, high user risk, IP location change), the resource API issues an &lt;code&gt;HTTP 401&lt;/code&gt; with a &lt;code&gt;WWW-Authenticate&lt;/code&gt; claims challenge, and the client replays to Entra for a fresh token. Latency on the side channel is bounded: &quot;up to 15 minutes&quot; for non-IP events, &quot;instant&quot; for IP locations [@ms-cae-concept]. CAE was tied to an emerging open standard from day one, the OpenID Continuous Access Evaluation Profile [@ms-cae-concept]. The general-availability announcement landed on 10 January 2022 [@ms-techcom-cae-ga-2022].&lt;/p&gt;
&lt;p&gt;Remaining limit: applies to humans only. Service principals do not consume CAE-aware client libraries; they cannot perform a claims challenge. Motivated Gen 5.&lt;/p&gt;
&lt;h3&gt;Gen 5 (2023 GA): Conditional Access for workload identities&lt;/h3&gt;
&lt;p&gt;Same engine, constrained grant set. The Microsoft Learn page is blunt on the boundaries: &quot;Workload Identities Premium licenses are required&quot; and the constraint set is unusual -- &quot;Policy can be applied to single tenant service principals that are registered in your tenant. Microsoft and third-party SaaS applications, including multitenant apps, are not covered by these policies. Managed identities aren&apos;t covered by policy&quot; and &quot;Under Grant, Block access is the only available option&quot; [@ms-workload-identity-ca]. The public preview of CA filters for workload identities opened on 26 October 2022 [@vansurksum-2022-workload-ca]; the Microsoft Entra Workload Identities standalone product followed in late November 2022, and the Conditional Access feature for workload identities itself reached general availability later in 2023.&lt;/p&gt;
&lt;p&gt;The single-tenant restriction is a structural choice. Multi-tenant SaaS apps appear in many tenants&apos; service principal directories at once; policy scoping on them would require a cross-tenant resolution protocol the engine does not have. Managed identities are excluded because they belong to Azure subscriptions, not to user identity, and Microsoft has chosen not to extend the surface there. Group assignments do not work either: &quot;Conditional Access policies assigned to a group that contains a service principal are not enforced for that service principal&quot; [@ms-workload-identity-ca].&lt;/p&gt;
&lt;p&gt;Remaining limit: under-configured in most tenants because the grant taxonomy is so narrow that admins do not see immediate value. Motivated Gen 6.&lt;/p&gt;
&lt;h3&gt;Gen 6 (November 2023 onwards): Microsoft-managed policies and Authentication Strengths&lt;/h3&gt;
&lt;p&gt;In November 2023, Alex Weinert announced Microsoft-managed Conditional Access policies: a set of baselines that Microsoft would auto-deploy into tenants in Report-only mode and then auto-enable after a waiting period [@weinert-2023-managed-policies]. The launch announcement specified a 90-day window [@helpnet-2023-microsoft-entra-policies]. The current Microsoft Learn documentation specifies &quot;Microsoft enables these policies no less than 45 days after they&apos;re introduced in your tenant if they&apos;re left in the Report-only state&quot; with a 28-day pre-enablement notification [@ms-managed-policies].&lt;/p&gt;
&lt;p&gt;The window shrank deliberately. The 90-day window in the 2023 launch announcement was a calibration window; the 45-day window in current documentation is the post-calibration setting. Both numbers are correct in their respective time frames. The article uses the current number throughout.&lt;/p&gt;
&lt;p&gt;Parallel to the managed policies, Microsoft shipped &lt;em&gt;Authentication Strengths&lt;/em&gt; -- a named bundle of acceptable authentication methods that can be required as a grant. The three built-in strengths are &lt;em&gt;MFA strength&lt;/em&gt;, &lt;em&gt;Passwordless MFA strength&lt;/em&gt;, and &lt;em&gt;Phishing-resistant MFA strength&lt;/em&gt; (FIDO2 security key, &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;, multifactor certificate-based authentication) [@ms-auth-strengths]. The phishing-resistant strength is the modern way to express &quot;no adversary-in-the-middle phishing kit should be able to defeat this grant.&quot;&lt;/p&gt;
&lt;h3&gt;The pattern: extension, not replacement&lt;/h3&gt;
&lt;p&gt;From Gen 3 onward, each generation &lt;em&gt;extends&lt;/em&gt; the prior schema rather than replacing it. The &lt;code&gt;conditionalAccessPolicy&lt;/code&gt; JSON shape that shipped in 2016 still drives the engine in 2026 -- with new condition keys added, new grant types added, new session controls added. By the standards of cloud control surfaces, that is a long run without a rewrite.&lt;/p&gt;
&lt;p&gt;The reason is the architectural decision the next section is about.&lt;/p&gt;
&lt;h2&gt;5. The two-plane separation&lt;/h2&gt;
&lt;p&gt;The breakthrough is not a model, not a token format, not a wire protocol. It is a &lt;em&gt;separation&lt;/em&gt;: the &lt;strong&gt;signal plane&lt;/strong&gt; that produces risk detections from the &lt;strong&gt;policy plane&lt;/strong&gt; that consumes them.&lt;/p&gt;
&lt;p&gt;Stated like that, it sounds banal. Read it the other direction -- a policy engine whose risk model can change without changing the policy semantics, and whose policy can change without retraining the model -- and it is the design that makes the system maintainable at trillions of daily signals across hundreds of thousands of tenants.&lt;/p&gt;
&lt;h3&gt;The two planes, precisely&lt;/h3&gt;
&lt;p&gt;The signal plane is Microsoft Entra ID Protection. It runs detection logic on every interactive sign-in (and, for offline detections, on historical sign-ins) and emits a &lt;code&gt;riskDetection&lt;/code&gt; resource into a per-tenant log on Microsoft Graph at &lt;code&gt;/identityProtection/riskDetections&lt;/code&gt;. Each detection carries five fields you care about: &lt;code&gt;riskEventType&lt;/code&gt; (one of about two dozen named detection types like &lt;code&gt;anonymizedIPAddress&lt;/code&gt;, &lt;code&gt;leakedCredentials&lt;/code&gt;, &lt;code&gt;unlikelyTravel&lt;/code&gt;), &lt;code&gt;riskLevel&lt;/code&gt; (&lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, plus the bookkeeping values &lt;code&gt;hidden&lt;/code&gt; and &lt;code&gt;none&lt;/code&gt;), &lt;code&gt;riskState&lt;/code&gt; (&lt;code&gt;atRisk&lt;/code&gt;, &lt;code&gt;confirmedCompromised&lt;/code&gt;, &lt;code&gt;dismissed&lt;/code&gt;, &lt;code&gt;remediated&lt;/code&gt;), &lt;code&gt;detectionTimingType&lt;/code&gt; (&lt;code&gt;realtime&lt;/code&gt;, &lt;code&gt;nearRealtime&lt;/code&gt;, &lt;code&gt;offline&lt;/code&gt;), and &lt;code&gt;additionalInfo&lt;/code&gt; (a JSON blob with user-agent, IP, alert URL, reason codes) [@ms-graph-riskdetection][@ms-id-protection-risks].&lt;/p&gt;
&lt;p&gt;The policy plane is Conditional Access. It is a JSON object at &lt;code&gt;/identity/conditionalAccess/policies/{id}&lt;/code&gt; on the Graph API [@ms-graph-capolicy]. Each policy has &lt;code&gt;displayName&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt; (&lt;code&gt;enabled&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;, &lt;code&gt;enabledForReportingButNotEnforced&lt;/code&gt;), &lt;code&gt;conditions&lt;/code&gt;, &lt;code&gt;grantControls&lt;/code&gt;, and &lt;code&gt;sessionControls&lt;/code&gt;. The conditions block contains the per-policy targeting: which users, which apps, which platforms, which network locations -- and two condition keys named &lt;code&gt;signInRiskLevels&lt;/code&gt; and &lt;code&gt;userRiskLevels&lt;/code&gt;.&lt;/p&gt;

**Sign-in risk** is a per-sign-in probability that the credential being used is being used by someone other than the legitimate owner *at this moment*. **User risk** is a per-user probability that the account itself has been compromised over its recent history. A user with leaked credentials in a breach corpus carries persistent user risk until the password is reset; a user signing in from an anonymizing proxy carries sign-in risk for that session. CA policies can match on either, both, or neither. Risk-based conditions require Entra ID P2 [@ms-id-protection-policies].
&lt;p&gt;Those two condition keys -- &lt;code&gt;signInRiskLevels&lt;/code&gt; and &lt;code&gt;userRiskLevels&lt;/code&gt; -- are the entire API surface between the signal plane and the policy plane. Everything else about ID Protection is hidden behind them. The policy plane does not know whether &lt;code&gt;high&lt;/code&gt; came from a transformer or a logistic regression or a hardcoded rule. The signal plane does not know which policies will read its output. The contract is two strings.&lt;/p&gt;

flowchart LR
    subgraph SP[Signal plane Entra ID Protection]
        DET[Detection pipeline]
        RD[(riskDetection log)]
        RL[Risk level low medium high]
    end
    subgraph PP[Policy plane Conditional Access]
        EV[Policy evaluator]
        POL[(conditionalAccessPolicy JSON)]
        TOK[Token issuer]
    end
    subgraph SES[Session plane CAE]
        CH[Critical event channel]
        RP[Resource API]
    end
    DET --&amp;gt; RD
    DET --&amp;gt; RL
    RL -. signInRiskLevels userRiskLevels .-&amp;gt; EV
    POL --&amp;gt; EV
    EV --&amp;gt; TOK
    TOK -- access token --&amp;gt; RP
    DET -. user risk events .-&amp;gt; CH
    CH -. 401 insufficient claims .-&amp;gt; RP
&lt;h3&gt;Why the separation matters&lt;/h3&gt;
&lt;p&gt;Three concrete consequences fall out of the design:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The risk model is re-trainable without policy rewrites.&lt;/strong&gt; Microsoft&apos;s ID Protection team can change the underlying detection algorithm tomorrow. Add a new &lt;code&gt;riskEventType&lt;/code&gt;. Replace the classifier for &lt;code&gt;unlikelyTravel&lt;/code&gt;. Re-tune the threshold that maps a score to &lt;code&gt;low&lt;/code&gt;/&lt;code&gt;medium&lt;/code&gt;/&lt;code&gt;high&lt;/code&gt;. None of these require tenants to rewrite their CA policies, because policies match on the &lt;em&gt;level&lt;/em&gt;, not the &lt;em&gt;signal&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tenants without the licence simply do not use the risk conditions.&lt;/strong&gt; An Entra ID P1 tenant can deploy CA policies that match on users, apps, locations, devices, client apps, and platforms. P2 unlocks the risk conditions. The schema accommodates both: P1 policies just leave the risk arrays empty. There is no parallel policy surface for the non-risk-aware tenants; they use the same engine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CAE is a third plane layered onto the same skeleton.&lt;/strong&gt; Continuous Access Evaluation did not require redesign of the policy plane. The CAE channel is a new &lt;em&gt;event delivery&lt;/em&gt; mechanism; the events it propagates are things the signal plane already knew about (high user risk, password reset, account disabled) plus new ones the policy plane introduced (network-location-policy changed). The architecture absorbed CAE because the design was already a separation of concerns.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The signal plane and the policy plane are separable; the contract between them is &lt;em&gt;two condition keys&lt;/em&gt; (&lt;code&gt;signInRiskLevels&lt;/code&gt; and &lt;code&gt;userRiskLevels&lt;/code&gt;). That is what makes the system maintainable across a decade of evolution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The &quot;pit of success&quot; framing&lt;/h3&gt;
&lt;p&gt;Alex Weinert calls this the &quot;pit of success.&quot; His November 2023 piece on Microsoft-managed policies put the metric on it: a decade ago Microsoft turned on a &quot;radical&quot; tenant-wide policy requiring MFA for every consumer Microsoft account, and &quot;today, 100 percent of consumer Microsoft accounts older than 60 days have multifactor authentication&quot; [@weinert-2023-managed-policies].&lt;/p&gt;
&lt;p&gt;The 100 percent number is achievable because the policy plane and the signal plane can each evolve independently. Microsoft can ship a managed policy that says &quot;require MFA for high-risk sign-ins&quot; without committing to a fixed definition of &quot;high risk.&quot; The definition lives on the signal plane and changes weekly. The policy lives on the policy plane and is stable for years.&lt;/p&gt;
&lt;p&gt;With the separation as the spine, the next section walks the end-to-end pipeline in one continuous trace, from signal to grant to token to session, on a real sign-in -- the trace no public Microsoft document assembles in one place.&lt;/p&gt;
&lt;h2&gt;6. The end-to-end pipeline&lt;/h2&gt;
&lt;p&gt;Take Alice&apos;s Tuesday morning from Section 1 and walk it forward. This section has six subsections. By the end of them, the question &quot;who decided?&quot; has six independently sourced answers and one combined picture.&lt;/p&gt;
&lt;h3&gt;6.1 What the signal plane sees&lt;/h3&gt;
&lt;p&gt;Identity Protection&apos;s detection taxonomy splits into five rough groups, based on what kind of information triggered the detection. The canonical taxonomy is the Microsoft Learn page on risk types [@ms-id-protection-risks]; the wire-format enum on the Graph schema is at [@ms-graph-riskdetection].&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Network signals.&lt;/em&gt; &lt;code&gt;anonymizedIPAddress&lt;/code&gt;, &lt;code&gt;maliciousIPAddress&lt;/code&gt;, &lt;code&gt;nationStateIP&lt;/code&gt;, &lt;code&gt;riskyIPAddress&lt;/code&gt;. The signal is the source IP and reputation databases that ID Protection ingests.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Behavioural signals.&lt;/em&gt; &lt;code&gt;unlikelyTravel&lt;/code&gt;, &lt;code&gt;mcasImpossibleTravel&lt;/code&gt;, &lt;code&gt;newCountry&lt;/code&gt;, &lt;code&gt;unfamiliarFeatures&lt;/code&gt;, &lt;code&gt;anomalousUserActivity&lt;/code&gt;. The signal is a deviation from the tenant&apos;s or the user&apos;s historical baseline.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Credential signals.&lt;/em&gt; &lt;code&gt;leakedCredentials&lt;/code&gt;, &lt;code&gt;passwordSpray&lt;/code&gt;. The signal is a match against a corpus of breached credentials or a velocity-based pattern across tenants.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Token and session signals.&lt;/em&gt; &lt;code&gt;anomalousToken&lt;/code&gt;, &lt;code&gt;tokenIssuerAnomaly&lt;/code&gt;, &lt;code&gt;attemptedPrtAccess&lt;/code&gt;, &lt;code&gt;attackerinTheMiddle&lt;/code&gt;, &lt;code&gt;authenticatorPhishing&lt;/code&gt;. The signal is on the token itself or on the way the authenticator flow ran.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Inbox behaviour.&lt;/em&gt; &lt;code&gt;suspiciousInboxForwarding&lt;/code&gt;, &lt;code&gt;mcasSuspiciousInboxManipulationRules&lt;/code&gt;. The signal is on what happened &lt;em&gt;after&lt;/em&gt; the sign-in -- a post-compromise indicator that retroactively flags the sign-in that enabled it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each detection is also tagged with a timing: real-time, near-real-time, or offline. Microsoft Learn is precise about the latencies: &quot;Detections triggered in real-time take 5-10 minutes to surface details in the reports. Offline detections take up to 48 hours&quot; [@ms-risk-detection-types].&lt;/p&gt;
&lt;p&gt;The detection is mapped to a risk &lt;em&gt;level&lt;/em&gt;, not a probability. Microsoft Learn calls the level &quot;calculated by our machine learning algorithms&quot; and explicitly notes the meaning: low/medium/high &quot;represent how confident Microsoft is that one or more of the user&apos;s credentials are known by an unauthorized entity&quot; [@ms-risk-detection-types].&quot;Confidence&quot; here is meant in the everyday sense, not the strict statistical sense of a confidence interval. Microsoft has not published a calibration study that would let you map a &quot;high&quot; risk level to a frequentist probability of compromise.&lt;/p&gt;
&lt;p&gt;The figure you sometimes see in Microsoft marketing materials -- &quot;more than 100 trillion signals processed per day&quot; [@ms-managed-policies], or, in older sources, &quot;78 trillion&quot; [@ms-id-protection-overview] -- is the &lt;em&gt;aggregate signal volume across all tenants and product surfaces&lt;/em&gt;, not per-sign-in features per user. The article keeps the two carefully separate.&lt;/p&gt;
&lt;p&gt;Microsoft has not publicly disclosed the production model architecture, the feature vector size, or per-detection precision and recall. The 2021 Microsoft Security Blog interview with Maria Puertas Calvo describes the existence of the ML team and the operational scale (&quot;hundreds of terabytes every day&quot;) but stops well short of architecture details [@ms-puertas-calvo-interview]. The model class is publicly unspecified; the taxonomy and the operating output are both public.&lt;/p&gt;
&lt;h3&gt;6.2 How risk surfaces&lt;/h3&gt;
&lt;p&gt;Two parallel logs matter for risk. The Sign-in log is the universe: every interactive and non-interactive sign-in produces an entry. The &lt;code&gt;riskDetections&lt;/code&gt; log is the &lt;em&gt;sparse overlay&lt;/em&gt;: a &lt;code&gt;riskDetection&lt;/code&gt; is emitted only when a detection fires for the sign-in. Most sign-ins produce a Sign-in log entry with no corresponding &lt;code&gt;riskDetection&lt;/code&gt;. Only flagged sign-ins do [@ms-graph-riskdetection].&lt;/p&gt;
&lt;p&gt;This is a common source of confusion. It is tempting to assume &quot;ID Protection scored every sign-in,&quot; and in a sense it did -- the detectors ran -- but the &lt;em&gt;durable artefact&lt;/em&gt; exists only when at least one detector fired. To compute a per-sign-in distribution of risk you need to &lt;em&gt;join&lt;/em&gt; the Sign-in log with the riskDetections log and treat the unjoined rows as &quot;no risk flagged at the moment of issuance.&quot;&lt;/p&gt;
&lt;p&gt;There is one more wrinkle. The detection taxonomy on the Microsoft Learn concept page and the &lt;code&gt;riskEventType&lt;/code&gt; enum on the Graph schema are not perfectly aligned. The concept page lists &lt;code&gt;mcasImpossibleTravel&lt;/code&gt; and &lt;code&gt;authenticatorPhishing&lt;/code&gt; as named detection types; the Graph enum lists &lt;code&gt;impossibleTravel&lt;/code&gt; (without the &lt;code&gt;mcas&lt;/code&gt; prefix). The two surfaces sometimes use different value names for the same logical detection -- a UI display string versus a Graph enum value. Detection engineers writing KQL against the Sign-in logs should account for both.&lt;/p&gt;
&lt;h3&gt;6.3 How CA consumes risk&lt;/h3&gt;
&lt;p&gt;Conditional Access evaluation runs in a fixed order: assignments are checked first (does this sign-in match this policy at all?), then conditions (do all the condition predicates hold?), then grants (which controls are demanded?), then session controls (which token lifetime, sign-in frequency, persistent browser).&lt;/p&gt;
&lt;p&gt;The key semantic, repeated across the Microsoft Learn documentation: a &lt;em&gt;block&lt;/em&gt; grant in any policy matching the sign-in overrides any allow grant in any other policy. The policy plane is not just additive; it has an explicit precedence rule.&lt;/p&gt;

flowchart TD
    A[Sign-in request] --&amp;gt; B[First-factor auth]
    B --&amp;gt; C[Enumerate matching policies]
    C --&amp;gt; D{Any policy matches?}
    D -- No --&amp;gt; E[Default allow with token]
    D -- Yes --&amp;gt; F[Evaluate conditions per policy]
    F --&amp;gt; G{Block grant in any match?}
    G -- Yes --&amp;gt; H[Deny access return error]
    G -- No --&amp;gt; I[Aggregate required grants]
    I --&amp;gt; J{All grants satisfied?}
    J -- No --&amp;gt; K[Issue challenge MFA or device]
    J -- Yes --&amp;gt; L[Apply session controls]
    L --&amp;gt; M[Issue access token]
&lt;p&gt;The pseudocode below is a compressed restatement of that flow. It is not Microsoft source code; it is the algorithmic shape an admin should keep in their head when reading a policy or debugging a sign-in.&lt;/p&gt;
&lt;p&gt;{`
function evaluate(signin) {
  const matching = allPolicies.filter(p =&amp;gt;
    p.state !== &apos;disabled&apos; &amp;amp;&amp;amp;
    matchesAssignments(p.conditions, signin) &amp;amp;&amp;amp;
    matchesConditions(p.conditions, signin)
  );&lt;/p&gt;
&lt;p&gt;  // Block precedence: any block grant wins
  if (matching.some(p =&amp;gt; p.grantControls.builtInControls.includes(&apos;block&apos;))) {
    return { decision: &apos;DENY&apos;, reason: &apos;block grant matched&apos; };
  }&lt;/p&gt;
&lt;p&gt;  // Aggregate required grants across matching policies
  const requiredGrants = new Set();
  for (const p of matching) {
    for (const g of p.grantControls.builtInControls) requiredGrants.add(g);
    if (p.grantControls.authenticationStrength) {
      requiredGrants.add(&apos;authStrength:&apos; + p.grantControls.authenticationStrength.id);
    }
  }&lt;/p&gt;
&lt;p&gt;  const satisfied = [...requiredGrants].every(g =&amp;gt; signin.satisfies(g));
  if (!satisfied) {
    return { decision: &apos;CHALLENGE&apos;, missing: [...requiredGrants].filter(g =&amp;gt; !signin.satisfies(g)) };
  }&lt;/p&gt;
&lt;p&gt;  // Apply session controls (token lifetime, sign-in frequency, persistent browser)
  const session = mergeSessionControls(matching.map(p =&amp;gt; p.sessionControls));
  return { decision: &apos;ALLOW&apos;, session };
}&lt;/p&gt;
&lt;p&gt;const result = evaluate({
  user: &apos;&lt;a href=&quot;mailto:alice@contoso.com&quot; rel=&quot;noopener&quot;&gt;alice@contoso.com&lt;/a&gt;&apos;,
  app: &apos;Office365 Exchange Online&apos;,
  location: { ip: &apos;203.0.113.42&apos;, country: &apos;PT&apos; },
  device: { compliant: true, joinType: &apos;Entra&apos; },
  signInRisk: &apos;low&apos;,
  userRisk: &apos;none&apos;,
  satisfies(grant) {
    const mfa = [&apos;mfa&apos;, &apos;authStrength:phishingResistantMfa&apos;];
    return mfa.includes(grant) || grant === &apos;compliantDevice&apos;;
  },
});
console.log(JSON.stringify(result, null, 2));
`}&lt;/p&gt;
&lt;p&gt;Risk-based conditions require Entra ID P2 [@ms-id-protection-overview]. Without that licence, the &lt;code&gt;signInRiskLevels&lt;/code&gt; and &lt;code&gt;userRiskLevels&lt;/code&gt; arrays in a policy are ignored. The rest of the engine works the same.&lt;/p&gt;
&lt;h3&gt;6.4 The grants&lt;/h3&gt;
&lt;p&gt;Each policy declares a set of grants. The grants are &lt;em&gt;additive within a policy&lt;/em&gt; (all required to satisfy the policy) but the &lt;em&gt;block grant in any matching policy&lt;/em&gt; takes precedence over allow grants in any other policy. Here are the grants currently in the schema:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Grant&lt;/th&gt;
&lt;th&gt;What it requires&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;block&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deny access.&lt;/td&gt;
&lt;td&gt;Always wins against allow grants.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mfa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any MFA method registered for the user.&lt;/td&gt;
&lt;td&gt;The legacy generic-MFA grant; replaced in modern deployments by Authentication Strength.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;requireAuthenticationStrength&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A named bundle of acceptable methods.&lt;/td&gt;
&lt;td&gt;The modern grant. Built-in strengths include phishing-resistant [@ms-auth-strengths].&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compliantDevice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The device record has &lt;code&gt;isCompliant: true&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Set by Intune or a third-party compliance partner.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;domainJoinedDevice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hybrid Azure AD joined device.&lt;/td&gt;
&lt;td&gt;Requires Entra Connect on-prem trust.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;approvedApplication&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use an approved client app.&lt;/td&gt;
&lt;td&gt;A small allow-list of Microsoft mobile apps.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compliantApplication&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An app under an Intune App Protection Policy.&lt;/td&gt;
&lt;td&gt;Mobile app management.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;passwordChange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User must change their password.&lt;/td&gt;
&lt;td&gt;Used for password-leaked recovery.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;requireTermsOfUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User must accept a terms-of-use document.&lt;/td&gt;
&lt;td&gt;Used for compliance and guest scenarios.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

A named, ordered bundle of acceptable authentication methods that a CA grant can demand. The three built-in strengths are *MFA strength* (any registered second factor), *Passwordless MFA strength* (no password used), and *Phishing-resistant MFA strength* (FIDO2 security key, Windows Hello for Business or a platform credential, or multifactor certificate-based authentication) [@ms-auth-strengths]. The phishing-resistant strength is the canonical modern grant for high-value access.
&lt;p&gt;The Authentication Strength grant is where the phishing-resistance story lives in 2026. A policy that demands the phishing-resistant strength refuses to accept TOTP or SMS or push as the second factor. Only credentials with cryptographic binding to the device or hardware token will satisfy the grant. That class of credential, by construction, cannot be replayed by an adversary-in-the-middle phishing kit -- because the underlying &lt;a href=&quot;https://paragmali.com/blog/webauthn-and-passkeys-on-windows-from-ctap-to-the-credential/&quot; rel=&quot;noopener&quot;&gt;WebAuthn&lt;/a&gt; ceremony is bound to the origin of the relying party.&lt;/p&gt;
&lt;h3&gt;6.5 The Windows-side handoff&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://paragmali.com/blog/inside-the-primary-refresh-token-the-cryptographic-seam-betw/&quot; rel=&quot;noopener&quot;&gt;PRT&lt;/a&gt; issuance is an interactive sign-in. It goes through CA like any other.&lt;/p&gt;

A long-lived refresh token issued to a Windows session at user sign-in to Entra-joined or hybrid-Entra-joined devices. The PRT is bound to the device&apos;s TPM where one is available, and it grants the user single sign-on to all CA-targeted apps from that Windows session. Issuance is subject to CA evaluation; if a CA policy demands compliant device, the device must already be marked `isCompliant` before the PRT is issued.
&lt;p&gt;The compliance state lands on the device object as &lt;code&gt;isCompliant&lt;/code&gt;. Intune (or a third-party MDM through Intune&apos;s compliance-partner API) writes that field after evaluating the device against a compliance policy: disk encrypted, OS patched, antivirus running, jailbreak detection clean, and so on. CA reads it on subsequent policy evaluations. If a policy requires &lt;code&gt;compliantDevice&lt;/code&gt; and the device object says &lt;code&gt;isCompliant: false&lt;/code&gt;, the grant is not satisfied.&lt;/p&gt;
&lt;p&gt;The operational seam to on-prem Active Directory runs the other direction. &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; and &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; against on-prem domain controllers never consult Entra. The Microsoft Learn CA overview is explicit: CA is a &lt;em&gt;cloud control plane&lt;/em&gt;; on-prem authentication is outside its scope [@ms-ca-overview]. This is the limit Section 8 will name precisely.&lt;/p&gt;
&lt;h3&gt;6.6 CAE in session&lt;/h3&gt;
&lt;p&gt;The third plane. Wire format lives in two Microsoft Learn pages: the claims-challenge page [@ms-claims-challenge] and the app-resilience CAE page [@ms-app-resilience-cae].&lt;/p&gt;
&lt;p&gt;A client opts in to CAE by advertising the &lt;code&gt;cp1&lt;/code&gt; capability via the &lt;code&gt;xms_cc&lt;/code&gt; claim in token requests. In MSAL, that opt-in looks like &lt;code&gt;WithClientCapabilities(new[] { &quot;cp1&quot; })&lt;/code&gt; [@ms-app-resilience-cae]. The Microsoft Learn claims-challenge page says it cleanly: &quot;The only currently known value is &lt;code&gt;cp1&lt;/code&gt;&quot; [@ms-claims-challenge].&lt;/p&gt;
&lt;p&gt;When the policy plane sees a critical event after the token was issued, the resource API responds to the next call with &lt;code&gt;HTTP 401 Unauthorized&lt;/code&gt; and a &lt;code&gt;WWW-Authenticate&lt;/code&gt; header of the shape:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer authorization_uri=&quot;&amp;lt;entra-authorize-endpoint&amp;gt;&quot;, error=&quot;insufficient_claims&quot;, claims=&quot;&amp;lt;base64-encoded JSON&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;claims&lt;/code&gt; value is a base64-encoded JSON object that the client passes verbatim to the token endpoint when acquiring a fresh token [@ms-claims-challenge][@ms-app-resilience-cae]. The IdP evaluates the embedded claims, runs CA again with the new context, and issues a new token (or refuses).&lt;/p&gt;

The HTTP wire format CAE uses to revoke a session mid-flight. A CAE-aware resource API returns `HTTP 401` with `WWW-Authenticate: Bearer error=&quot;insufficient_claims&quot;, claims=&quot;&quot;`. The client replays the base64 blob to Entra; Entra re-runs CA with the new context; the client receives a fresh token or a definitive refusal. The wire format is documented at [@ms-claims-challenge] and demonstrated at [@ms-app-resilience-cae].
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The CAE-aware capability is signalled by the &lt;em&gt;client&lt;/em&gt;, not by the &lt;em&gt;token&lt;/em&gt;. The client advertises &lt;code&gt;cp1&lt;/code&gt; via &lt;code&gt;xms_cc&lt;/code&gt;; the token&apos;s CAE-awareness shows up as its lifetime (up to 28 hours) and the resource API&apos;s willingness to issue a claims challenge. Folk knowledge that says &quot;look for a &lt;code&gt;cae&lt;/code&gt; claim in the JWT&quot; is incorrect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Microsoft Learn CAE document enumerates five critical events: account disabled or deleted, password change or reset, MFA enabled by an administrator, administrator token revocation, and high user risk detected by ID Protection [@ms-cae-concept]. A parallel pathway, &lt;em&gt;Conditional Access policy evaluation&lt;/em&gt;, propagates network-location and policy changes to CAE-aware resource providers on the same channel. For IP-location changes the latency is &quot;instant&quot;; for everything else the ceiling is up to 15 minutes [@ms-cae-concept].&lt;/p&gt;

sequenceDiagram
    participant C as Client app
    participant R as Resource API CAE aware
    participant E as Entra token issuer
    participant P as ID Protection
    Note over C: Client holds long-lived CAE token
    C-&amp;gt;&amp;gt;R: GET messages with bearer token
    R-&amp;gt;&amp;gt;R: Token still cryptographically valid
    P-&amp;gt;&amp;gt;E: High user risk event for Alice
    E-&amp;gt;&amp;gt;R: Push critical event Alice high risk
    C-&amp;gt;&amp;gt;R: GET messages with bearer token again
    R-&amp;gt;&amp;gt;C: 401 WWW-Authenticate insufficient_claims claims base64
    C-&amp;gt;&amp;gt;E: Token request with claims blob and cp1 capability
    E-&amp;gt;&amp;gt;E: Re-run CA with new context
    E--&amp;gt;&amp;gt;C: New token or definitive refusal
    C-&amp;gt;&amp;gt;R: Retry with new token
&lt;p&gt;{`
// Simplified MSAL.js-shaped pseudocode for CAE opt-in and challenge handling
const ENTRA_AUTHORITY = &apos;&apos;;
const EXCHANGE_ENDPOINT = &apos;&apos;;
const MAIL_READ_SCOPE = &apos;&apos;;&lt;/p&gt;
&lt;p&gt;const msal = new PublicClientApplication({
  auth: { clientId: &apos;&apos;, authority: ENTRA_AUTHORITY },
});&lt;/p&gt;
&lt;p&gt;async function callExchange() {
  let token = await msal.acquireTokenSilent({
    scopes: [MAIL_READ_SCOPE],
    clientCapabilities: [&apos;cp1&apos;], // advertise CAE awareness
  });&lt;/p&gt;
&lt;p&gt;  let res = await fetch(EXCHANGE_ENDPOINT, {
    headers: { Authorization: &apos;Bearer &apos; + token.accessToken },
  });&lt;/p&gt;
&lt;p&gt;  if (res.status === 401) {
    const header = res.headers.get(&apos;WWW-Authenticate&apos;) || &apos;&apos;;
    const m = /claims=&quot;([^&quot;]+)&quot;/.exec(header);
    if (m) {
      // Replay the embedded claims to acquire a fresh token
      token = await msal.acquireTokenSilent({
        scopes: [MAIL_READ_SCOPE],
        claims: Buffer.from(m[1], &apos;base64&apos;).toString(&apos;utf8&apos;),
        clientCapabilities: [&apos;cp1&apos;],
      });
      res = await fetch(EXCHANGE_ENDPOINT, {
        headers: { Authorization: &apos;Bearer &apos; + token.accessToken },
      });
    }
  }&lt;/p&gt;
&lt;p&gt;  console.log(&apos;HTTP&apos;, res.status);
}&lt;/p&gt;
&lt;p&gt;callExchange();
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; CAE inverts the conventional trade-off: lengthen the token, shorten the revocation. The token can live 28 hours because revocation is an event, not a clock.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The chain is now visible. The signal plane scored Alice&apos;s Tuesday sign-in. The policy plane evaluated the policies. The token issuer issued an access token (CAE-aware because Outlook advertises &lt;code&gt;cp1&lt;/code&gt;). Exchange Online accepted the token and returned mail. If, twelve minutes from now, Alice&apos;s account is flagged high risk because a different sign-in attempt fires &lt;code&gt;leakedCredentials&lt;/code&gt;, the critical event will fire, Exchange will issue a claims challenge, and Outlook will either acquire a fresh token (passing the new CA evaluation) or surface the refusal to the user.&lt;/p&gt;
&lt;p&gt;Six independent components co-decided on one access event. Microsoft is one vendor. The same problem has been solved differently by Google, Okta, AWS, Cloudflare, and Zscaler. The Microsoft answer is not the only correct answer.&lt;/p&gt;
&lt;h2&gt;7. How others do it&lt;/h2&gt;
&lt;p&gt;Microsoft chose to enforce at &lt;em&gt;token issuance and claims challenge&lt;/em&gt;. Google chose to enforce at &lt;em&gt;every HTTP request via a reverse proxy&lt;/em&gt;. AWS chose a decidable policy DSL. These are not minor variations; they are different answers to &quot;where does the policy engine live in the data path?&quot;&lt;/p&gt;
&lt;p&gt;Both Microsoft&apos;s and Google&apos;s models scale. Neither is strictly better. The choice is a function of what the enterprise already runs.&lt;/p&gt;
&lt;h3&gt;Google BeyondCorp, IAP, Chrome Enterprise Premium&lt;/h3&gt;
&lt;p&gt;Google&apos;s Identity-Aware Proxy puts the policy engine in the data path. The documentation calls it bluntly: &quot;IAP lets you establish a central authorization layer for applications accessed by HTTPS, so you can use an application-level access control model instead of relying on network-level firewalls&quot; [@google-iap]. Every HTTP request to an IAP-protected app passes through the proxy. The proxy authenticates the user (via Google Account, Workforce Identity Federation, or Identity Platform), evaluates a Common Expression Language policy against the request context, and -- on allow -- forwards the request to the backend with signed identity headers.&lt;/p&gt;
&lt;p&gt;The BeyondCorp Enterprise product (recently rebranded as Chrome Enterprise Premium) layers context-aware access on top: device posture, geographic location, time of day [@google-bce-overview]. The architecture matches the 2014 USENIX paper [@ward-beyer-2014-beyondcorp] and the 2016 production follow-up [@osborn-2016-beyondcorp].&lt;/p&gt;
&lt;p&gt;The strength is per-request authorization: every HTTP call is its own decision point. The weakness, from the M365 perspective, is that IAP does not gate Microsoft 365 first-party API traffic. The Outlook client does not route through Google&apos;s IAP; it routes through Entra and Exchange Online. For Microsoft 365 workloads, IAP is complementary at best.&lt;/p&gt;
&lt;h3&gt;Okta Identity Engine and ThreatInsight&lt;/h3&gt;
&lt;p&gt;Okta&apos;s policy engine is closer to Microsoft&apos;s structurally: the identity provider is the policy engine, app sign-on policies live on the IdP, and the resource side relies on the IdP&apos;s token rather than a per-request proxy. The Okta Identity Engine documents the rule shape: &quot;App sign-in policies define how a user must authenticate to gain access to an app. They verify ... group membership, the IP zone they&apos;re signing in from, risk level, and others&quot; [@okta-sign-on-policies]. Every new app gets a default policy with a single catch-all rule that allows access with two factors.&lt;/p&gt;
&lt;p&gt;Okta ThreatInsight is the IP-reputation feed. The documentation describes it operationally: &quot;Okta ThreatInsight aggregates data about sign-in activity across the Okta customer base to analyze and detect potentially malicious IP addresses ... password spraying, credential stuffing, brute-force cryptographic attacks&quot; [@okta-threatinsight]. The signal coverage is narrower than ID Protection: ThreatInsight is IP-centric, where ID Protection runs a multi-detection ML pipeline on tokens, sessions, behaviour, and credentials.&lt;/p&gt;
&lt;h3&gt;AWS IAM Identity Center and Verified Access&lt;/h3&gt;
&lt;p&gt;AWS splits the problem. IAM Identity Center handles workforce SSO and trusted identity propagation to AWS services [@aws-iam-identity-center]. AWS Verified Access handles per-request authorization for HTTPS-fronted apps -- the ZTNA piece. The Verified Access docs put it plainly: &quot;Verified Access evaluates each application access request in real time&quot; and &quot;verifies the trustworthiness of users and devices against a set of security requirements&quot; [@aws-verified-access].&lt;/p&gt;
&lt;p&gt;The interesting bit is the policy language: Cedar. Cedar is a deliberately decidable language for authorization policy. &quot;Decidable&quot; here is a precise term: the safety question (will some policy edit, in some future edit chain, leak this right?) is answerable by a static analyser for any Cedar policy [@cedar-security].&lt;/p&gt;
&lt;p&gt;Cedar&apos;s intentional non-Turing-completeness is the language-design hedge against the Harrison-Ruzzo-Ullman undecidability result the next section will name. The trade-off is expressiveness: Cedar cannot express arbitrary computational predicates, which is the price of being analysable [@cedar-security].&lt;/p&gt;
&lt;h3&gt;Cloudflare Access and Zscaler Private Access&lt;/h3&gt;
&lt;p&gt;Cloudflare Access is an edge proxy. Policies are deny-by-default, with four building blocks: Actions (Allow, Block, Bypass, Service Auth), Rule types (Include, Require, Exclude), Selectors, and Values [@cloudflare-access-policies]. The deny-by-default semantics are explicit: &quot;Since Access is deny by default, users who do not match a Block policy will still be denied access unless they explicitly match an Allow policy&quot; [@cloudflare-access-policies]. Cloudflare also ships a policy tester that lets administrators dry-run a policy against the existing user population [@cloudflare-access-policy-mgmt].&lt;/p&gt;
&lt;p&gt;Zscaler Private Access is a broker-based ZTNA: the user connects to a Zscaler edge node, the broker establishes a connection to the private app, and &quot;users never access the corporate network, and apps are never exposed to the public internet&quot; [@zscaler-zpa]. Zscaler&apos;s own marketing surveys put the VPN-replacement framing in numbers: &quot;91% of organizations are concerned that VPNs compromise their security&quot; and &quot;56% of organizations suffered one or more VPN-related attacks in 2023-2024&quot; [@zscaler-zpa].&lt;/p&gt;
&lt;p&gt;Architecturally, Cloudflare Access and ZPA both sit closer to BeyondCorp than to Microsoft CA: the policy engine is in the data path; the protected resource is fronted by the proxy rather than gated at token issuance.&lt;/p&gt;
&lt;h3&gt;OpenID Shared Signals Framework and CAEP&lt;/h3&gt;
&lt;p&gt;Not a competitor: the &lt;em&gt;cross-vendor wire format&lt;/em&gt; for what Microsoft built into CAE. On 22 September 2025, the OpenID Foundation approved three Final Specifications: the Shared Signals Framework 1.0, the Continuous Access Evaluation Profile 1.0, and the Risk Incident Sharing and Coordination Profile 1.0 [@helpnet-2025-openid][@openid-caep-final]. CAEP defines five event types -- Session Revoked, Token Claims Change, Credential Change, Assurance Level Change, Device Compliance Change -- as the cross-vendor revocation vocabulary.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s CAE implementation is, in Microsoft&apos;s own words, &quot;an industry standard based on Open ID Continuous Access Evaluation Profile&quot; [@ms-cae-concept]. The Final Specifications from September 2025 are the canonical post-2025 reference; older drafts at OpenID&apos;s site are superseded.&lt;/p&gt;
&lt;h3&gt;Head-to-head comparison&lt;/h3&gt;
&lt;p&gt;The differences worth memorising:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Enforcement point&lt;/th&gt;
&lt;th&gt;Native risk feed&lt;/th&gt;
&lt;th&gt;Post-issuance revocation&lt;/th&gt;
&lt;th&gt;Gates M365 first-party?&lt;/th&gt;
&lt;th&gt;Best suited for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Microsoft Entra CA + ID Protection + CAE&lt;/td&gt;
&lt;td&gt;Token issuer + CAE-aware resource APIs&lt;/td&gt;
&lt;td&gt;ID Protection ML pipeline&lt;/td&gt;
&lt;td&gt;CAE up to 15 min, instant for IP&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;M365 tenants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google IAP / Chrome Enterprise Premium&lt;/td&gt;
&lt;td&gt;HTTPS reverse proxy&lt;/td&gt;
&lt;td&gt;Context-aware access signals&lt;/td&gt;
&lt;td&gt;Per-request (always re-decides)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Google Cloud workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Okta Identity Engine + ThreatInsight&lt;/td&gt;
&lt;td&gt;IdP token issuance&lt;/td&gt;
&lt;td&gt;ThreatInsight IP feed&lt;/td&gt;
&lt;td&gt;Limited, IdP-dependent&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Vendor-neutral front door&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS IAM Identity Center + Verified Access&lt;/td&gt;
&lt;td&gt;Verified Access proxy + IAM&lt;/td&gt;
&lt;td&gt;Trust providers (third-party)&lt;/td&gt;
&lt;td&gt;Per-request for Verified Access&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;AWS-hosted apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Access&lt;/td&gt;
&lt;td&gt;Edge proxy&lt;/td&gt;
&lt;td&gt;Risk score + identity factors&lt;/td&gt;
&lt;td&gt;Per-request&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Public web apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zscaler Private Access&lt;/td&gt;
&lt;td&gt;Broker / edge node&lt;/td&gt;
&lt;td&gt;Posture + identity&lt;/td&gt;
&lt;td&gt;Per-request&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Private app access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Per-cell sourcing for the table: the Microsoft row&apos;s &quot;Yes&quot; cell on M365 first-party gating is the directly-stated claim from the Microsoft Learn CA overview [@ms-ca-overview]. The other rows&apos; &quot;No&quot; cells are &lt;em&gt;negative inferences&lt;/em&gt; drawn from each peer&apos;s own product documentation, none of which advertises Microsoft 365 first-party API gating: Google IAP gates HTTPS-fronted apps behind the proxy [@google-iap]; Cloudflare Access deny-by-default applies to the apps fronted by Cloudflare [@cloudflare-access-policies]; Verified Access &quot;evaluates each application access request&quot; for HTTPS apps behind AWS [@aws-verified-access]; Zscaler ZPA brokers private app access [@zscaler-zpa]; Okta sign-on policies gate apps wired into Okta&apos;s IdP [@okta-sign-on-policies]. The cell semantics are &quot;does the system gate Outlook/Teams/SharePoint/Graph first-party traffic&quot; and the answer is structurally No outside Microsoft.&lt;/p&gt;

flowchart LR
    subgraph TOK[Token issuance model Microsoft Okta]
        U1[User] --&amp;gt; AT[Acquire token]
        AT --&amp;gt; CA1[CA evaluator]
        CA1 --&amp;gt; IS[Issue token]
        IS --&amp;gt; R1[Resource API validates token]
        R1 -. CAE 401 .-&amp;gt; AT
    end
    subgraph PRX[Data path proxy model Google BeyondCorp AWS Verified Access Cloudflare Zscaler]
        U2[User] --&amp;gt; PXY[Proxy intercepts every request]
        PXY --&amp;gt; POL[Policy evaluator at the proxy]
        POL --&amp;gt; BCK[Backend application]
    end
&lt;p&gt;The honest observation worth sitting with: none of the proxy systems gates M365 first-party API traffic. Outlook, Teams, SharePoint, and Microsoft Graph route through Entra. For those workloads, Entra remains the only effective policy plane. The proxy systems gate &lt;em&gt;the apps that sit behind the proxy&lt;/em&gt; -- internal apps, partner-facing apps, custom workloads. That makes BeyondCorp, Okta, Cloudflare Access, and ZPA &lt;em&gt;complementary to&lt;/em&gt; Entra CA in an M365 environment, not substitutes for it.&lt;/p&gt;
&lt;p&gt;Six systems, six architectural choices. None of them wrong. But what do they &lt;em&gt;all&lt;/em&gt; leave on the table?&lt;/p&gt;
&lt;h2&gt;8. What Conditional Access fundamentally cannot do&lt;/h2&gt;
&lt;p&gt;Section 7 cannot be the ending. There are at least five things Conditional Access -- and every peer in Section 7 -- &lt;em&gt;cannot&lt;/em&gt; do. Some are engineering limits; some are theorems. Both classes are worth naming.&lt;/p&gt;
&lt;h3&gt;(a) On-prem authentication&lt;/h3&gt;
&lt;p&gt;CA is a cloud control plane. Kerberos and NTLM against on-prem domain controllers do not consult Entra. There is no policy hook for the legacy Windows protocols. If a domain user signs in to a domain-joined workstation, authenticates to a file server, and accesses a share, no piece of that flow touches Conditional Access. The Microsoft Learn overview is explicit about the scope [@ms-ca-overview].&lt;/p&gt;
&lt;p&gt;This is the operational seam between cloud identity and on-prem identity. State it plainly; do not soften.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Conditional Access does not gate Kerberos or NTLM against on-prem domain controllers. If your threat model includes lateral movement after credential theft on the on-prem side, CA is not your defence. Layer in Defender for Identity, on-prem MFA gateways, or a privileged-access workstation architecture instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;(b) Post-issuance token theft&lt;/h3&gt;
&lt;p&gt;Once a refresh token is exfiltrated -- whether via an adversary-in-the-middle phishing kit like Evilginx [@ms-aitm-phishing-blog], an infostealer that scrapes the token cache, or a malicious browser extension -- the pre-issuance CA evaluation is bypassed. The attacker has a bearer token. They can present it to the resource API directly. CAE-aware resource providers can revoke mid-session on the published critical-event list, but the latency ceiling is &quot;up to 15 minutes&quot; for non-IP events [@ms-cae-concept]. In fifteen minutes a competent attacker has done plenty.&lt;/p&gt;
&lt;p&gt;The mitigation is &lt;em&gt;device-bound&lt;/em&gt; credentials: Primary Refresh Tokens bound to &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; hardware, FIDO2 with hardware attestation, certificate-based authentication with hardware-protected keys [@ms-prt-concept]. A bearer token bound to a TPM is not exfiltratable in the same way; the wrapped key material never leaves the device.&lt;/p&gt;
&lt;h3&gt;(c) Consent-grant phishing&lt;/h3&gt;
&lt;p&gt;CA evaluates &lt;em&gt;authentication&lt;/em&gt;, not &lt;em&gt;authorization grants&lt;/em&gt; that a user makes to a malicious OAuth app. A user who clicks &quot;Allow&quot; on a permissions-consent prompt for an attacker-controlled app has performed an OAuth authorization, not a sign-in. The malicious app now has the user&apos;s delegated permissions for whatever scopes were granted. CA was not invoked because CA gates the user&apos;s sign-ins; it does not inspect the user&apos;s OAuth grants. Microsoft Defender for Cloud Apps documents the attack class as &quot;risky OAuth apps&quot; and ships investigation and remediation tooling on a separate plane from CA [@ms-illicit-consent-grant].&lt;/p&gt;
&lt;p&gt;Admin consent settings, app governance policies, and explicit allow-listing of acceptable publishers live on that different plane. The policy admin who deploys CA needs to deploy app governance separately.&lt;/p&gt;
&lt;h3&gt;(d) Risk evaluation is probabilistic&lt;/h3&gt;
&lt;p&gt;Identity Protection produces a &lt;em&gt;score&lt;/em&gt;, not a &lt;em&gt;proof&lt;/em&gt;. A &quot;high&quot; risk level is a confidence; it is not the assertion &quot;this sign-in is definitely an attack.&quot; No vendor in the Section 7 survey publishes precision or recall numbers for its risk engine. The operating point -- the threshold that maps a continuous score to discrete buckets -- is a trade-off that the vendor calibrates and the customer does not see.&lt;/p&gt;
&lt;p&gt;This is a &lt;em&gt;structural&lt;/em&gt; lower bound on any ML-driven risk plane, not a Microsoft-specific failure. Any classifier has false positives and false negatives. A risk-aware CA policy that says &quot;block at high risk&quot; will, with non-zero probability, block a legitimate sign-in. A policy that says &quot;require MFA at medium risk&quot; will, with non-zero probability, let through a sophisticated attacker whose detections fall under the threshold.&lt;/p&gt;
&lt;h3&gt;(e) Workload-identity CA is constrained by design&lt;/h3&gt;
&lt;p&gt;Block-only grants. No managed identities. No group assignments. The full human grant taxonomy does not transfer because a service principal cannot perform an MFA challenge, cannot register a FIDO2 key, cannot accept a terms-of-use document. The Microsoft Learn page on workload-identity CA enumerates the constraints precisely [@ms-workload-identity-ca]. Section 9 will name this as an &lt;em&gt;open&lt;/em&gt; problem; for now, treat it as a documented limit.&lt;/p&gt;
&lt;h3&gt;The theorems behind the limits&lt;/h3&gt;
&lt;p&gt;Some of these limits are engineering choices that could be different in a future product. Some are deeper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Saltzer and Schroeder 1975&lt;/strong&gt; [@saltzer-schroeder-1975] give the upper bound on aspirations: complete mediation across every authentication and authorization decision &lt;em&gt;within scope of mediation&lt;/em&gt;. The principle does not constrain what is in scope. It constrains what you must do for whatever you have decided is in scope. On-prem AD is out of scope for CA by Microsoft&apos;s product decision; complete mediation cannot fix that, because the principle is about consistency &lt;em&gt;within&lt;/em&gt; the boundary, not about expanding the boundary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Harrison-Ruzzo-Ullman 1976&lt;/strong&gt; -- usually shortened to HRU [@harrison-ruzzo-ullman-1976] -- gives the lower bound on static analysis. The safety question in the general access-matrix model is &lt;em&gt;undecidable&lt;/em&gt;. In informal terms: there is no general algorithm that proves a Conditional Access policy edit cannot, under some future edit chain, leak a sensitive right. This is why every vendor in the survey relies on &lt;em&gt;evaluation-time&lt;/em&gt; mediation (the engine decides at the moment of the request) rather than &lt;em&gt;static-proof&lt;/em&gt; analysis (the engine certifies in advance that no edit can ever leak). Cedar&apos;s intentional restriction to a decidable fragment, in AWS Verified Access, is the counter-strategy: trade expressiveness for analysability.&lt;/p&gt;
&lt;p&gt;The bearer-token revocation trade-off is informal but real: the worst-case revocation latency is bounded below by the token&apos;s natural lifetime, unless a side channel exists. CAE is that side channel. Its latency is bounded by the propagation time of the channel (up to 15 minutes for non-IP events, instant for IP). Shorten the channel further and you discover that the IdP-to-resource-API event delivery has its own infrastructure costs.&lt;/p&gt;

The practical implication of HRU for a CA admin is that there is no tool, anywhere, that can examine your tenant&apos;s CA policies and certify that no sequence of policy edits could ever leak access to a sensitive resource. Vendors offer policy *testers* that simulate a single edit against the current population; that is decidable. The question &quot;is the system safe under all possible future edits?&quot; is not. This is why audit trails, change-control gates, and least-privilege role assignments on the CA admin role matter as much as the CA policies themselves.
&lt;p&gt;Naming the limits clears the way to name the &lt;em&gt;active&lt;/em&gt; unsolved problems -- the ones the field is still working on, where the current state of the art admits it is partial.&lt;/p&gt;
&lt;h2&gt;9. Where the policy plane is still incomplete&lt;/h2&gt;
&lt;p&gt;Microsoft&apos;s own 2026 documentation for Conditional Access on AI agents calls the current implementation &quot;a lightweight enforcement mechanism designed to block unauthorized or risky agents, not a full policy suite.&quot; That is not marketing modesty. It is an admission that the most active frontier of policy enforcement -- &lt;a href=&quot;https://paragmali.com/blog/agentic-identity-on-windows-when-the-process-acting-on-your-/&quot; rel=&quot;noopener&quot;&gt;agent identities&lt;/a&gt; -- is deliberately under-specified.&lt;/p&gt;
&lt;p&gt;Five open problems sit on that frontier in 2026.&lt;/p&gt;

Organizations are expanding Zero Trust across more users, applications, and now a growing population of AI agent identities ... the Conditional Access Optimization Agent moves beyond static guidance to continuous, context-aware identity posture optimization. [@ms-techcom-ca-optimization-agent]
&lt;h3&gt;9.1 Agent identity policy semantics&lt;/h3&gt;
&lt;p&gt;What grants should exist for AI agents beyond block and allow? Useful candidate grants include: &quot;read-but-not-move&quot; for mail or files; &quot;business-hours-only&quot;; &quot;any autonomous action requires a fresh sign-off from the on-behalf-of human.&quot; None of these exist as first-class CA grant types in 2026.&lt;/p&gt;
&lt;p&gt;What does exist: CA targeting of agent identities -- the ability to &lt;em&gt;match&lt;/em&gt; a policy on the agent identity rather than the human -- and the Conditional Access Optimization Agent, which gives administrators continuous recommendations on policy posture [@ms-techcom-ca-optimization-agent]. The targeting is there. The grant taxonomy is still mostly the human one, applied imperfectly.&lt;/p&gt;
&lt;h3&gt;9.2 Cross-vendor CAEP interop&lt;/h3&gt;
&lt;p&gt;The wire format was finalised in September 2025 [@helpnet-2025-openid][@openid-caep-final]. Production receiver coverage outside Microsoft Entra-internal resource providers is partial. Two large vendors agreeing on an event schema is necessary but not sufficient for cross-vendor revocation to work in practice; the receiving side needs to &lt;em&gt;act&lt;/em&gt; on the events. The next eighteen months are the period in which CAEP either becomes the cross-vendor wire format for revocation, or it does not.&lt;/p&gt;
&lt;h3&gt;9.3 Workload-identity grant set&lt;/h3&gt;
&lt;p&gt;What richer expressions could exist for non-human identities? The current Microsoft Learn page lists workload-identity detections: &lt;code&gt;investigationsThreatIntelligence&lt;/code&gt;, &lt;code&gt;suspiciousSignins&lt;/code&gt;, &lt;code&gt;adminConfirmedServicePrincipalCompromised&lt;/code&gt;, &lt;code&gt;leakedCredentials&lt;/code&gt;, &lt;code&gt;maliciousApplication&lt;/code&gt;, &lt;code&gt;suspiciousApplication&lt;/code&gt;, &lt;code&gt;anomalousServicePrincipalActivity&lt;/code&gt;, &lt;code&gt;suspiciousAPITraffic&lt;/code&gt; [@ms-workload-identity-risk]. The detections exist; the grant taxonomy stops at block.&lt;/p&gt;
&lt;p&gt;Candidate richer grants: &quot;workload attestation&quot; (the service principal proves it is running on attested infrastructure), &quot;verifiable claim from a trusted attester&quot; (a third party signs a statement about the workload), &quot;step-up authorization for sensitive scopes&quot; (a higher-privilege scope requires a separate per-request authorization step). None of these is generally available in 2026.&lt;/p&gt;

A non-human identity in Entra ID: a service principal, an application registration&apos;s owned service principal, or a managed identity in Azure. Workload identities authenticate via client secrets, client certificates, federated credentials, or (for managed identities) instance-metadata-service tokens. Conditional Access for workload identities currently applies only to single-tenant service principals registered in the tenant; it does not cover multi-tenant SaaS apps or managed identities [@ms-workload-identity-ca].
&lt;h3&gt;9.4 The break-glass paradox&lt;/h3&gt;
&lt;p&gt;Emergency-access accounts must be excluded from CA. If a CA misconfiguration locks out every admin, the break-glass account is the recovery path. But exclusion creates a high-value bypass: an attacker who compromises a break-glass account inherits its exclusion.&lt;/p&gt;
&lt;p&gt;There is no clean answer. Microsoft&apos;s guidance is exclusion plus FIDO2 binding plus alerting: the break-glass accounts have hardware-bound FIDO2 keys (so they cannot be phished), they are excluded from all CA policies (so misconfiguration cannot lock them out), and &lt;em&gt;every&lt;/em&gt; sign-in is alerted on (so misuse is detected within minutes) [@ms-emergency-access].&lt;/p&gt;

Run two break-glass accounts, not one. Store the FIDO2 keys in separate physical safes under separate custodians. Never use them for anything but a recovery exercise once per quarter; if they sign in unexpectedly, treat the alert as a P1 incident. The operational pattern accepts that you have a bypass and treats the bypass as the highest-value alert in the tenant [@ms-emergency-access].
&lt;h3&gt;9.5 The risk-engine transparency problem&lt;/h3&gt;
&lt;p&gt;No vendor in the Section 7 survey publishes model architecture, feature vector size, or per-detection precision and recall. Microsoft does not. Okta does not. Google does not. Defenders, auditors, and regulators must accept a black-box score.&lt;/p&gt;
&lt;p&gt;This matters in three places. First, for incident response: when an &quot;atypical travel&quot; detection fires for an executive, the responder cannot see which features contributed and how strongly. Second, for compliance: an auditor asked to evidence the effectiveness of the control plane gets the operating output (3-tier risk levels) but not a quantitative evaluation. Third, for the risk-engine vendors themselves, who must respond to legitimate regulatory questions about model bias and operational reliability without revealing the architecture that attackers would use to evade detection.&lt;/p&gt;
&lt;p&gt;The article does not predict a resolution. It names the gap.&lt;/p&gt;
&lt;p&gt;The architecture is incomplete by admission. It is also actionable today. A competent tenant administrator can deploy a sensible baseline in an afternoon.&lt;/p&gt;
&lt;h2&gt;10. Using Conditional Access today&lt;/h2&gt;
&lt;p&gt;The architectural story ends; the operational story begins. Here is what a competent tenant looks like in 2026.&lt;/p&gt;
&lt;h3&gt;The licensing reality&lt;/h3&gt;
&lt;p&gt;Conditional Access is not a feature every Microsoft 365 tenant gets. It is a feature gated by SKU. The licensing tiers are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Entra ID Free.&lt;/strong&gt; Security Defaults only [@ms-security-defaults]. No Conditional Access policies. No risk-based conditions. No CA-driven CAE (the critical-event-evaluation subsystem -- for events like account disable, password reset, and high user risk -- still propagates to CAE-aware M365 services at the service layer regardless of SKU; see Section 6.6) [@ms-cae-concept].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entra ID P1.&lt;/strong&gt; Conditional Access is unlocked [@ms-ca-overview]. You can author policies with any of the non-risk conditions: users, apps, locations, devices, client app, platform. You can demand any of the non-risk grants.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Entra ID P2.&lt;/strong&gt; Adds risk-based conditions. &lt;code&gt;signInRiskLevels&lt;/code&gt; and &lt;code&gt;userRiskLevels&lt;/code&gt; become usable [@ms-id-protection-overview]. ID Protection&apos;s full report pane (risky users, risky sign-ins, risk detections) is accessible. The legacy ID-Protection-side risk policies retire 1 October 2026 [@ms-id-protection-policies].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workload Identities Premium.&lt;/strong&gt; A separate SKU. Unlocks CA scoped to service principals [@ms-workload-identity-ca].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This corrects a premise discarded earlier: &quot;Conditional Access is the policy plane every M365 tenant runs on&quot; is &lt;em&gt;not&lt;/em&gt; true. Many tenants run on Security Defaults. The &quot;policy plane every tenant runs on&quot; is the cloud sign-in pipeline; CA is the configurable richer layer that P1+ tenants opt into.&lt;/p&gt;
&lt;h3&gt;Start with the managed baselines&lt;/h3&gt;
&lt;p&gt;Microsoft-managed Conditional Access policies are the recommended starting point [@ms-managed-policies]. They auto-deploy in Report-only mode, run for at least 45 days while administrators review the impact in the Sign-in logs, and are auto-enabled with a 28-day pre-enablement notification unless administrators opt out [@ms-managed-policies]. The currently shipping baselines, per Microsoft Learn, include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MFA for admins accessing Microsoft admin portals (the most-privileged roles).&lt;/li&gt;
&lt;li&gt;MFA for users who already have per-user MFA enabled (a migration aid).&lt;/li&gt;
&lt;li&gt;MFA and reauthentication for risky sign-ins (the P2 baseline).&lt;/li&gt;
&lt;li&gt;Block legacy authentication.&lt;/li&gt;
&lt;li&gt;Block access for high-risk users (P2-tier protection on the user-risk surface).&lt;/li&gt;
&lt;li&gt;Block all high-risk agents accessing all resources (Preview, AI-agent surface).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The original announcement called for a 90-day report-only window [@weinert-2023-managed-policies][@helpnet-2023-microsoft-entra-policies]. The current default is 45 days [@ms-managed-policies]; the window shrank as Microsoft gained confidence that customers were not surprised by the auto-enablement.&lt;/p&gt;
&lt;h3&gt;Five custom policies on top of the baselines&lt;/h3&gt;
&lt;p&gt;Beyond the managed policies, every well-run tenant in operational experience runs five custom policies on top of the baselines [@ms-ca-policy-common]: block legacy authentication unconditionally [@ms-managed-policies]; require the phishing-resistant Authentication Strength for any user in a privileged role [@ms-auth-strengths]; require &lt;code&gt;compliantDevice&lt;/code&gt; for admin centres, finance apps, and customer-data exports [@ms-intune-compliance-partners]; restrict privileged sign-ins to a named-location allow-list with block-or-step-up outside it [@ms-ca-network]; and, where Entra ID P2 is licensed, demand a sign-in-risk-based step-up (MFA at high risk, a passwordless or phishing-resistant method at medium risk) [@ms-id-protection-policies].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 1. Block legacy authentication. 2. Phishing-resistant Authentication Strength for admin roles. 3. Require compliant device for sensitive applications. 4. Named-location restrictions for privileged roles. 5. Sign-in-risk-based step-up where Entra ID P2 is available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Automation entry points (Microsoft Graph)&lt;/h3&gt;
&lt;p&gt;The Graph endpoints administrators care about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /identity/conditionalAccess/policies&lt;/code&gt; -- list policies. &lt;code&gt;POST&lt;/code&gt; to create, &lt;code&gt;PATCH&lt;/code&gt; to update [@ms-graph-capolicy].&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /identityProtection/riskDetections&lt;/code&gt; -- the per-detection log. Filterable by &lt;code&gt;riskLevel&lt;/code&gt;, &lt;code&gt;riskState&lt;/code&gt;, &lt;code&gt;userPrincipalName&lt;/code&gt;, &lt;code&gt;activityDateTime&lt;/code&gt; [@ms-graph-riskdetection].&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /identityProtection/riskyUsers&lt;/code&gt; -- the per-user risk view.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A policy authored in code looks like this (truncated for readability):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;displayName&quot;: &quot;Require phishing-resistant for admins&quot;,
  &quot;state&quot;: &quot;enabledForReportingButNotEnforced&quot;,
  &quot;conditions&quot;: {
    &quot;users&quot;: { &quot;includeRoles&quot;: [&quot;62e90394-69f5-4237-9190-012177145e10&quot;] },
    &quot;applications&quot;: { &quot;includeApplications&quot;: [&quot;All&quot;] }
  },
  &quot;grantControls&quot;: {
    &quot;operator&quot;: &quot;OR&quot;,
    &quot;authenticationStrength&quot;: { &quot;id&quot;: &quot;00000000-0000-0000-0000-000000000004&quot; }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The recommended deployment dance is &lt;code&gt;enabledForReportingButNotEnforced&lt;/code&gt; first; let the Sign-in log show you the impact for a calibration window; promote to &lt;code&gt;enabled&lt;/code&gt; only after the report-only data matches expectations [@ms-ca-report-only].&lt;/p&gt;
&lt;h3&gt;Audit-time visibility&lt;/h3&gt;
&lt;p&gt;Three surfaces matter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sign-in logs&lt;/strong&gt; in the Entra portal show the per-sign-in evaluation, including which CA policies matched and which grants were satisfied.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Risk-detection log&lt;/strong&gt; in Identity Protection (P2 only) shows the per-detection narrative: which &lt;code&gt;riskEventType&lt;/code&gt; fired, with what &lt;code&gt;additionalInfo&lt;/code&gt;, against which user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The What-If tool&lt;/strong&gt; simulates a policy evaluation for a hypothetical sign-in, before you enable a policy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Detection engineering&lt;/h3&gt;
&lt;p&gt;For E5 tenants, the Sign-in logs and risk detections flow into Microsoft Sentinel (via the Microsoft Entra ID connector) or Defender XDR [@ms-sentinel-aad-connector]. A KQL skeleton for high-risk-with-CA-failure looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kusto&quot;&gt;SigninLogs
| where ResultType != 0
| join kind=inner (AADRiskDetections | where RiskLevel == &quot;high&quot;) on UserPrincipalName, CorrelationId
| project TimeGenerated, UserPrincipalName, IPAddress, ConditionalAccessStatus, RiskEventType, FailureReason
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The aggregate scale figure is worth remembering: Microsoft processes &quot;more than 100 trillion security signals&quot; daily across all identity products [@ms-managed-policies]. The detection engineer is consuming a small slice that landed in their tenant.&lt;/p&gt;

Run the following in Microsoft Sentinel or the Entra advanced hunting blade to surface sign-ins that succeeded *despite* a high-confidence risk detection -- the most operationally interesting subset. The query is original to this article; the schema it targets is the canonical Microsoft Sentinel Entra ID connector tables `SigninLogs` and `AADRiskDetections` [@ms-sentinel-aad-connector], and the join-and-filter pattern follows the practice documented in Microsoft&apos;s Sentinel hunting guidance [@ms-sentinel-hunting].&lt;pre&gt;&lt;code class=&quot;language-kusto&quot;&gt;let window = 7d;
SigninLogs
| where TimeGenerated &amp;gt; ago(window)
| where ResultType == 0
| where ConditionalAccessStatus == &quot;success&quot;
| join kind=inner (
    AADRiskDetections
    | where TimeGenerated &amp;gt; ago(window)
    | where RiskLevel == &quot;high&quot;
) on UserPrincipalName, CorrelationId
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, RiskEventType, ConditionalAccessPolicies
| order by TimeGenerated desc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expected count for a well-tuned tenant is small. Spikes warrant a P2 investigation.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Break-glass&lt;/h3&gt;
&lt;p&gt;Two emergency-access accounts. FIDO2-bound. Excluded from every CA policy. Stored as separate hardware tokens in separate safes. Every sign-in is wired to a P1 alert. Per Section 9.4 and Microsoft Learn&apos;s emergency-access guidance, this is the acknowledged operational compromise to the break-glass paradox [@ms-emergency-access].&lt;/p&gt;

A non-personal Entra ID administrator account excluded from Conditional Access and from MFA enforcement, used only when the primary identity infrastructure has failed. Best practice: at least two such accounts, with hardware FIDO2 keys stored separately, monitored by an unconditional alert on any sign-in.
&lt;p&gt;The article has answered &quot;who decided?&quot; five times over: by signal, by policy, by token, by session, by operational pattern. One section remains: the misconceptions that keep recurring.&lt;/p&gt;
&lt;h2&gt;11. Misconceptions that recur&lt;/h2&gt;
&lt;p&gt;Every time these questions come up in practice, the same wrong answers come back. The corrections are worth memorising.&lt;/p&gt;

Only if you have Entra ID P1 or higher and have configured CA policies. Free SKU tenants run Security Defaults, which is a coarse tenant-wide on/off switch, not CA [@ms-security-defaults]. CA is unlocked at P1 [@ms-ca-overview]; risk-based conditions are unlocked at P2 [@ms-id-protection-overview]. The &quot;every tenant runs on CA&quot; framing you sometimes see in marketing material is incorrect.

No. CA is a cloud control plane. Kerberos and NTLM against on-prem domain controllers do not consult Entra at all [@ms-ca-overview]. If your threat model includes on-prem lateral movement, layer in Defender for Identity and the standard on-prem hardening playbook.

No. CAE is event-driven push from the policy plane to CAE-aware resource APIs. The Microsoft Learn CAE document gives the latency ceiling precisely: &quot;the goal for critical event evaluation is for response to be near real time, but latency of up to 15 minutes might be observed because of event propagation time; however, IP locations policy enforcement is instant&quot; [@ms-cae-concept]. There is no 30-second poll. The token can live up to 28 hours because the revocation is event-driven.

No. Clients advertise CAE-readiness via the `cp1` client capability in token requests, specifically by adding `cp1` to the `xms_cc` claim mechanism (or by calling `WithClientCapabilities(new[] { &quot;cp1&quot; })` in MSAL) [@ms-claims-challenge][@ms-app-resilience-cae]. The Microsoft Learn claims-challenge page is explicit: &quot;The only currently known value is `cp1`&quot; [@ms-claims-challenge]. The CAE-aware token is recognisable by its long lifetime (up to 28 hours) and by the resource API&apos;s willingness to issue an `insufficient_claims` challenge, not by a Boolean claim.

No. Third-party MDM compliance partners can write the device compliance state into Entra via Intune&apos;s compliance-partner API [@ms-intune-compliance-partners]. The CA grant reads `isCompliant` on the device object; it does not care which MDM wrote that value. Microsoft&apos;s preferred deployment is Intune, but the integration point is open by design.

In 2023. The public preview of CA filters for workload identities opened on 26 October 2022 [@vansurksum-2022-workload-ca]; the Microsoft Entra Workload Identities standalone product reached GA in late November 2022, and the Conditional Access feature itself reached general availability later in 2023 [@ms-workload-identity-ca]. Any article asserting a 2025 GA date for workload-identity CA is incorrect.

No. Every sign-in produces a Sign-in log entry; ID Protection emits a `riskDetection` only when at least one detector fires for that sign-in [@ms-graph-riskdetection]. Most sign-ins produce no `riskDetection`. Detection engineers querying for risk should join the Sign-in log with the riskDetections log and treat unjoined rows as &quot;no risk flagged at the moment.&quot;

No Microsoft primary source publicly describes the production model architecture or names a per-sign-in feature-vector size. What is published is the detection taxonomy (about two dozen named `riskEventType` values [@ms-id-protection-risks][@ms-graph-riskdetection]), the timing split (real-time / near-real-time / offline [@ms-risk-detection-types]), and the three-tier risk output. The &quot;transformer with 80+ signals&quot; framing is folk knowledge with no Microsoft primary source behind it. The article reframes it as &quot;ML-based with detailed architecture publicly undisclosed.&quot;

Not on its own. A standard MFA grant does not defeat a kit like Evilginx, which proxies both the password and the MFA challenge in real time. The defence is to require the *phishing-resistant Authentication Strength* in CA: FIDO2 with hardware attestation, Windows Hello for Business, or multifactor certificate-based authentication [@ms-auth-strengths]. The cryptographic origin-binding in WebAuthn-class credentials defeats AitM by construction. But the defence only works *when the grant is applied*. A CA policy that demands phishing-resistant for admin roles but not for users will block AitM against admins and not against users.
&lt;h2&gt;12. Two planes, one boundary&lt;/h2&gt;
&lt;p&gt;Replay Alice&apos;s Tuesday.&lt;/p&gt;
&lt;p&gt;Identity Protection&apos;s signal plane scored her 09:02 sign-in. The score was below the medium-risk threshold. Conditional Access&apos;s policy plane evaluated four matching policies. Two demanded MFA; her cached refresh token already satisfied that grant from yesterday. One demanded a compliant device; Intune had marked her laptop compliant overnight. None demanded the block grant. The token issuer issued a CAE-aware bearer token with a 28-hour lifetime. Exchange Online accepted the token. Outlook&apos;s data path opened. Bytes returned to Alice.&lt;/p&gt;
&lt;p&gt;If, twelve minutes later, an attacker tries to sign in with Alice&apos;s credentials from an anonymizing proxy, ID Protection will fire a detection. The detection will lift her user risk to high. CAE will deliver the high-user-risk event to Exchange. Exchange will issue a claims challenge on the next call from Alice&apos;s Outlook. Outlook will replay the challenge to Entra. Entra will re-run CA, see the elevated risk, demand step-up MFA, and either issue a fresh token (after Alice satisfies the step-up) or refuse.&lt;/p&gt;
&lt;p&gt;The modern identity boundary is not a wall. It is a conversation between planes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The boundary is a conversation between planes, not a wall.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The open frontier is real. Agent identities want a richer grant taxonomy than the human one provides. Cross-vendor CAEP wants production receivers outside Microsoft. Workload-identity policy wants grants that go beyond block. The break-glass paradox wants an answer that does not depend on operational discipline. None of these problems will resolve in 2026. They are the next frontier.&lt;/p&gt;
&lt;p&gt;What the reader should now be able to do: trace a sign-in through the signal, policy, token, and session planes; read a &lt;code&gt;conditionalAccessPolicy&lt;/code&gt; JSON and predict the evaluation outcome; identify which class of attack each grant defends against; and name, by reference to specific Microsoft Learn pages, what CA does &lt;em&gt;not&lt;/em&gt; defend against. The promise from Section 1 is delivered.&lt;/p&gt;

Today, 100 percent of consumer Microsoft accounts older than 60 days have multifactor authentication. -- Alex Weinert, Microsoft Identity, November 2023 [@weinert-2023-managed-policies]
&lt;p&gt;Who decided this token is good? The boundary itself decided, by composing the work of every plane named above.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;conditional-access-and-entra-id-protection&quot; keyTerms={[
  { term: &quot;Conditional Access (CA)&quot;, definition: &quot;Microsoft Entra&apos;s JSON-driven policy engine that matches users, apps, and conditions against grants such as block, MFA, and phishing-resistant Authentication Strength.&quot; },
  { term: &quot;Microsoft Entra ID Protection&quot;, definition: &quot;The ML-driven signal plane that emits riskDetection events tagged with riskEventType, riskLevel, riskState, and detectionTimingType.&quot; },
  { term: &quot;Continuous Access Evaluation (CAE)&quot;, definition: &quot;The event-driven session plane between Entra and CAE-aware resource APIs; uses HTTP 401 with WWW-Authenticate insufficient_claims to trigger mid-session re-evaluation.&quot; },
  { term: &quot;Sign-in risk vs user risk&quot;, definition: &quot;Sign-in risk is per-session probability the credential is being used by an attacker; user risk is per-user probability the account is compromised over recent history.&quot; },
  { term: &quot;Authentication Strength&quot;, definition: &quot;A named bundle of acceptable authentication methods that a CA grant can demand; the phishing-resistant strength defeats AitM by binding the credential to the relying-party origin via WebAuthn.&quot; },
  { term: &quot;Primary Refresh Token (PRT)&quot;, definition: &quot;A long-lived refresh token issued to a Windows session at user sign-in to Entra-joined or hybrid-joined devices, bound to the TPM where available, subject to CA at issuance.&quot; },
  { term: &quot;Claims challenge (insufficient_claims)&quot;, definition: &quot;HTTP 401 wire format CAE uses to demand a fresh token: WWW-Authenticate: Bearer error=&quot;insufficient_claims&quot;, claims=&quot;&quot;.&quot; },
  { term: &quot;Workload identity&quot;, definition: &quot;A non-human Entra identity (service principal, managed identity, or app registration&apos;s owned service principal); CA for workload identities applies only to single-tenant service principals with a block-only grant set.&quot; },
  { term: &quot;Break-glass account&quot;, definition: &quot;An emergency-access account excluded from Conditional Access, ideally FIDO2-bound, monitored by an unconditional sign-in alert.&quot; }
]} questions={[
  { q: &quot;What is the only API surface between Entra ID Protection (the signal plane) and Conditional Access (the policy plane), and why does the answer explain the maintainability of the architecture across a decade?&quot;, a: &quot;Two condition keys on the CA policy: signInRiskLevels and userRiskLevels. Because the contract is two strings, the risk model can be re-trained without policy rewrites, and policies can evolve without retraining the model.&quot; },
  { q: &quot;Why did Microsoft reject the &apos;shortened token lifetime&apos; approach to revocation, and what did they ship instead?&quot;, a: &quot;Shortened token lifetimes degraded user experience and reliability without eliminating risks (Microsoft&apos;s documented &apos;blunt object&apos; framing). CAE lengthens tokens (up to 28 hours) and adds an event-driven side channel that fires HTTP 401 with insufficient_claims when a critical event occurs.&quot; },
  { q: &quot;Name the documented critical events that fire a CAE claims challenge, and the documented latency ceiling.&quot;, a: &quot;Five critical events: account disabled or deleted, password change or reset, MFA enabled by an admin, admin token revocation, and high user risk detected by ID Protection. A parallel pathway propagates network-location and CA policy changes on the same channel. Latency is up to 15 minutes for non-IP events, instant for IP locations.&quot; },
  { q: &quot;Why does Conditional Access not gate on-prem Active Directory logons?&quot;, a: &quot;CA is a cloud control plane. Kerberos and NTLM against on-prem domain controllers authenticate against the on-prem KDC and do not consult Entra. This is a documented scope limit, not a bug.&quot; },
  { q: &quot;What HRU result establishes a theoretical lower bound on what CA can guarantee, and what is the practical implication?&quot;, a: &quot;Harrison-Ruzzo-Ullman 1976 proves the safety question in the general access-matrix model is undecidable. Practically, no tool can certify that no sequence of policy edits will ever leak access to a sensitive resource; vendors rely on evaluation-time mediation rather than static proof.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>conditional-access</category><category>entra-id</category><category>identity-protection</category><category>continuous-access-evaluation</category><category>zero-trust</category><category>security</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Privileged Identity Management: How a Two-State Role Assignment Retired Standing Admin</title><link>https://paragmali.com/blog/privileged-identity-management-how-a-two-state-role-assignme/</link><guid isPermaLink="true">https://paragmali.com/blog/privileged-identity-management-how-a-two-state-role-assignme/</guid><description>Microsoft Entra PIM did not add eight features. It added one field to the role-assignment object -- and everything else, from activation policies to GDAP, is downstream.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Standing Global Administrator was never a design choice. It was the only posture a single-state role-assignment object could produce.** Microsoft Entra PIM added one field to that object -- `type: eligible | active` -- and everything downstream (activation policies, audit logs, access reviews, six PIM Alerts, PIM-for-Groups, PIM-for-Azure-Resources, GDAP, Lighthouse, PIM with Conditional Access) is a structural consequence of that single change. The pattern works for human users. The open boundary in 2026 is application identities -- service principals, managed identities, OAuth consent grants -- which route around PIM entirely via the Azure Instance Metadata Service endpoint at `169.254.169.254`, the bypass class Andy Robbins documented in June 2022 and MITRE ATT&amp;amp;CK now maps to T1078.004.
&lt;h2&gt;1. The Tenant with Zero Standing Global Administrators&lt;/h2&gt;
&lt;p&gt;At 14:03:01 on a Tuesday in 2026, &lt;a href=&quot;mailto:alice@contoso.com&quot; rel=&quot;noopener&quot;&gt;alice@contoso.com&lt;/a&gt; became Global Administrator of her company&apos;s Microsoft Entra tenant. At 15:03:01 the same day, she stopped being one. In between, she restored a deleted user, exported an audit log, and produced a single PIM record: &lt;code&gt;Justification&lt;/code&gt; reads &quot;incident MSRC-2026-PIM-12345, ticket SNOW-INC-987654&quot;; &lt;code&gt;Approver&lt;/code&gt; reads &quot;&lt;a href=&quot;mailto:bob@contoso.com&quot; rel=&quot;noopener&quot;&gt;bob@contoso.com&lt;/a&gt; (decided 14:02:17)&quot;; &lt;code&gt;ActivatedAt&lt;/code&gt; and &lt;code&gt;ExpiredAt&lt;/code&gt; differ by exactly &lt;code&gt;PT1H&lt;/code&gt;. The SOC 2 auditor signed it off without follow-up questions.&lt;/p&gt;
&lt;p&gt;The 2015-vintage version of the same tenant looked nothing like this. Twelve standing Global Administrators. No multifactor challenge at privilege use. No approval workflow. No justification field. No audit trail beyond ordinary sign-in logs. A single phish of any one of those twelve identities was tenant takeover. The math required no sophistication: the attack surface for &quot;Global Administrator of contoso.com&quot; equalled the union of twelve personal attack surfaces, indefinitely.&lt;/p&gt;
&lt;p&gt;What changed between the two tenants is not a habit, not a policy, not a culture shift. It is a single field on a single object inside Microsoft Entra ID.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Standing admin was never a deliberate design decision. It was the only deployment posture a single-state role-assignment object could produce. Once Microsoft made the role-assignment object two-state, JIT admin became expressible -- and standing admin became visibly the anti-pattern it had been since 1975.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To explain that field, and to explain why it took fifty-one years to ship, we start where the principle did: a 1975 paper by two MIT researchers who knew what privilege should look like but had no mechanism to enforce it.&lt;/p&gt;
&lt;h2&gt;2. The Default Wasn&apos;t a Decision&lt;/h2&gt;
&lt;p&gt;Who designed the standing Domain Admin pattern? No one. It was the only assignment category Active Directory shipped with.&lt;/p&gt;
&lt;p&gt;A forty-year deployment posture with no author. That is the first thing to internalize. Standing admin is what happens when a data model offers exactly one assignment category and operators still have real work to do. Every later &quot;best practice&quot; was an attempt to talk operators out of the one tool they had been given.&lt;/p&gt;
&lt;h3&gt;1975: The principle without a mechanism&lt;/h3&gt;
&lt;p&gt;In September 1975, Jerome Saltzer and Michael Schroeder published &lt;em&gt;The Protection of Information in Computer Systems&lt;/em&gt; in the &lt;em&gt;Proceedings of the IEEE&lt;/em&gt; [@saltzer-schroeder-1975]. The paper is a survey of secure-systems design, organized around eight named design principles that the authors crystallized from work on Multics and other early protected operating systems. Both authors were affiliated with MIT&apos;s Project MAC and the Department of Electrical Engineering and Computer Science [@saltzer-mit-meta].&lt;/p&gt;
&lt;p&gt;The sixth principle, named &lt;strong&gt;Least Privilege&lt;/strong&gt;, is the one every later JIT-admin product cites:&lt;/p&gt;

Every program and every user of the system should operate using the least set of privileges necessary to complete the job. -- Saltzer &amp;amp; Schroeder, *The Protection of Information in Computer Systems*, 1975, Design Principle (f), the sixth of eight [@saltzer-schroeder-1975]

Design Principle (f), the sixth of eight, in the 1975 Saltzer and Schroeder paper. Every program and every user of the system should operate using the least set of privileges necessary to complete the job. The principle is correct, parsimonious, and -- for four decades after publication -- mechanically unenforceable for the temporal case. Static enforcement (ACLs, capability lists, ring boundaries) was tractable in 1975; bounding the time interval during which a privilege is held was not.
&lt;p&gt;Read the principle carefully. It does not say &quot;every user should hold the least set of privileges.&quot; It says they should &lt;em&gt;operate using&lt;/em&gt; the least set of privileges. The two formulations look identical until you ask what a person does between bursts of administrative work. A user who holds the privilege &quot;permanently active&quot; is operating using it permanently, whether they touch the system or not. The 1975 paper points at the temporal dimension and walks past it. The worked examples cover static mechanisms -- protection rings, access control lists, capability tickets -- not time-bounded ones. The principle was correct. The mechanism did not yet exist.&lt;/p&gt;
&lt;p&gt;For the next forty years, every approximation tried to compensate. UNIX &lt;code&gt;sudo&lt;/code&gt; (1980) bound elevation to a single command. Kerberos delegation (1988) bound impersonation to a ticket. Windows DACLs and Active Directory groups (1993 and 2000) bound access to a static membership list. None made temporal least privilege a first-class data-model property. None let an operator say &quot;I am eligible to be Domain Admin, but I am not Domain Admin right now.&quot;&lt;/p&gt;

Microsoft&apos;s 2014 *Mitigating Pass-the-Hash v2* whitepaper introduced a three-tier administrative model. Tier 0 is identity-system-critical: domain controllers, ADFS, PKI, anything whose compromise gives forest-wide privilege. Tier 1 is enterprise servers and business-critical applications. Tier 2 is user workstations and end users. The enforcement rule is one sentence: an administrator credential for Tier N must never be exposed to a system at a higher (numerically larger) tier. Microsoft has progressively retired this framing in favour of the Enterprise Access Model, which we revisit in section 6.
&lt;h3&gt;2000-2013: Group membership as a boolean&lt;/h3&gt;
&lt;p&gt;When Active Directory shipped with Windows 2000 on February 17, 2000 [@ms-news-windows-2000-launch], privileged access was structurally a boolean property of the principal. A user was either a member of &lt;code&gt;BUILTIN\Administrators&lt;/code&gt;, &lt;code&gt;Domain Admins&lt;/code&gt;, &lt;code&gt;Enterprise Admins&lt;/code&gt;, or &lt;code&gt;Schema Admins&lt;/code&gt;, or they were not. The membership lived in the directory as the &lt;code&gt;member&lt;/code&gt; attribute on the group object (and the &lt;code&gt;memberOf&lt;/code&gt; back-link on the user). It was set when assignment was made, unset when an administrator manually revoked it. No third state. No attribute could hold one.&lt;/p&gt;

A privileged identity whose role assignment is active and permanent. The role&apos;s permissions are granted continuously, regardless of whether the principal is currently exercising the privilege. Standing admin is the default state of any pre-PIM tenant and the deployed-reality state of most AD-only environments through 2026.
&lt;p&gt;&lt;a href=&quot;https://paragmali.com/blog/kerberos-in-windows-the-other-half-of-ntlmless/&quot; rel=&quot;noopener&quot;&gt;Kerberos&apos;s Privilege Attribute Certificate&lt;/a&gt; -- the PAC -- carried the user&apos;s group SIDs forward into every Kerberos ticket the user obtained.The Privilege Attribute Certificate is the data structure inside a Kerberos ticket that lists the user&apos;s group SIDs. Pre-2016 Active Directory had no per-membership TTL metadata in the PAC. There was nowhere in the existing schema to put an expiry timestamp, which is why on-prem JIT membership later required a &lt;em&gt;separate forest&lt;/em&gt; rather than an in-directory mechanism. A ticket&apos;s lifetime was bounded; the SID set inside it was not. There was no per-membership TTL anywhere in the system. If you wanted &quot;Alice is Domain Admin between 14:00 and 15:00 today and not otherwise,&quot; the directory had no machinery to express it. Alice was Domain Admin permanently, or not at all.&lt;/p&gt;
&lt;p&gt;Twenty years of deployment matched the data model exactly. A typical 2010-vintage enterprise ran ten to thirty standing Domain Administrators across business units, because manually adding and removing membership for each task was untenable at human scale. The data model did not punish standing membership; the operator chose the only category the directory offered.&lt;/p&gt;
&lt;h3&gt;December 2012: Microsoft names the failure mode&lt;/h3&gt;
&lt;p&gt;In December 2012, Patrick Jungles, Mark Simos, Aaron Margosis, Roger Grimes, Laura Robinson and the Microsoft Trustworthy Computing team published &lt;em&gt;Mitigating Pass-the-Hash and Other Credential Theft, Version 1&lt;/em&gt; [@pth-download-center], [@berkouwer-pth-2013]. It is the first formal Microsoft acknowledgment that credential-theft propagation through Active Directory was not a software defect to be patched but a structural property of standing admin membership.&lt;/p&gt;
&lt;p&gt;The argument is direct. If twelve Domain Admins exist, the attack surface of &quot;Domain Admin of contoso.local&quot; is the union of those twelve people&apos;s personal attack surfaces. Any one gets phished, or gets hash-extracted from a Tier-1 server they accidentally signed into, and the attacker has Domain Admin permanently. The MIM PAM documentation later restated the failure in one sentence: &lt;em&gt;&quot;Today, it&apos;s too easy for attackers to obtain Domain Admins account credentials, and it&apos;s too hard to discover these attacks after the fact&quot;&lt;/em&gt; [@ms-learn-mim-pam-overview].&lt;/p&gt;
&lt;h3&gt;2014: The tier model arrives, the mechanism does not&lt;/h3&gt;
&lt;p&gt;The 2014 update -- &lt;em&gt;Mitigating Pass-the-Hash, Version 2&lt;/em&gt; [@pth-download-center] -- generalized the threat model and introduced the &lt;a href=&quot;https://paragmali.com/blog/who-is-allowed-to-log-in-where-the-kdc-side-answer-to-creden/&quot; rel=&quot;noopener&quot;&gt;Tier-0 / Tier-1 / Tier-2 framing&lt;/a&gt; as a structural mitigation. v2 said two things clearly that v1 had only implied. First, standing membership in Tier-0 groups was the root cause, not a downstream defect. Second, the mitigation pattern -- isolate tiers, reduce the standing count, use dedicated Privileged Access Workstations -- was &lt;em&gt;guidance&lt;/em&gt;, not a mechanism. Microsoft Trustworthy Computing did not yet have a product that could mechanically time-bound group membership in Active Directory.&lt;/p&gt;
&lt;p&gt;v2 named the problem, drew the threat model, and recommended the structural fix. What it could not do was ship a mechanism. The mechanism would come, but on the wrong side of the cloud boundary.&lt;/p&gt;
&lt;h2&gt;3. The On-Prem Detour: MIM 2016 PAM, Bastion Forests, and Shadow Principals&lt;/h2&gt;
&lt;p&gt;Microsoft&apos;s first mechanical JIT-admin product was not in the cloud. It was on-premises, and it required a separate Active Directory forest.&lt;/p&gt;
&lt;p&gt;Stop and re-read that. To bound the duration of a group membership in pre-2016 Active Directory, Microsoft had to build a &lt;em&gt;different&lt;/em&gt; directory and inject SIDs from one into the other across a trust. The reason was the data model. The production forest&apos;s &lt;code&gt;member&lt;/code&gt; attribute had no TTL field. Adding one meant changing the AD schema. Changing the schema meant a Windows Server release. So while the schema change was in flight, Microsoft shipped the on-prem JIT-admin product on a different architecture: ask the operator to stand up a second forest whose only job was to issue time-bounded SIDs into the first.&lt;/p&gt;
&lt;h3&gt;August 6, 2015: MIM 2016 ships PAM&lt;/h3&gt;
&lt;p&gt;On August 6, 2015, Microsoft Identity Manager 2016 reached general availability and shipped a new capability named &lt;strong&gt;Privileged Access Management&lt;/strong&gt; [@ms-learn-mim-pam-overview]. The architecture is the interesting part. MIM PAM uses three primitives that, together, give Active Directory a mechanically time-bounded group membership for the first time:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;bastion forest&lt;/strong&gt; -- an entirely separate Active Directory forest, sometimes called the &quot;red&quot; forest or &quot;admin&quot; forest, where privileged accounts live.&lt;/li&gt;
&lt;li&gt;A one-way &lt;strong&gt;PAM trust&lt;/strong&gt; from the production forest to the bastion forest, configured for selective authentication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shadow principal&lt;/strong&gt; objects in the bastion forest, each carrying a SID that names a real privileged group in the production forest.&lt;/li&gt;
&lt;/ol&gt;

A separate Active Directory forest dedicated to housing privileged accounts. In MIM 2016 PAM the bastion forest holds shadow-principal objects whose SIDs point at production-forest privileged groups; a one-way PAM trust lets the production forest accept those SIDs in incoming Kerberos tickets for a bounded duration.

An Active Directory object (schema class `msDS-ShadowPrincipal`, introduced in Windows Server 2016) that represents a foreign user, group, or computer in the bastion forest and carries an `msDS-ShadowPrincipalSid` attribute populated with the SID of a production-forest privileged group. Membership in a shadow principal results in that production-forest SID being added to the requesting user&apos;s Kerberos PAC for the membership TTL.
&lt;p&gt;The activation flow is direct. A user in the bastion forest requests privilege through the MIM Portal. An approver decides. MIM writes a TTL-bounded membership in the appropriate shadow principal, with the TTL enforced by the Windows Server 2016 temporal-group-membership feature [@teal-esae3]. The bastion KDC injects the production-forest SID into the user&apos;s Kerberos PAC. The production forest accepts that SID across the PAM trust. After the TTL expires, subsequent ticket renewals exclude the privileged SID, and the user no longer holds the privilege.&lt;/p&gt;

flowchart LR
    subgraph BASTION[&quot;CORP-PRIV bastion forest&quot;]
        A[&quot;Privileged user account&quot;]
        SP[&quot;Shadow principal (msDS-ShadowPrincipal) carries production SID, TTL&quot;]
        BKDC[&quot;Bastion KDC&quot;]
        A --&amp;gt;|&quot;Time-bound membership&quot;| SP
        SP --&amp;gt; BKDC
    end
    subgraph PROD[&quot;CORP production forest&quot;]
        DA[&quot;Domain Admins&quot;]
        PKDC[&quot;Production KDC&quot;]
    end
    BKDC --&amp;gt;|&quot;Kerberos ticket carries injected SID via PAM trust&quot;| PKDC
    PKDC --&amp;gt;|&quot;SID in PAC grants membership for TTL only&quot;| DA
&lt;h3&gt;October 15, 2016: Windows Server 2016 makes the mechanism real&lt;/h3&gt;
&lt;p&gt;For the first fourteen months of MIM 2016&apos;s life, the full feature did not work. The temporal-group-membership and shadow-principal schema classes that MIM PAM depends on are AD primitives that arrived only with Windows Server 2016, which reached general availability on October 15, 2016 [@ms-learn-lifecycle-ws2016]. Microsoft Learn states the requirement directly: &lt;em&gt;&quot;With Windows Server 2016, PAM features of time-limited group memberships and shadow principal groups are built into Windows Server Active Directory&quot;&lt;/em&gt; [@ms-learn-raise-bastion], and &lt;em&gt;&quot;All domain controllers in the bastion environment for the PRIV forest must be Windows Server 2016 or later&quot;&lt;/em&gt; [@ms-learn-raise-bastion].The PAM trust is technically a forest trust with selective authentication enabled. The selective authentication flag is what prevents the bastion forest&apos;s privileged identities from being usable for anything other than the explicit shadow-principal SID injection -- without it, the bastion forest would itself become a sprawling privileged-access surface.&lt;/p&gt;
&lt;p&gt;This is the moment AD itself gains a temporal least-privilege primitive, forty-one years after Saltzer and Schroeder published the principle. The mechanism is real, but the operational profile is brutal.&lt;/p&gt;
&lt;h3&gt;Three reasons it did not generalize&lt;/h3&gt;
&lt;p&gt;MIM PAM solved exactly one problem and could not be extended to the next. Three structural constraints kept it confined to a niche.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;it was on-premises only&lt;/strong&gt;. A bastion forest is an Active Directory artifact. Microsoft Entra ID, Office 365, and Azure RBAC role assignments live in a different identity system, with no concept of a forest, no PAM trust target, and no place to plug a shadow-principal object. MIM PAM had no cloud story, and by 2015 the cloud was already where most new Microsoft privileged-access surfaces were being deployed.&lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;the operational complexity filtered out everyone except the most security-mature shops&lt;/strong&gt;. A bastion forest is a separate Active Directory forest, with its own domain controllers, replication, backup, disaster recovery, and PKI implications. The deployment also requires MIM Service, MIM Portal, MIM Web Service, and SQL Server. Auditing the PAM trust correctly is itself non-trivial work. Microsoft Learn now positions MIM PAM as appropriate only for isolated, non-Internet-connected deployments [@ms-learn-mim-pam-overview]; the verbatim positioning and the MIM 2016 lifecycle details are in the Callout below.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft Learn states MIM PAM is &quot;not recommended for new deployments in Internet-connected environments&quot; and positions it for &quot;isolated AD environments where Internet access is not available&quot; [@ms-learn-mim-pam-overview]. MIM 2016 itself remains in extended support through January 9, 2029 [@ms-learn-mim-2016], and Microsoft has shipped SP3 compatibility updates for SharePoint Subscription Edition, Exchange SE, and SQL Server 2022 -- but the cloud-first Entra PIM path is the canonical answer for new tenants.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Third, &lt;strong&gt;the forest-functional-level dependency delayed real deployment by more than a year&lt;/strong&gt;. Shadow principals were not usable until Windows Server 2016 reached GA in October 2016. MIM 2016 had been generally available since August 2015. For its first fourteen months in market, the headline JIT-admin feature could not be configured at full fidelity. By the time Windows Server 2016 shipped, Microsoft was already operating its cloud PIM in production.&lt;/p&gt;
&lt;h3&gt;What the on-prem detour reveals about the cloud&apos;s shape&lt;/h3&gt;
&lt;p&gt;MIM PAM mechanically bounds membership &lt;em&gt;in groups&lt;/em&gt; via &lt;em&gt;shadow principals&lt;/em&gt; in &lt;em&gt;a separate forest&lt;/em&gt;. The cloud has no concept of a forest. So the cloud-native mechanical bound must attach to the &lt;em&gt;assignment object&lt;/em&gt; directly, not to the &lt;em&gt;group object indirected through a separate forest&lt;/em&gt;. The cloud needed a new assignment-category type, not a new forest topology.&lt;/p&gt;
&lt;p&gt;The cloud does not have a forest. It has a role-assignment object. What if that object grew a second state?&lt;/p&gt;
&lt;h2&gt;4. The Breakthrough: A Two-State Role-Assignment Object&lt;/h2&gt;
&lt;p&gt;By August 2015, while MIM 2016 PAM was still in late preview for the on-premises case, the Microsoft Identity Division had already shipped something different for the cloud. They shipped a role-assignment object with one new field. That field changed everything that came after it.&lt;/p&gt;
&lt;h3&gt;The 2015 preview&lt;/h3&gt;
&lt;p&gt;Alex Simons&apos;s August 27, 2015 capability-update post on the CloudBlogs (now migrated to Microsoft Tech Community) is the first public articulation of what Azure AD PIM was building [@simons-2015-aug]. It introduced four surfaces: an &lt;strong&gt;eligible&lt;/strong&gt; assignment category distinct from active, multifactor authentication required at activation, security alerts that watched for privileged-role anomalies, and what the post called Security Reviews -- the precursor to access reviews. The architecture under those four surfaces is the load-bearing part: a single new field on the role-assignment object.&lt;/p&gt;
&lt;p&gt;On September 15, 2016, Azure AD Premium P2 reached general availability and carried the first generally-available cloud-native PIM, attributed to Joy Chik (then Corporate Vice President of the Identity Division) and the Identity engineering team [@techcommunity-p2-ga]. Eligible-versus-active was now a billable, supported, production-grade feature.&lt;/p&gt;
&lt;h3&gt;The one-function spine&lt;/h3&gt;
&lt;p&gt;Read this carefully. It is the article&apos;s central claim.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Standing admin was the default not because anyone thought it was secure, but because the role-assignment object had only one state. PIM&apos;s contribution is to add a second state -- &lt;code&gt;eligible&lt;/code&gt; -- and to make the transition from eligible to active a gated, audited, time-bounded operation that is by definition mediated by PIM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The principle was Saltzer and Schroeder, 1975. The recognition that standing admin was the failure mode was &lt;em&gt;Mitigating Pass-the-Hash&lt;/em&gt;, 2012 and 2014. The on-premises mechanism was MIM 2016 PAM. The cloud answer is a different shape entirely: not a new directory and a SID-injection trust, but a single field on the assignment object itself.&lt;/p&gt;
&lt;p&gt;Microsoft Learn documents the resulting terminology in the PIM overview. A principal -- user, group, service principal, or managed identity -- can be &lt;code&gt;eligible&lt;/code&gt; or &lt;code&gt;active&lt;/code&gt; for a role, and either assignment can be &lt;code&gt;permanent&lt;/code&gt; or &lt;code&gt;time-bound&lt;/code&gt; [@ms-learn-pim-configure]. The same page elevates a forty-year-old phrase into a product term: &lt;em&gt;&quot;principle of least privilege access -- A recommended security practice in which every user is provided with only the minimum privileges needed to accomplish the tasks they&apos;re authorized to perform&quot;&lt;/em&gt; [@ms-learn-pim-configure]. The 1975 sentence is now a glossary entry inside a 2026 product, and the product has a mechanism that makes the sentence enforceable.&lt;/p&gt;
&lt;h3&gt;The formal tuple&lt;/h3&gt;
&lt;p&gt;Concretely, a PIM-managed role assignment is a 5-tuple. Let $A = (p, r, s, t, d)$ where $p$ is the principal, $r$ is the role, $s$ is the scope, $t \in {\text{eligible}, \text{active}}$, and $d \in {\text{permanent}, \text{time-bound}[s_0, e_0]}$. The activation transition is&lt;/p&gt;
&lt;p&gt;$$\text{activate}: A_{t=\text{eligible}} \longrightarrow A_{t=\text{active},\ d=\text{time-bound}[\text{now},\ \text{now}+\Delta]}$$&lt;/p&gt;
&lt;p&gt;subject to the per-role activation policy. The interesting part is what the tuple makes expressible:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RoleAssignment = {
    principal:  user | group | service principal | managed identity,
    role:       Entra directory role | Azure RBAC role | group membership | group ownership,
    scope:      directory | management-group | subscription | resource-group | resource | group,
    type:       eligible | active,
    duration:   permanent | time-bound[start, end]
}

activate: eligible_assignment -&amp;gt; active_assignment   // PIM-mediated, gated, audited
&lt;/code&gt;&lt;/pre&gt;

A PIM-managed role assignment that grants no privilege until the principal invokes `activate()`. The eligible assignment is the standing relationship between principal and role; the active assignment is the time-bounded materialization that follows when the activation policy is satisfied [@ms-learn-pim-configure].

A PIM-managed role assignment that grants the role&apos;s permissions for the duration of the assignment. Active assignments are either permanent (the legacy pre-PIM posture, or an explicit permanent-active PIM assignment) or time-bound (the result of an `activate()` call on an eligible assignment) [@ms-learn-pim-configure].

flowchart TD
    subgraph Permanent[&quot;Permanent duration&quot;]
        PE[&quot;Permanent eligible -- standing eligibility, no privilege held&quot;]
        PA[&quot;Permanent active -- legacy standing admin&quot;]
    end
    subgraph TimeBound[&quot;Time-bound duration&quot;]
        TE[&quot;Time-bound eligible -- standing eligibility with end date&quot;]
        TA[&quot;Time-bound active -- JIT admin after activate()&quot;]
    end
    PE --&amp;gt;|&quot;activate()&quot;| TA
    TE --&amp;gt;|&quot;activate()&quot;| TA
    TA --&amp;gt;|&quot;expire or deactivate()&quot;| PE
    PA --&amp;gt;|&quot;legacy posture being retired&quot;| PE
&lt;p&gt;The grid has only four cells. Permanent active is the pre-PIM world, the standing-admin posture every later best practice has been trying to retire. Time-bound active is the JIT-admin state, materialized only at the moment of work and expired shortly after. The two eligible states -- permanent or time-bound -- are the standing relationships between a principal and a role that grant no privilege at rest. The expressive change is small. The deployment consequences are total.&lt;/p&gt;

PIM did not add eight features. It added one field, and everything else is downstream.
&lt;p&gt;This is Aha #1. The reader who came in believing standing admin persisted for forty years because operators lacked discipline now sees it differently. Operator discipline was a fragile workaround for a missing data-model field. The 1975 principle was correct. The 2012-2014 PtH whitepapers were correct. The operators were not the problem. The role-assignment object had one state to be in, and the deployment matched the data model exactly. The fix was a structural change to the data model.&lt;/p&gt;
&lt;p&gt;The next nine years of PIM history are about extending that two-state primitive: to Azure RBAC, to security groups, to partner tenants, to the conditional-access plane, and to a detection layer that flags people who try to skip activation entirely. We walk each extension in turn. First, the mechanism itself.&lt;/p&gt;
&lt;h2&gt;5. Anatomy of an Activation&lt;/h2&gt;
&lt;p&gt;We have seen what changed. Walk through what happens, end to end, when &lt;a href=&quot;mailto:alice@contoso.com&quot; rel=&quot;noopener&quot;&gt;alice@contoso.com&lt;/a&gt; clicks &quot;Activate&quot; on her eligible Global Administrator assignment at 14:00:00 on a Tuesday.&lt;/p&gt;
&lt;h3&gt;The activation flow, step by step&lt;/h3&gt;
&lt;p&gt;Six things happen, in order, and each writes audit-log evidence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The eligible assignment already exists.&lt;/strong&gt; Alice has been a permanent-eligible Global Administrator since she was hired. The PIM directory object records principal &lt;code&gt;alice@contoso.com&lt;/code&gt;, role &lt;code&gt;Global Administrator&lt;/code&gt;, scope &lt;code&gt;directory&lt;/code&gt;, &lt;code&gt;type=eligible&lt;/code&gt;, &lt;code&gt;duration=permanent&lt;/code&gt;. Today she holds zero of the role&apos;s permissions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The activation request lands on PIM.&lt;/strong&gt; Alice clicks Activate in the Entra admin centre, or fires the equivalent Microsoft Graph call. PIM pulls the activation policy for &lt;code&gt;(role=Global Administrator, scope=directory)&lt;/code&gt; and prepares to evaluate the gates [@ms-learn-pim-change-default-settings].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The policy gates evaluate.&lt;/strong&gt; This is the load-bearing part, and the place readers most often misread the docs. The gates are per-role configurable, not universal. Microsoft Learn documents five gates the tenant can independently switch on or off [@ms-learn-pim-change-default-settings]:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multifactor authentication at activation&lt;/strong&gt; if &lt;code&gt;requires_mfa&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approval routing&lt;/strong&gt; to named approvers or an approver group if &lt;code&gt;requires_approval&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Justification text capture&lt;/strong&gt; if &lt;code&gt;requires_justification&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ticket number capture&lt;/strong&gt;, optionally tagged with a ticketing-system identifier, if &lt;code&gt;requires_ticket&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Activation duration validation&lt;/strong&gt; against the per-role configurable maximum -- one to twenty-four hours, with one hour the default for the highest-privileged Entra roles such as Global Administrator and Privileged Role Administrator [@ms-learn-pim-change-default-settings].&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PIM materializes the active assignment.&lt;/strong&gt; Microsoft Learn states the latency directly: &lt;em&gt;&quot;Microsoft Entra PIM creates active assignment (assigns user to a role) within seconds&quot;&lt;/em&gt; [@ms-learn-pim-activate]. A new token Alice obtains after this moment will carry the activated role&apos;s claims.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The PIM audit log records the entire transaction.&lt;/strong&gt; A new entry captures the request, the approver&apos;s decision and decision time, the justification text, the ticket reference, the activation start, and the planned expiry. The audit log is retained for thirty days by default and can be routed to Azure Monitor for longer retention [@ms-learn-pim-audit-log].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto-deactivation fires at the duration boundary.&lt;/strong&gt; At 15:00:00 -- one hour after activation -- PIM deactivates the assignment within seconds [@ms-learn-pim-activate]. Alice can also call &lt;code&gt;deactivate()&lt;/code&gt; explicitly to return early.&lt;/li&gt;
&lt;/ol&gt;

sequenceDiagram
    autonumber
    participant User as alice
    participant PIM
    participant MFA
    participant Approver as bob
    participant Graph as Microsoft Graph
    participant Audit as PIM audit log
    User-&amp;gt;&amp;gt;PIM: Activate Global Administrator
    PIM-&amp;gt;&amp;gt;MFA: Require MFA challenge
    MFA--&amp;gt;&amp;gt;PIM: MFA passed
    PIM-&amp;gt;&amp;gt;Approver: Route approval request
    Approver--&amp;gt;&amp;gt;PIM: Approve with justification context
    PIM-&amp;gt;&amp;gt;Graph: Materialize active assignment within seconds
    PIM-&amp;gt;&amp;gt;Audit: Write request, decision, materialization records
    Note over PIM,Audit: Token issued with activated role claims
    Note over PIM,Graph: One-hour TTL begins
    PIM-&amp;gt;&amp;gt;Graph: Auto-deactivate at expiry within seconds
    PIM-&amp;gt;&amp;gt;Audit: Write deactivation record
&lt;h3&gt;Activation policies are configured, not assumed&lt;/h3&gt;
&lt;p&gt;Two of the most common misunderstandings the documentation receives are about this configurability. First, MFA at activation is not universally required by PIM. The role&apos;s activation policy must be set to require it. Second, the activation maximum is configurable per role per scope inside a one-to-twenty-four-hour range, with the default for Global Administrator and Privileged Role Administrator at one hour [@ms-learn-pim-change-default-settings]. A &quot;PIM tenant&quot; where one role requires MFA and approval and another role requires only justification text is a perfectly valid configuration; both roles are PIM-gated, but their gate sets differ.&lt;/p&gt;

A per-role-per-scope configuration of which gates an activation must satisfy: MFA at activation, approval, justification, ticket number, and the activation maximum duration. PIM evaluates the policy at activation time. The gates are independent flags; any combination can be required [@ms-learn-pim-change-default-settings].
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PIM&apos;s activation maximum duration is configurable per role per scope in the one-to-twenty-four-hour range. The default value for the highest-privileged Entra directory roles -- Global Administrator and Privileged Role Administrator -- is one hour [@ms-learn-pim-change-default-settings]. Other roles default to higher values. Tighten the duration where you can; the activation cost is small, the standing-active surface saving is large.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Authentication context: gating activation, not sign-in&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://paragmali.com/blog/inside-the-primary-refresh-token-the-cryptographic-seam-betw/&quot; rel=&quot;noopener&quot;&gt;Conditional Access&lt;/a&gt; has gated sign-in since 2014. Until 2023, it had no way to gate the activation event itself. The integration between PIM and Conditional Access changes that by attaching an &lt;strong&gt;authentication context&lt;/strong&gt; label to the activation, which Conditional Access can target the same way it targets any other authentication. Microsoft Learn includes the activation policy option &lt;em&gt;&quot;On activation, require Microsoft Entra Conditional Access authentication context&quot;&lt;/em&gt; [@ms-learn-pim-change-default-settings].&lt;/p&gt;

A label that PIM attaches to the activation event so that Conditional Access policies can target the activation itself, not just the sign-in. Policies such as &quot;activation of Global Administrator requires a compliant device and an MFA challenge issued within the last five minutes&quot; become expressible without bolting on a third-party stack [@ms-learn-pim-change-default-settings].
&lt;h3&gt;The activation gate, as code&lt;/h3&gt;
&lt;p&gt;To make the gate-composition idea concrete, here is the activation policy as a small JavaScript function. Edit the policy or the request and re-run it.&lt;/p&gt;
&lt;p&gt;{`
function activate(request, policy) {
  // policy gates are independent; any combination can be required
  if (policy.requires_mfa &amp;amp;&amp;amp; !request.mfa_passed) {
    return { ok: false, reason: &apos;MFA challenge failed or absent&apos; };
  }
  if (policy.requires_approval &amp;amp;&amp;amp; !request.approval_decision) {
    return { ok: false, reason: &apos;Approval pending&apos; };
  }
  if (policy.requires_justification &amp;amp;&amp;amp; !request.justification) {
    return { ok: false, reason: &apos;Justification text missing&apos; };
  }
  if (policy.requires_ticket &amp;amp;&amp;amp; !request.ticket_number) {
    return { ok: false, reason: &apos;Ticket number missing&apos; };
  }
  if (request.duration_hours &amp;gt; policy.max_duration_hours) {
    return { ok: false, reason: &apos;Requested duration exceeds policy maximum&apos; };
  }
  // activation succeeds: materialize a time-bound active assignment
  const expires_at = new Date(Date.now() + request.duration_hours * 3600 * 1000);
  return {
    ok: true,
    active_assignment: {
      principal: request.principal,
      role: request.role,
      scope: request.scope,
      type: &apos;active&apos;,
      duration: { kind: &apos;time-bound&apos;, start: new Date(), end: expires_at }
    }
  };
}&lt;/p&gt;
&lt;p&gt;const policy = {
  requires_mfa: true, requires_approval: true,
  requires_justification: true, requires_ticket: true,
  max_duration_hours: 1
};
const request = {
  principal: &apos;&lt;a href=&quot;mailto:alice@contoso.com&quot; rel=&quot;noopener&quot;&gt;alice@contoso.com&lt;/a&gt;&apos;, role: &apos;Global Administrator&apos;, scope: &apos;directory&apos;,
  mfa_passed: true, approval_decision: &apos;approve&apos;,
  justification: &apos;MSRC-2026-PIM-12345&apos;, ticket_number: &apos;SNOW-INC-987654&apos;,
  duration_hours: 1
};
console.log(activate(request, policy));
`}&lt;/p&gt;
&lt;p&gt;The function is mechanical and short for a reason. Every PIM gate is independently expressible, the policy is a record, the request is a record, and the active-assignment output is itself a record the system can audit. The complexity of PIM, such as it is, lives in the surrounding infrastructure -- the directory, the audit log, Conditional Access, the alert engine -- not in the gate itself.&lt;/p&gt;
&lt;h3&gt;The Azure-resource five-minute floor&lt;/h3&gt;
&lt;p&gt;One operational detail belongs here.Azure resource role assignments under PIM-for-Azure-Resources carry an additional latency floor: an Azure resource role assignment cannot be made for a duration of less than five minutes and cannot be removed within five minutes of being created [@ms-learn-pim-resource-roles]. This is the rare place where the cloud control plane exposes a hard minimum-time bound in its assignment-state machine, and it shapes the lower limit of any tightening strategy on Azure RBAC scopes.&lt;/p&gt;
&lt;p&gt;Activation is the per-event control. But what about the standing posture across the tenant -- the eligibility surface, the drift you did not notice, the assignment configuration in places PIM does not reach by default? For that, you need access reviews, and you need to push the eligible/active primitive beyond the original twenty-eight built-in directory roles.&lt;/p&gt;
&lt;h2&gt;6. Beyond Directory Roles: Extending Eligible and Active Across Four Boundaries&lt;/h2&gt;
&lt;p&gt;PIM at GA in September 2016 covered roughly twenty-eight built-in Entra directory roles. Everything else -- Azure RBAC, security groups, partner-tenant delegation, the Conditional Access activation event -- was still single-state and permanent-active. The next nine years of PIM history are the story of closing those four boundaries, one at a time.&lt;/p&gt;

flowchart TD
    Core[&quot;Two-state assignment object, 2016&quot;]
    Core --&amp;gt; Azure[&quot;PIM for Azure Resources, 2017-2019, RBAC at four scopes&quot;]
    Core --&amp;gt; Groups[&quot;PIM for Groups, GA October 2023, membership and ownership&quot;]
    Core --&amp;gt; Partner[&quot;GDAP May 2022 plus Azure Lighthouse eligible authorizations&quot;]
    Core --&amp;gt; CA[&quot;PIM with Conditional Access authentication context, GA October 2023&quot;]
&lt;h3&gt;Boundary 1: PIM for Azure Resources&lt;/h3&gt;
&lt;p&gt;Between 2017 and 2019, Microsoft extended the eligible-versus-active model from Entra directory roles to Azure RBAC. The extension covers four scopes -- management group, subscription, resource group, and individual resource -- and supports both built-in roles (Owner, Contributor, User Access Administrator, and the security roles) and custom roles [@ms-learn-pim-resource-roles].&lt;/p&gt;
&lt;p&gt;The non-obvious operational property of PIM-for-Azure-Resources is that &lt;strong&gt;role settings do not inherit down the RBAC hierarchy&lt;/strong&gt;. A policy you tighten on Owner at the management-group scope does not automatically flow down to Owner on subscriptions, resource groups, or resources beneath it. Each (role, scope) pair is its own policy slot, and each must be configured.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Configure activation policies per role per scope explicitly across the management-group, subscription, resource-group, and resource hierarchy. A tightening at the management-group scope does not flow to subscriptions beneath it. The most common operational defect in mature PIM tenants is the unconfigured policy at a downstream scope, leaving a wide-open activation surface under what looked like a hardened parent.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Boundary 2: PIM for Groups&lt;/h3&gt;
&lt;p&gt;The PIM-for-Groups timeline is three distinct events. In August 2020, Microsoft previewed the feature under its original name, &quot;Privileged Access Groups,&quot; and limited the preview scope to role-assignable security groups [@simons-2020-aug]. In January 2023, Microsoft renamed the feature to &quot;Privileged Identity Management for Groups&quot; in the Entra admin centre; the underlying eligible/active model was unchanged [@ms-learn-pim-for-groups]. In October 2023, more than three years after the preview, PIM for Groups reached general availability with a broader scope -- role-assignable security groups (carried forward), non-role-assignable security groups (newly supported), and Microsoft 365 groups (newly supported), with JIT for both membership and ownership [@ms-techcommunity-pim-groups-ca-ga-2023], [@ms-learn-pim-for-groups], [@ms-learn-pim-groups-role-settings].The three events span more than three years and should not be conflated. August 2020: preview of &quot;Privileged Access Groups,&quot; role-assignable security groups only [@simons-2020-aug]. January 2023: rename to &quot;PIM for Groups&quot;; same scope and model [@ms-learn-pim-for-groups]. October 2023: general availability with the broader scope (non-role-assignable security groups plus M365 groups), and JIT for both membership and ownership [@ms-techcommunity-pim-groups-ca-ga-2023]. Two structural exclusions persist throughout: dynamic-membership groups and groups synchronized from on-premises Active Directory [@ms-learn-pim-for-groups]. The scope is broad: any Entra security group and any Microsoft 365 group, except dynamic-membership groups and on-premises-synced groups, can be PIM-enabled [@ms-learn-pim-for-groups].&lt;/p&gt;
&lt;p&gt;The interesting design choice is that PIM-for-Groups gates &lt;strong&gt;two distinct surfaces per group&lt;/strong&gt;: membership and ownership. The two surfaces each get their own activation policy [@ms-learn-pim-groups-role-settings].&lt;/p&gt;

The extension of PIM eligible/active assignment to Entra security groups and Microsoft 365 groups. Originally previewed in August 2020 as &quot;Privileged Access Groups&quot; (role-assignable security groups only) [@simons-2020-aug]; renamed to &quot;PIM for Groups&quot; in January 2023 [@ms-learn-pim-for-groups]; reached general availability in October 2023 with the broader scope (role-assignable security groups, non-role-assignable security groups, and M365 groups), with JIT for both membership and ownership [@ms-techcommunity-pim-groups-ca-ga-2023]. Excludes dynamic-membership groups and groups synchronized from on-premises environments [@ms-learn-pim-for-groups], [@ms-learn-pim-groups-role-settings].

A group owner can add members. A privileged access group whose membership is PIM-gated but whose ownership is permanent-active offers an unmediated elevation path: a compromised owner adds themselves as a member, bypassing the membership gate they would have had to activate. PIM-for-Groups gates both surfaces because gating membership without gating ownership is a one-bypass-step elevation. The two policies are independent; both must be set.
&lt;h3&gt;Boundary 3: Partner tenants -- GDAP and Azure Lighthouse&lt;/h3&gt;
&lt;p&gt;Until 2022, the Microsoft partner channel -- Cloud Solution Providers and Managed Service Providers -- worked through a model called &lt;strong&gt;Delegated Admin Privileges (DAP)&lt;/strong&gt;, in which the partner held standing Global Administrator on every customer tenant they touched. The Nobelium supply-chain attack tradition of 2020-2021 made the structural risk of that posture unignorable [@cisa-aa20-352a]: one compromise of one partner credential meant Global Administrator across hundreds or thousands of customer tenants simultaneously.&lt;/p&gt;
&lt;p&gt;In May 2022, Microsoft introduced &lt;strong&gt;Granular Delegated Admin Privileges (GDAP)&lt;/strong&gt; [@ms-learn-gdap], [@crayon-gdap]. GDAP replaces the standing-GA pattern with time-bound (one to seven-hundred-thirty days) and role-scoped delegation between partner and customer tenants. Microsoft Learn&apos;s framing makes the design explicit: &lt;em&gt;&quot;GDAP is a security feature that provides partners with least-privileged access following the Zero Trust cybersecurity protocol. It lets partners configure granular and time-bound access to their customers&apos; workloads in production and sandbox environments. Customers must explicitly grant the least-privileged access to their partners&quot;&lt;/em&gt; [@ms-learn-gdap].&lt;/p&gt;

The May 2022 Microsoft Partner Center capability that replaces legacy DAP&apos;s standing-Global-Administrator-on-every-customer-tenant pattern with time-bound (one to seven-hundred-thirty days) and role-scoped delegation between partner and customer tenants. GDAP is the partner-tenant analogue of PIM eligible assignment [@ms-learn-gdap].
&lt;p&gt;The Azure plane has a parallel construct. &lt;strong&gt;Azure Lighthouse eligible authorizations&lt;/strong&gt;, introduced alongside GDAP, extend PIM-for-Azure-Resources eligibility across the tenant boundary [@ms-learn-lighthouse-eligible]. The customer (not the partner) controls the PIM policy on the delegated authorization. One important exception: service principals cannot use eligible authorizations, because there is currently no way for a service principal to elevate its access [@ms-learn-lighthouse-eligible]. The application-identity gap we reach in section 9 reaches into Lighthouse too.&lt;/p&gt;
&lt;h3&gt;Boundary 4: PIM and Conditional Access authentication context&lt;/h3&gt;
&lt;p&gt;The October 2023 GA wave closed the activation-gate-versus-sign-in-gate gap. Before October 2023, Conditional Access could gate sign-in into the tenant, but it could not gate the activation event itself. After October 2023, an authentication-context-tagged Conditional Access policy can target activation specifically [@ms-techcommunity-pim-groups-ca-ga-2023]. A policy of the form &lt;em&gt;&quot;activation of any control-plane role requires a compliant device and a fresh MFA challenge&quot;&lt;/em&gt; becomes expressible without third-party tooling [@ms-learn-pim-change-default-settings].&lt;/p&gt;
&lt;h3&gt;The retirement of Tier-0, Tier-1, Tier-2&lt;/h3&gt;
&lt;p&gt;The umbrella framing has also shifted. Microsoft&apos;s 2014 Tier-0 / Tier-1 / Tier-2 model is being progressively retired in favour of the &lt;strong&gt;Enterprise Access Model (EAM)&lt;/strong&gt;, which uses control plane, management plane, and data/workload plane as the structural divisions [@ms-learn-eam]. EAM is cloud-native where Tier-0/1/2 was on-premises-centric. Microsoft Learn states the mapping: &lt;em&gt;&quot;Tier 0 expands to become the control plane and addresses all aspects of access control&quot;&lt;/em&gt;, and &lt;em&gt;&quot;what was tier 1 is now split into the following areas: Management plane ... Data/Workload plane&quot;&lt;/em&gt; [@ms-learn-eam].&lt;/p&gt;

The post-2021 Microsoft reference architecture that replaces the Tier-0/Tier-1/Tier-2 administrative model with a plane-based division: control plane, management plane, and data/workload plane. EAM is cloud-native and zero-trust-friendly where Tier-0/1/2 was on-premises-centric [@ms-learn-eam]. Microsoft&apos;s RaMP -- the Rapid Modernization Plan -- is the post-2018 deployment roadmap that operationalizes EAM [@ms-docs-github-ramp].
&lt;p&gt;The retirement is partial. The practitioner audience still uses Tier-0/1/2 more often than EAM in day-to-day language. The Microsoft Learn page for Securing Privileged Access explicitly cross-references both [@ms-learn-spa-overview].&lt;/p&gt;
&lt;p&gt;Coverage is one half of the story. The other half is detection. What does PIM do when someone in the Privileged Role Administrator role simply assigns Global Administrator to a user directly through Microsoft Graph, bypassing the activation workflow entirely?&lt;/p&gt;
&lt;h2&gt;7. The Detection Layer: Six PIM Alerts and the Assignment-Bypass Class&lt;/h2&gt;
&lt;p&gt;PIM gates activation. The first question every adversary thinks of, and every architect should think of next, is: what about the assignment itself? What happens when someone in the Privileged Role Administrator role just creates a permanent-active Global Administrator assignment directly, skipping the eligible-to-active workflow entirely?&lt;/p&gt;
&lt;p&gt;The answer is the article&apos;s second aha moment, and it is deliberately surprising.&lt;/p&gt;
&lt;h3&gt;The six PIM Alerts&lt;/h3&gt;
&lt;p&gt;Microsoft Learn documents seven named alerts in the PIM Alerts surface for Microsoft Entra roles [@ms-learn-pim-alerts]. Six of them are behavioural detections; the seventh is a licensing-precondition alert that fires when the tenant lacks the appropriate license.The seventh alert, named &quot;The organization doesn&apos;t have Microsoft Entra ID P2 or Microsoft Entra ID Governance,&quot; is a low-severity licensing-precondition alert. The &quot;six PIM Alerts&quot; framing in this article refers to the six behavioural alerts; the licensing alert is structurally distinct. The six behavioural alerts, with the canonical names verbatim from the documentation, are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Alert (verbatim)&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;What it detects&lt;/th&gt;
&lt;th&gt;Configurable threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;There are too many Global Administrators&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Tenant exceeds a tunable count and percentage of standing GAs&lt;/td&gt;
&lt;td&gt;Minimum count 2-100 and percentage 0-100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Roles are being assigned outside of Privileged Identity Management&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A privileged role assignment was created via Microsoft Graph or the classic admin centre without going through PIM&lt;/td&gt;
&lt;td&gt;None (binary)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Roles are being activated too frequently&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Post-hoc activation-frequency anomaly&lt;/td&gt;
&lt;td&gt;Activation count and time window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Administrators aren&apos;t using their privileged roles&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Staleness on activation; eligible assignment unused&lt;/td&gt;
&lt;td&gt;0-100 day threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Roles don&apos;t require multifactor authentication for activation&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Configuration drift on the per-role activation policy&lt;/td&gt;
&lt;td&gt;None (binary on role policy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Potential stale accounts in a privileged role&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Sign-in staleness on a privileged principal&lt;/td&gt;
&lt;td&gt;1-365 day threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The third row -- &quot;Roles are being assigned outside of Privileged Identity Management&quot; -- is the load-bearing one. Microsoft Learn rates it &lt;strong&gt;High&lt;/strong&gt; severity because it is the alert that fires when somebody routed around PIM entirely [@ms-learn-pim-alerts]. The verbatim documentation reads: &lt;em&gt;&quot;Privileged role assignments made outside of Privileged Identity Management aren&apos;t properly monitored and might indicate an active attack&quot;&lt;/em&gt; [@ms-learn-pim-alerts].&lt;/p&gt;

The High-severity PIM Alert &quot;Roles are being assigned outside of Privileged Identity Management.&quot; It fires when a privileged role is assigned via a path other than PIM -- typically via Microsoft Graph, the classic admin centre assignment surface, or PowerShell. The alert is detective. It fires after the assignment is created [@ms-learn-pim-alerts].
&lt;h3&gt;Detective, not preventive -- and why&lt;/h3&gt;
&lt;p&gt;Read the definition again. The alert fires &lt;em&gt;after&lt;/em&gt; the assignment is created. PIM does not block direct assignments outside its workflow.&lt;/p&gt;
&lt;p&gt;For most architects this lands hard. The reasonable next thought is &quot;if PIM does not block the bypass, what is the point?&quot; Sit with that thought, then read the design rationale.&lt;/p&gt;
&lt;p&gt;The Microsoft Graph endpoints that allow direct role assignment are the integration surface every legitimate administrative tool uses. Identity Governance products use them. CI/CD identity provisioning scripts use them. Break-glass automations use them. Microsoft&apos;s own admin centres use them in some configurations. The customer-side tools that scan, audit, remediate, and provision against the tenant use them. A preventive block on direct assignment would break every one of those integrations. It would also break PIM itself; the eligible-to-active materialization step is a write to the same assignment surface.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PIM does not block direct role assignments outside its workflow because blocking would break the Microsoft Graph integration surface every legitimate administrative tool uses. The High-severity assignment-bypass alert is detective: it fires after the assignment is created. Customers who need preventive blocking layer a separate Conditional Access policy on the Graph endpoint, an Azure Policy at the management-group scope, or an entitlement-management workflow on top of PIM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is Aha #2. The reader who walked in expecting PIM to be a &quot;deny direct assignments&quot; product walks out understanding why the design says &quot;alert loudly via High severity, then let the customer layer preventive controls based on their tooling estate.&quot; The trade-off is named, not hidden.&lt;/p&gt;
&lt;h3&gt;The 1000-notification ceiling and the SIEM-side correlation&lt;/h3&gt;
&lt;p&gt;One operational footnote and one wider observation. The notification fan-out has a hard cap: &lt;em&gt;&quot;The maximum number of notifications sent per one event is 1000. If the number of recipients exceeds 1000, only the first 1000 recipients will receive an email notification&quot;&lt;/em&gt; [@ms-learn-pim-alerts]. Very large tenants whose privileged groups exceed the cap should not rely on email-notification fan-out alone.The detection layer beyond PIM Alerts is Microsoft Sentinel UEBA, which builds dynamic behavioural profiles for users, hosts, IP addresses, applications, and other entities and emits anomaly scores against &lt;code&gt;AuditLogs&lt;/code&gt; operations including role-eligibility additions and activations [@ms-learn-sentinel-ueba]. Sentinel UEBA is the closest 2026 Microsoft-shipped activation-anomaly-scoring surface; it is detective SIEM correlation, not synchronous gating.&lt;/p&gt;
&lt;p&gt;The wider observation is that the PIM detection layer is one piece of a larger pipeline. PIM Alerts give you the High-severity assignment-bypass detection. Microsoft Sentinel UEBA gives you per-user behavioural-anomaly scoring against the audit-log events [@ms-learn-sentinel-ueba]. Entra ID Protection gives you sign-in-risk and user-risk classifications for the principal whose token was used. The mature 2026 deployment correlates all three; the assignment-bypass alert is the floor of that pipeline, not the ceiling.&lt;/p&gt;
&lt;p&gt;Microsoft solved the JIT-admin problem with a two-state assignment object, four extension surfaces, and a six-alert detection layer. Did the rest of the industry agree? Look at what AWS and Google bet on, and at the third-party vault market that predates both.&lt;/p&gt;
&lt;h2&gt;8. Competing Architectures: AWS Sessions, GCP Bindings, and the Vault Model&lt;/h2&gt;
&lt;p&gt;Microsoft bet on a two-state assignment object. The rest of the industry placed different bets.&lt;/p&gt;
&lt;p&gt;AWS bet on the session credential. Google bet on the conditional binding. The third-party PAM market bet on the vault. HashiCorp bet on the ephemeral credential. Each architecture is a different answer to one question: &lt;em&gt;what should be the bounded unit of privilege?&lt;/em&gt; PIM bounds the assignment state; AWS bounds the session; GCP bounds the binding; CyberArk and Vault bound the credential. The methods are architecturally distinct, and they coexist in real estates more often than they compete.&lt;/p&gt;
&lt;h3&gt;AWS: bound the session&lt;/h3&gt;
&lt;p&gt;AWS IAM Identity Center plus the Security Token Service &lt;code&gt;AssumeRole&lt;/code&gt; API bound the &lt;em&gt;session&lt;/em&gt;, not the &lt;em&gt;assignment&lt;/em&gt;. Permanent role-bindings -- permission sets attached to identities -- are themselves standing. The temporary part is the session that materializes when the identity calls &lt;code&gt;AssumeRole&lt;/code&gt;. AWS documents this directly: &lt;em&gt;&quot;Temporary security credentials are short-term, as the name implies. They can be configured to last for anywhere from a few minutes to several hours. After the credentials expire, AWS no longer recognizes them or allows any kind of access from API requests made with them&quot;&lt;/em&gt; [@aws-temp-creds].&lt;/p&gt;
&lt;p&gt;The session lifecycle is concrete. &lt;code&gt;AssumeRole&lt;/code&gt; returns an access key, a secret key, and a session token, with a minimum fifteen-minute and a maximum twelve-hour session duration; the API operation default is one hour [@aws-roles-use]. IAM Identity Center permission sets ship with a one-hour default and a one-to-twelve-hour configurable range [@aws-sessionduration].&lt;/p&gt;

The AWS Security Token Service API by which a principal materializes a time-bounded session credential -- access key, secret key, session token -- from a permanent role-binding. The session is the ephemeral artifact; the binding is permanent [@aws-temp-creds], [@aws-roles-use].
&lt;p&gt;The AWS approach has clear strengths in multi-account AWS Organizations and in programmatic access. It is also the natural fit for any workload that needs short-lived credentials. The gaps relative to PIM: no built-in approval workflow, no equivalent of the PIM Alerts surface, and no eligible-versus-active distinction on the role-binding itself. A standing AssumeRole grant is, structurally, standing privilege; what is bounded is the session that consumes it.&lt;/p&gt;
&lt;h3&gt;Google Cloud: bound the binding&lt;/h3&gt;
&lt;p&gt;Google Cloud IAM took a different route. &lt;strong&gt;IAM Conditional Bindings&lt;/strong&gt; let an allow policy include a Common Expression Language predicate that is evaluated at request time. The canonical temporal pattern is &lt;code&gt;request.time &amp;lt; timestamp(...)&lt;/code&gt;, which expires the binding at a wall-clock instant [@gcp-conditions]. There is a practical ceiling of one hundred conditional bindings per allow policy.&lt;/p&gt;
&lt;p&gt;On top of conditional bindings, Google launched &lt;strong&gt;Privileged Access Manager (PAM)&lt;/strong&gt; in public preview in May 2024 [@gcp-iam-release-notes], [@gcp-pam]. PAM adds the entitlement-and-grant workflow that PIM ships natively: eligible principals, eligible roles, max duration, justification, approvers, and notifications, with grant duration enforced by the underlying conditional binding revocation. Audit-event correlation is documented in a separate page [@gcp-pam-audit].&lt;/p&gt;

A Google Cloud IAM role binding that includes a Common Expression Language predicate evaluated at request time. The most common temporal pattern, `request.time &amp;lt; timestamp(...)`, expires the binding at a wall-clock instant; Google Cloud Privileged Access Manager layers an entitlement-and-grant workflow on top [@gcp-conditions], [@gcp-pam].
&lt;p&gt;The GCP approach is the closest hyperscaler analogue to PIM&apos;s eligible/active model in architecture, but the PAM productization shipped in preview in May 2024 [@gcp-iam-release-notes] -- nearly a decade after Azure AD PIM&apos;s 2016 GA -- and the alert and detection surfaces are correspondingly less mature.&lt;/p&gt;
&lt;h3&gt;The third-party vault: CyberArk, BeyondTrust, Delinea&lt;/h3&gt;
&lt;p&gt;The longest-standing answer is the one the third-party PAM market built. CyberArk, BeyondTrust, and Delinea -- all three 2024 Gartner Magic Quadrant Leaders for Privileged Access Management [@cyberark-press-2024], [@beyondtrust-press-2024], [@delinea-press-2024] -- bound the &lt;em&gt;credential&lt;/em&gt;, not the assignment or the session. The credential exists permanently in the vault; access to the credential is bounded by session brokering, periodic password rotation, and full session recording.&lt;/p&gt;
&lt;p&gt;The vault model has structural strengths PIM&apos;s role-assignment-state model cannot match. The vault covers heterogeneous estates that include Windows, Linux, network devices, databases, mainframes, and OT/SCADA appliances -- every system whose credentials cannot be re-architected to a cloud-IAM eligible-active object. Vault-and-broker products provide session recording for SOX and PCI-DSS evidence collection, and they integrate with credential-rotation workflows for legacy vendor appliances whose hard-coded credentials cannot be eliminated.&lt;/p&gt;
&lt;p&gt;Most large enterprises run &lt;em&gt;both&lt;/em&gt; Entra PIM (for Entra and Azure role assignments) and a third-party PAM product (for SSH, on-premises service accounts, database passwords, network devices). The two markets are complements more than substitutes.&lt;/p&gt;
&lt;h3&gt;HashiCorp Vault and OpenBao: bound the credential&apos;s lifetime&lt;/h3&gt;
&lt;p&gt;HashiCorp Vault took the credential-bounded idea and made it ephemeral through &lt;strong&gt;dynamic secrets&lt;/strong&gt;: a credential materialized on demand by Vault for a configured backend (a database, a cloud IAM, a PKI), returned with a lease and TTL, and revoked at the backend when the lease expires [@vault-databases]. The OpenBao fork, governed under the Linux Foundation, preserves the same dynamic-credential semantics [@openbao].OpenBao was created in late 2023 after HashiCorp moved Vault from the open-source MPL to the Business Source License. The Linux Foundation announced on April 30, 2024 that OpenBao would join &lt;strong&gt;LF Edge&lt;/strong&gt; as one of four new projects (alongside EdgeLake, InfiniEdgeAI, and InstantX) at the Open Networking and Edge (ONE) Summit [@lfedge-openbao-2024]. The dynamic-secret primitive -- &quot;create a credential, hand it out, revoke it at lease expiry&quot; -- is preserved on both code lines.&lt;/p&gt;

A credential materialized by Vault on demand for a configured backend -- database, cloud IAM, or PKI -- returned with a lease ID and TTL; at lease expiry Vault revokes the credential at the backend. The canonical 2026 open-source primitive for replacing hard-coded application credentials [@vault-databases].
&lt;p&gt;The Vault story matters for our purposes because it is the strongest 2026 coverage of the &lt;em&gt;application-identity surface&lt;/em&gt; -- dynamic database credentials, Kubernetes service-account tokens, cloud-IAM short-lived credentials. PIM does not cover that surface today; Vault does. This previews the open boundary in section 9.&lt;/p&gt;
&lt;h3&gt;What is bound, in one comparison table&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;What is bound&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Default duration&lt;/th&gt;
&lt;th&gt;Approval workflow&lt;/th&gt;
&lt;th&gt;Detection layer&lt;/th&gt;
&lt;th&gt;Partner tenant&lt;/th&gt;
&lt;th&gt;Application identities&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Entra PIM&lt;/td&gt;
&lt;td&gt;Assignment state&lt;/td&gt;
&lt;td&gt;eligible -&amp;gt; active transition with policy gates&lt;/td&gt;
&lt;td&gt;1h (Global Admin)&lt;/td&gt;
&lt;td&gt;Built-in approver routing&lt;/td&gt;
&lt;td&gt;Six behavioural PIM Alerts plus Sentinel UEBA&lt;/td&gt;
&lt;td&gt;GDAP + Lighthouse&lt;/td&gt;
&lt;td&gt;Not yet (open boundary)&lt;/td&gt;
&lt;td&gt;Entra ID P2 or Entra ID Governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS IAM Identity Center + STS&lt;/td&gt;
&lt;td&gt;Session credential&lt;/td&gt;
&lt;td&gt;AssumeRole returns access/secret/session token&lt;/td&gt;
&lt;td&gt;1h&lt;/td&gt;
&lt;td&gt;Not built-in&lt;/td&gt;
&lt;td&gt;Not equivalent to PIM Alerts&lt;/td&gt;
&lt;td&gt;Not directly comparable&lt;/td&gt;
&lt;td&gt;Strong (short-lived creds native)&lt;/td&gt;
&lt;td&gt;Included in AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCP IAM + PAM&lt;/td&gt;
&lt;td&gt;Policy binding&lt;/td&gt;
&lt;td&gt;CEL predicate plus entitlement-and-grant&lt;/td&gt;
&lt;td&gt;Per entitlement&lt;/td&gt;
&lt;td&gt;Built-in via PAM&lt;/td&gt;
&lt;td&gt;Audit events plus Cloud Audit Logs&lt;/td&gt;
&lt;td&gt;Cross-org via folders&lt;/td&gt;
&lt;td&gt;Service-account impersonation&lt;/td&gt;
&lt;td&gt;Included in GCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CyberArk/BeyondTrust/Delinea&lt;/td&gt;
&lt;td&gt;Credential knowledge&lt;/td&gt;
&lt;td&gt;Vault stores, broker hands out, rotates&lt;/td&gt;
&lt;td&gt;Per session policy&lt;/td&gt;
&lt;td&gt;Built-in approver routing&lt;/td&gt;
&lt;td&gt;Session recording, full SIEM integration&lt;/td&gt;
&lt;td&gt;Per-tenant deployment&lt;/td&gt;
&lt;td&gt;Coverage via shared accounts&lt;/td&gt;
&lt;td&gt;Per-seat commercial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HashiCorp Vault / OpenBao&lt;/td&gt;
&lt;td&gt;Credential lifetime&lt;/td&gt;
&lt;td&gt;Lease-based revocation, dynamic secrets&lt;/td&gt;
&lt;td&gt;Per backend, per lease&lt;/td&gt;
&lt;td&gt;Optional plugins&lt;/td&gt;
&lt;td&gt;Audit log; lease events&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Strong (dynamic secrets)&lt;/td&gt;
&lt;td&gt;Open source / commercial&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The five methods occupy four positions on the &quot;what is bound&quot; axis: &lt;strong&gt;assignment-state&lt;/strong&gt; (PIM), &lt;strong&gt;session-credential&lt;/strong&gt; (AWS), &lt;strong&gt;policy-binding&lt;/strong&gt; (GCP), and &lt;strong&gt;knowledge-of-credential&lt;/strong&gt; (CyberArk and Vault). The methods are architecturally distinct, and the right enterprise answer in heterogeneous estates is some composition of more than one.&lt;/p&gt;
&lt;p&gt;PIM is the most mature JIT-admin product in the cloud, and it has the most complete coverage of the user-principal surface. The remaining gaps are not about catching up to the competitors; they are about a class of identity the eligible/active model was never designed to gate.&lt;/p&gt;
&lt;h2&gt;9. What the JIT-Admin Pattern Does NOT Close&lt;/h2&gt;
&lt;p&gt;For all the architectural elegance of the two-state assignment object, PIM does not close the JIT-admin problem. It closes a sub-problem, very well, and leaves five structural limits an honest treatment must name.&lt;/p&gt;
&lt;h3&gt;9.1 Standing eligibility is itself standing privilege&lt;/h3&gt;
&lt;p&gt;PIM bounds the &lt;em&gt;active&lt;/em&gt; duration. It does not bound the &lt;em&gt;eligibility&lt;/em&gt; duration. A user with a permanent-eligible Global Administrator assignment is one &lt;code&gt;activate()&lt;/code&gt; call away from the role&apos;s permissions for the next hour. If that user has been phished -- credential plus MFA bypass via a session-cookie capture, say -- the attacker can satisfy the gates. The MFA challenge passes. The justification text is whatever the attacker types. The approval, if required, routes to the legitimate approver, who may approve a legitimate-looking request that actually came from the attacker.&lt;/p&gt;
&lt;p&gt;PIM produces an audit-log record of every step. It does not produce a structural impossibility. Eligibility is itself a security-critical property of the identity, and standing eligibility is the modern analogue of standing membership: a long-lived relationship between principal and role that a successful credential compromise can exercise.&lt;/p&gt;
&lt;h3&gt;9.2 Approver collusion&lt;/h3&gt;
&lt;p&gt;The approval gate is two-phishee resistant only when the requester and approver are independently compromisable. Two-phishee collusion -- the requester and the approver are the same adversary, or two adversaries cooperating -- defeats the workflow at the mechanism layer. The usual mitigations raise the bar: named approvers rather than approver groups (which can be compromised at the group level), CA-gated approval actions, and four-eyes alternatives. None close the class.&lt;/p&gt;
&lt;h3&gt;9.3 The application-identity gap&lt;/h3&gt;
&lt;p&gt;This is the article&apos;s heaviest limit, and it deserves the most space.&lt;/p&gt;
&lt;p&gt;PIM&apos;s eligible-active state machine is currently defined over &lt;code&gt;principal in (user | group)&lt;/code&gt;. Service principals, managed identities, and OAuth consent grants do not flow through PIM activation. Their role assignments are permanent and active by default, and there is no eligible category that applies to them. Microsoft Learn&apos;s documentation for Workload ID Premium and Conditional Access for workload identities makes this explicit: ID Protection workload-identity risk detections cover service principals in single-tenant, non-Microsoft SaaS, and multitenant apps, but &lt;em&gt;&quot;Managed Identities aren&apos;t currently in scope&quot;&lt;/em&gt; [@ms-learn-workload-identity-risk]. Conditional Access for workload identities applies similarly only to service principals owned by the organization, and CA policies &lt;em&gt;&quot;assigned to a group that contains a service principal are not enforced for that service principal&quot;&lt;/em&gt; [@ms-learn-ca-workload-identity].&lt;/p&gt;
&lt;p&gt;Andy Robbins&apos;s three-part &lt;em&gt;Managed Identity Attack Paths&lt;/em&gt; series, published June 6-8, 2022 on the SpecterOps blog, is the canonical demonstration of how this gap is exploited [@robbins-mip-part1], [@robbins-mip-part2], [@robbins-mip-part3]. The mechanism is direct. An Azure compute resource -- an Automation Account [@robbins-mip-part1], a Logic App [@robbins-mip-part2], or a Function App [@robbins-mip-part3] -- carries an attached managed identity. The managed identity holds standing role assignments at whatever scope the operator granted, often Owner or Contributor on a subscription.&lt;/p&gt;
&lt;p&gt;From inside the resource, any code can fetch an OAuth access token for the managed identity by calling the Azure Instance Metadata Service endpoint at &lt;code&gt;http://169.254.169.254/metadata/identity/oauth2/token&lt;/code&gt;. No human in the loop. No MFA challenge. No PIM activation. The audit log records a service-principal token issuance, not an alice-clicked-Activate event.&lt;/p&gt;

Managed Identity assignments are an extremely effective security control... But Managed Identities introduce a new problem: they can quickly create identity-based attack paths in Azure that may lead to escalation of privilege opportunities. -- Andy Robbins, *Managed Identity Attack Paths, Part 1: Automation Accounts*, June 6, 2022 [@robbins-mip-part1]

An Azure-managed service principal whose credentials are issued and rotated by Azure itself. The underlying Azure resource (a VM, App Service, Function App, Logic App, AKS cluster) retrieves the OAuth access token via the Instance Metadata Service endpoint. Managed identities are not currently in scope for PIM activation; their role assignments are permanent and active [@ms-learn-managed-identities-overview].

The Azure Instance Metadata Service endpoint at `http://169.254.169.254/metadata/identity/oauth2/token`, a link-local non-routable address reachable only from inside the Azure resource itself, that returns an OAuth 2.0 access token for the attached managed identity. The address is the credential: any process running on the resource can fetch the token without storing or presenting any secret.

sequenceDiagram
    autonumber
    participant Attacker
    participant FunctionApp as Compromised Function App
    participant IMDS as IMDS endpoint 169.254.169.254
    participant ARM as Azure Resource Manager
    participant PIMUnused as PIM activation (unused)
    Attacker-&amp;gt;&amp;gt;FunctionApp: Code execution via supply-chain or vuln
    FunctionApp-&amp;gt;&amp;gt;IMDS: GET /metadata/identity/oauth2/token
    IMDS--&amp;gt;&amp;gt;FunctionApp: OAuth access token for managed identity
    FunctionApp-&amp;gt;&amp;gt;ARM: Action as Owner on subscription
    ARM--&amp;gt;&amp;gt;FunctionApp: Action succeeds
    Note over PIMUnused,Attacker: No human, no MFA, no activation, no PIM audit
&lt;p&gt;MITRE ATT&amp;amp;CK maps the class explicitly. &lt;strong&gt;T1078.004 -- Valid Accounts: Cloud Accounts&lt;/strong&gt; cites Robbins&apos;s Part 1 as primary reference for the managed-identity case [@mitre-t1078-004]. The page reads: &lt;em&gt;&quot;In Azure environments, adversaries may target Azure Managed Identities, which allow associated Azure resources to request access tokens. By compromising a resource with an attached Managed Identity, such as an Azure VM, adversaries may be able to Steal Application Access Tokens to move laterally across the cloud environment&quot;&lt;/em&gt; [@mitre-t1078-004].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;T1548.005 -- Temporary Elevated Cloud Access&lt;/strong&gt; explicitly names PIM as an instance of the JIT-access pattern adversaries abuse: &lt;em&gt;&quot;Many cloud environments allow administrators to grant user or service accounts permission to request just-in-time access to roles... Just-in-time access is a mechanism for granting additional roles to cloud accounts in a granular, temporary manner&quot;&lt;/em&gt; [@mitre-t1548-005].&lt;/p&gt;

T1548.005 (Temporary Elevated Cloud Access) lists Microsoft&apos;s *Approve just-in-time access requests* documentation as citation [1] of the technique, recognizing PIM as a canonical implementation of the JIT-access pattern adversaries abuse [@mitre-t1548-005]. Being named in the ATT&amp;amp;CK framework is, in the security domain, the most explicit acknowledgement an adversary model can give a defensive product.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Three anchors to walk away with: Andy Robbins&apos;s June 2022 &lt;em&gt;Managed Identity Attack Paths&lt;/em&gt; series [@robbins-mip-part1], [@robbins-mip-part2], [@robbins-mip-part3]; MITRE ATT&amp;amp;CK T1078.004 citing Robbins as primary [@mitre-t1078-004]; the IMDS endpoint at &lt;code&gt;169.254.169.254&lt;/code&gt; as the technical mechanism [@ms-learn-managed-identities-overview]. If your tenant has any managed identity with Owner or User Access Administrator at a subscription scope, you have an unmediated bypass path around PIM until that role assignment is tightened.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9.4 The assignment-bypass is detective, not preventive&lt;/h3&gt;
&lt;p&gt;The High-severity assignment-bypass alert documented in §7 is detective by design (see Aha #2). The structural limit it leaves open is that preventive blocking is not the PIM product&apos;s default: customers who want it layer a Conditional Access policy on the Microsoft Graph endpoint or an Azure Policy at the management-group scope [@ms-learn-azure-policy], accepting that some legitimate Graph integration may need an exception.&lt;/p&gt;
&lt;h3&gt;9.5 Customer-owned PIM policy in CSP and Lighthouse scenarios&lt;/h3&gt;
&lt;p&gt;In the partner-managed case, the customer (not the partner) controls the PIM policy on a delegated authorization [@ms-learn-lighthouse-eligible]. This is the right place to put control, but it is also the place misconfiguration is most common. A customer whose Lighthouse eligible authorization is set with permissive activation policies (no MFA, no approval, large maximum duration) has an unmediated partner activation surface, and the partner cannot tighten the customer-side policy. The MSP-managed case is the operational gotcha most frequently raised at PIM-deployment review boards.&lt;/p&gt;
&lt;h3&gt;Aha #3: The gap is a data-model problem, not a patchable defect&lt;/h3&gt;
&lt;p&gt;This is the third aha moment, and it lands differently from the first two.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The application-identity gap is not a backlog item. Extending the eligible-active state machine from &lt;code&gt;principal in (user | group)&lt;/code&gt; to &lt;code&gt;principal in (user | group | service principal | managed identity | OAuth consent grant)&lt;/code&gt; is a data-model extension that would require changes to the role-assignment object schema, the Microsoft Graph role-management endpoints, the PIM evaluation pipeline, the audit-log schema, the Sentinel detection schema, and every downstream IGA tool. The 2024+ Microsoft responses extend some controls to application identities. They do not yet introduce an eligible/active assignment-category type for application principals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft has shipped partial responses. &lt;strong&gt;Entra Workload ID Premium&lt;/strong&gt; [@ms-entra-workload-id-product] is a separate three-dollar-per-workload-identity-per-month SKU [@ms-entra-workload-id-product] that unlocks &lt;strong&gt;Conditional Access for workload identities&lt;/strong&gt; [@ms-learn-ca-workload-identity] (with the explicit managed-identity exclusion clause) and &lt;strong&gt;ID Protection workload-identity risk detections&lt;/strong&gt; [@ms-learn-workload-identity-risk]. The PIM page on access reviews documents that &lt;em&gt;&quot;Using Access Reviews for Service Principals requires a Microsoft Entra Workload ID Premium plan in addition to a Microsoft Entra ID P2 or Microsoft Entra ID Governance license&quot;&lt;/em&gt; [@ms-learn-pim-access-reviews]. Microsoft&apos;s flagship Ignite 2025 announcement was &lt;strong&gt;Microsoft Entra Agent ID&lt;/strong&gt; for AI agents [@ms-entra-ignite-2025]; the announcement is identity for AI workloads, not an eligible-active type extension for service-principal role assignments.&lt;/p&gt;
&lt;p&gt;Robbins&apos;s class is closed-form within the 2026 PIM architecture. Closing it requires a new architecture, not a patch.&lt;/p&gt;
&lt;p&gt;None of these limits is a defect. Each is a deliberate design boundary, and naming them is the academic honesty the topic deserves. The interesting question: where is active research happening, and what would closing the gap actually look like?&lt;/p&gt;
&lt;h2&gt;10. Open Problems: Where Active Research Is Happening&lt;/h2&gt;
&lt;p&gt;The five limits in section 9 are settled architectural boundaries. The open problems are different. Each is something nobody has shipped a complete solution to as of 2026, but each has named partial results and named anchors.&lt;/p&gt;
&lt;h3&gt;10.1 JIT-gating application identities&lt;/h3&gt;
&lt;p&gt;The data-model extension previewed in section 9&apos;s Aha #3 is the largest open problem in this space, and the one Microsoft is responding to most publicly.&lt;/p&gt;
&lt;p&gt;What has been tried. &lt;strong&gt;Entra Workload ID Premium&lt;/strong&gt; at three dollars per workload identity per month [@ms-entra-workload-id-product]. &lt;strong&gt;Conditional Access for workload identities&lt;/strong&gt;, which lets the tenant block service-principal sign-ins based on IP range, ID-Protection risk score, or authentication context [@ms-learn-ca-workload-identity]. &lt;strong&gt;ID Protection workload-identity risk detections&lt;/strong&gt; that flag suspicious sign-ins, leaked credentials, and admin-confirmed compromise for service principals [@ms-learn-workload-identity-risk]. &lt;strong&gt;Service-principal access reviews&lt;/strong&gt;, gated behind Workload ID Premium plus Entra ID P2 or Governance [@ms-learn-pim-access-reviews]. &lt;strong&gt;Microsoft Entra Agent ID&lt;/strong&gt;, the flagship Ignite 2025 announcement, brings first-class identity to AI agents [@ms-entra-ignite-2025] -- parallel to, but not the same as, an eligible-active type extension on application role assignments.&lt;/p&gt;

An identity used by a software workload to authenticate to other services. In Microsoft Entra ID the term encompasses application objects, service principals, and managed identities [@ms-learn-workload-identities-overview]. As of 2026, workload identities are not in scope of the eligible/active assignment-category model. The 2024+ Workload ID Premium SKU extends sign-in-time controls and risk detection to service principals, but does not yet introduce an eligible category for service-principal role assignments.
&lt;p&gt;What is the conjecture? Closing this gap requires extending the role-assignment object&apos;s &lt;code&gt;principal&lt;/code&gt; axis to include service principals, managed identities, and OAuth consent grants as first-class subjects of the eligible-active state machine. That extension would require a defined &lt;code&gt;activate()&lt;/code&gt; semantics for non-human principals -- itself the hard problem, because the canonical user activation flow assumes an interactive MFA challenge.&lt;/p&gt;
&lt;p&gt;Microsoft Learn states the difficulty bluntly: workload identities &lt;em&gt;&quot;can&apos;t perform multifactor authentication. Often have no formal lifecycle process. Need to store their credentials or secrets somewhere&quot;&lt;/em&gt; [@ms-learn-workload-identities-overview]. The non-interactive case requires either programmatic policy gates (request from this caller, from this IP range, against this entitlement) or a delegation model where a human approver supplies the gate-passing event on the workload&apos;s behalf.&lt;/p&gt;
&lt;h3&gt;10.2 Real-time activation-anomaly blocking&lt;/h3&gt;
&lt;p&gt;The PIM Alert &quot;Roles are being activated too frequently&quot; is post-hoc. It fires after the activation has already occurred and after the count crosses a threshold. The phished-but-still-authentic activation -- the attacker who supplies a valid MFA, a plausible justification, and a real ticket number -- is observationally indistinguishable from a legitimate emergency activation at the mechanism layer. The only signal that distinguishes them must come from behavioural telemetry.&lt;/p&gt;
&lt;p&gt;What has been tried. &lt;strong&gt;Microsoft Defender for Cloud Apps&lt;/strong&gt; ships an out-of-the-box user-and-entity behavioural analytics (UEBA) and machine-learning anomaly-detection layer; the documented policy weighs more than thirty risk indicators across eight risk-factor groups (risky IP, login failures, admin activity, inactive accounts, location, impossible travel, device and user agent, activity rate), with a seven-day initial learning period and a June 2025 transition to a dynamic threat-detection model [@ms-learn-dfca-anomaly]. &lt;strong&gt;Microsoft Sentinel UEBA&lt;/strong&gt; scores anomalies post-event against &lt;code&gt;AuditLogs&lt;/code&gt; operations including role-eligibility additions and activations [@ms-learn-sentinel-ueba]. &lt;strong&gt;Microsoft Defender for Identity&lt;/strong&gt; correlates on-premises and cloud sign-in patterns for behavioural-anomaly detection. Neither Sentinel UEBA nor Defender for Cloud Apps is a synchronous gate. Both are detective layers that fire after the activation event has already created consequences.&lt;/p&gt;
&lt;p&gt;The academic upper bound for what character-level and LSTM detectors achieve on adjacent tasks comes from Hendler, Kels, and Rubin&apos;s 2019 work on AMSI-based detection of malicious PowerShell code, which reports a true-positive rate of nearly 90% at a false-positive rate of less than 0.1% on the PowerShell-misuse classification problem [@arxiv-hendler-1905]. That is the ceiling a probabilistic activation-anomaly classifier could approach. It is not enough to gate synchronously without false-positive operational pain, which is why the deployed surface is post-hoc UEBA scoring rather than pre-commit blocking.&lt;/p&gt;
&lt;p&gt;The conjecture. Synchronous gating on behavioural signal at activation time would require Conditional Access (or its successor) to subscribe to an activation-event hook and consume a risk score from ID Protection, Defender for Cloud Apps, or Sentinel UEBA in the few hundred milliseconds before PIM materializes the active assignment. The architectural primitives exist; the synchronous risk-evaluation hook does not yet ship.&lt;/p&gt;
&lt;h3&gt;10.3 Hybrid-bridge JIT&lt;/h3&gt;
&lt;p&gt;A single approval workflow spanning the on-premises (MIM PAM / shadow principals) and cloud (Entra PIM) boundaries is not a shipping product. Microsoft has &lt;strong&gt;Entra Cloud Sync&lt;/strong&gt; and &lt;strong&gt;Entra Connect&lt;/strong&gt; for directory synchronization; neither bridges the &lt;em&gt;activation workflow&lt;/em&gt;. MIM 2016 is on extended support through January 9, 2029 [@ms-learn-mim-2016]; Microsoft Learn states the path forward is cloud-first PIM with on-prem AD progressively scoped down to the few resources that cannot move [@ms-learn-mim-pam-overview].&lt;/p&gt;

MIM 2016 PAM is in extended support, not active development, and Microsoft Learn explicitly states it is &quot;not recommended for new deployments in Internet-connected environments&quot; [@ms-learn-mim-pam-overview]. SP3 ships compatibility updates for SharePoint SE, Exchange SE, and SQL Server 2022 [@ms-learn-mim-2016], but the product line is in maintenance posture. The on-premises half of a hybrid-bridge JIT story requires a different architectural choice than re-investing in MIM.
&lt;h3&gt;10.4 Coverage-as-code&lt;/h3&gt;
&lt;p&gt;How do you evaluate PIM policy coverage in CI/CD for a tenant with two hundred custom Azure roles and fifty directory roles, and gate every PR that touches the role-management policies?&lt;/p&gt;
&lt;p&gt;Best partial results. &lt;strong&gt;Microsoft Cloud Security Benchmark v3 Privileged Access controls&lt;/strong&gt; (PA-1, PA-2, ...) give Boolean per-recommendation pass/fail evaluation [@ms-learn-mcsb-v3-pa] -- close, but per-recommendation Boolean rather than composable policy. The PowerShell cmdlets &lt;code&gt;Get-MgPolicyRoleManagementPolicy&lt;/code&gt; and &lt;code&gt;Get-MgPolicyRoleManagementPolicyAssignment&lt;/code&gt; read role-management policies via Microsoft Graph; the cmdlets ship in the &lt;code&gt;Microsoft.Graph.Identity.SignIns&lt;/code&gt; module, despite the Identity Governance branding [@ms-learn-graph-pim-policy-cmdlet].The PIM role-management-policy cmdlets are commonly mis-attributed to the &lt;code&gt;Microsoft.Graph.Identity.Governance&lt;/code&gt; PowerShell module because of the Identity Governance branding. They are actually in &lt;code&gt;Microsoft.Graph.Identity.SignIns&lt;/code&gt;. The &lt;code&gt;Import-Module&lt;/code&gt; line that gets the cmdlets into scope is &lt;code&gt;Import-Module Microsoft.Graph.Identity.SignIns&lt;/code&gt; [@ms-learn-graph-pim-policy-cmdlet]. The &lt;strong&gt;EntraOps Privileged EAM&lt;/strong&gt; community project on GitHub, maintained by Thomas Naunheim, demonstrates the &quot;track changes and history of privileged principals and their assignments as code&quot; idiom against the Enterprise Access Model classification [@entraops-github]. Azure Policy itself operates on Azure resource configurations and does not directly evaluate PIM role-management policy state [@ms-learn-azure-policy], which is the data-model gap that drives the GitOps-flavoured drift-detection community pattern.&lt;/p&gt;
&lt;p&gt;{`
// Take an array of role-management policy assignments
// (the kind Get-MgPolicyRoleManagementPolicyAssignment returns)
// and assert tenant-wide PIM coverage invariants.&lt;/p&gt;
&lt;p&gt;function assertPrivilegedRoleCoverage(assignments, privilegedRoles, expected) {
  const findings = [];
  for (const role of privilegedRoles) {
    const a = assignments.find(x =&amp;gt; x.roleDefinitionId === role);
    if (!a) {
      findings.push({ role, severity: &apos;High&apos;, issue: &apos;No PIM policy assignment&apos; });
      continue;
    }
    const p = a.policy;
    if (p.requires_mfa !== expected.requires_mfa)
      findings.push({ role, severity: &apos;High&apos;, issue: &apos;MFA at activation not required&apos; });
    if (p.requires_approval !== expected.requires_approval)
      findings.push({ role, severity: &apos;High&apos;, issue: &apos;Approval not required&apos; });
    if (p.requires_justification !== expected.requires_justification)
      findings.push({ role, severity: &apos;Medium&apos;, issue: &apos;Justification not required&apos; });
    if (p.max_duration_hours &amp;gt; expected.max_duration_hours)
      findings.push({ role, severity: &apos;Medium&apos;,
        issue: &apos;Maximum activation duration exceeds expected value&apos;,
        actual: p.max_duration_hours, expected: expected.max_duration_hours });
  }
  return findings;
}&lt;/p&gt;
&lt;p&gt;const privileged = [&apos;Global Administrator&apos;, &apos;Privileged Role Administrator&apos;,
                    &apos;Security Administrator&apos;, &apos;User Access Administrator&apos;];
const expected = { requires_mfa: true, requires_approval: true,
                   requires_justification: true, max_duration_hours: 1 };
const sample = [{ roleDefinitionId: &apos;Global Administrator&apos;,
  policy: { requires_mfa: true, requires_approval: true,
            requires_justification: true, max_duration_hours: 4 } }];
console.log(assertPrivilegedRoleCoverage(sample, privileged, expected));
`}&lt;/p&gt;
&lt;p&gt;The conjecture. A full coverage-as-code primitive needs Azure Policy (or its successor) to evaluate PIM role-management policy state with the same first-class semantics it applies to Azure resource configuration. That extension would let a tenant declare an invariant -- &quot;every role in the control plane has &lt;code&gt;requires_mfa=true&lt;/code&gt; and &lt;code&gt;max_duration_hours &amp;lt;= 1&lt;/code&gt;&quot; -- and have the platform enforce it continuously across drift, the way Azure Policy already enforces resource invariants.&lt;/p&gt;
&lt;h3&gt;10.5 Adaptive-cadence eligibility reviews&lt;/h3&gt;
&lt;p&gt;Should eligible membership be access-reviewed at higher cadence than active assignments? Eligible membership is standing privilege; active membership is bounded. The argument for adaptive cadence -- reviewing eligibility more frequently when behavioural signals or organizational events suggest the principal may no longer need the role -- is intuitive but mechanically unshipped.&lt;/p&gt;
&lt;p&gt;Best partial result. The 2024+ ML-based access-review recommendations [@ms-learn-review-recommendations] -- inactive-user 30-day Deny, user-to-group-affiliation Deny -- are &lt;em&gt;within-cycle&lt;/em&gt; reviewer-assist features. They help reviewers decide during a configured access review. They are not &lt;em&gt;cross-cycle&lt;/em&gt; adaptive-cadence triggers that fire a new review off-schedule when conditions warrant.&lt;/p&gt;
&lt;p&gt;These are research problems. The practitioner does not have the luxury of waiting for them to be solved. What does Monday morning look like for the architect who has read this far and now has to deploy?&lt;/p&gt;
&lt;h2&gt;11. Practical Guide: Monday Morning for the 2026 Tenant Architect&lt;/h2&gt;
&lt;p&gt;You have read ten thousand words. You are responsible for a Microsoft 365 tenant that audits against SOX, SOC 2, and ISO 27001. You have a budget for Entra ID P2 (or Entra ID Governance) per privileged user. What do you do on Monday?&lt;/p&gt;
&lt;p&gt;Work in this order. The list is ordered by cost-to-impact, with the cheapest, highest-impact items first.&lt;/p&gt;
&lt;h3&gt;Step 1: Baseline the Tier-0 surface&lt;/h3&gt;
&lt;p&gt;Every directory role at &quot;Privileged&quot; classification or above should be PIM-eligible-only. The exceptions are the two emergency-access permanent-active Global Administrator accounts (break-glass), which we return to in Step 4.&lt;/p&gt;
&lt;p&gt;Activation requires MFA, approval, justification, and ticket number for control-plane and management-plane roles. Maximum activation duration is one hour for Global Administrator and Privileged Role Administrator, and four hours for less-privileged roles. Configure per role per scope; remember that PIM-for-Azure-Resources policies do not inherit.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Import-Module Microsoft.Graph.Identity.Governance
Connect-MgGraph -Scopes &apos;RoleManagement.Read.Directory&apos;,&apos;User.Read.All&apos;
$gaRoleId = (Get-MgRoleManagementDirectoryRoleDefinition `
    -Filter &quot;displayName eq &apos;Global Administrator&apos;&quot;).Id
Get-MgRoleManagementDirectoryRoleAssignment `
    -Filter &quot;roleDefinitionId eq &apos;$gaRoleId&apos;&quot; `
    -ExpandProperty Principal |
    Select-Object @{n=&apos;User&apos;;e={$_.Principal.AdditionalProperties.userPrincipalName}}, RoleDefinitionId
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This lists every standing-active Global Administrator in the tenant. Compare against your break-glass roster and your active PIM activations. Anything else is technical debt.&lt;/p&gt;
&lt;h3&gt;Step 2: Configure access reviews&lt;/h3&gt;
&lt;p&gt;Quarterly for Tier-0 and control-plane roles. Semi-annually for Tier-1 and management-plane. Annually for Tier-2 and data/workload-plane [@ms-learn-pim-access-reviews]. Turn on the ML-based review recommendations: the 30-day inactive-user Deny recommendation is the reviewer-assist baseline, and the user-to-group-affiliation Deny recommendation helps reviewers spot principals who are organizationally distant from the rest of the group&apos;s membership [@ms-learn-review-recommendations].&lt;/p&gt;
&lt;h3&gt;Step 3: Turn on every PIM Alert and tune the GA-count threshold&lt;/h3&gt;
&lt;p&gt;Enable all six behavioural PIM Alerts. Tune the &quot;There are too many Global Administrators&quot; alert to a minimum count of two and a percentage of 50% [@ms-learn-pim-alerts]. The expected steady-state count is &quot;fewer than five standing GAs, most of which are break-glass.&quot; The High-severity assignment-bypass alert is non-negotiable; route it to a 24x7 SOC queue with an incident-response runbook.Microsoft Secure Score&apos;s &quot;Limit the number of Global Administrators&quot; recommendation targets fewer than five standing GAs as the canonical baseline.&lt;/p&gt;
&lt;h3&gt;Step 4: Break-glass discipline&lt;/h3&gt;
&lt;p&gt;Two emergency-access permanent-active Global Administrator accounts. Not one, not three.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; One break-glass account is a single point of failure: if it is locked, lost, or compromised, the tenant has no emergency entry path. Three or more begin to expand the blast radius unnecessarily. Two balances the two failure modes. &lt;a href=&quot;https://paragmali.com/blog/webauthn-and-passkeys-on-windows-from-ctap-to-the-credential/&quot; rel=&quot;noopener&quot;&gt;FIDO2 hardware keys&lt;/a&gt;, stored in physical safes, with continuous sign-in alerting.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Conditional Access policies can lock you out. Break-glass accounts must be excluded from every CA policy that could prevent their sign-in. Compensate with continuous sign-in alerting on every break-glass authentication event; alerts are the substitute for the gate you are deliberately removing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Step 5: Extend PIM to the four boundaries&lt;/h3&gt;
&lt;p&gt;PIM-for-Groups: gate &lt;strong&gt;ownership&lt;/strong&gt; of every directory-role-assignable group, every privileged-access security group, and every group that grants management-group-level Azure RBAC. Membership alone is insufficient; ownership is a backdoor to membership.&lt;/p&gt;
&lt;p&gt;PIM-for-Azure-Resources: gate Owner, User Access Administrator, and Contributor at the management-group scope, then explicitly at every subscription, every resource group, and every resource where the role is assignable. Inheritance does not flow; configure per scope.&lt;/p&gt;
&lt;p&gt;GDAP and Lighthouse: every CSP partner authorization must be eligible, not active. Set the customer-side PIM policy explicitly. Audit annually.&lt;/p&gt;
&lt;p&gt;PIM with Conditional Access: attach an authentication-context tag to activation policies on the privileged Entra roles. Add a CA policy that requires a compliant device and a fresh MFA challenge on activation. The activation gate becomes structurally tighter than the sign-in gate, which is the correct ordering for high-privilege actions.&lt;/p&gt;
&lt;h3&gt;Step 6: Continuous detection&lt;/h3&gt;
&lt;p&gt;Pipe PIM activation events (via Microsoft Graph audit logs, surfaced in the &lt;code&gt;AuditLogs&lt;/code&gt; and &lt;code&gt;MicrosoftGraphActivityLogs&lt;/code&gt; Azure Monitor tables) to your SIEM. Cross-correlate with Entra ID Protection sign-in risk and Microsoft Sentinel UEBA anomaly signals [@ms-learn-sentinel-ueba]. KQL templates to write: (a) GA activations outside business hours; (b) activations from non-compliant devices; (c) the assignment-bypass alert correlated with the activating principal&apos;s recent sign-in risk score; (d) managed-identity token issuance against subscription-scoped Owner.&lt;/p&gt;
&lt;h3&gt;Step 7: Mind the application-identity surface&lt;/h3&gt;
&lt;p&gt;This is the longest-running open item. Inventory every managed identity in the tenant. For each, document the role assignment, the scope, and the resource that holds it.&lt;/p&gt;
&lt;p&gt;Apply the &quot;Owner and User Access Administrator at subscription scope is dangerous&quot; rule first; tighten those to Contributor or a custom role wherever possible. Where a managed identity must hold a high-privilege role at a high scope, treat the underlying resource (Function App, Logic App, VM, AKS cluster) as a Tier-0 asset for the purposes of patching, network exposure, and code-review process. Until PIM gates application identities natively, the Tier-0-asset framing is the substitute control.&lt;/p&gt;
&lt;p&gt;That is the playbook for the user-principal side of the JIT-admin problem. The application-identity side is still being written. The next iteration of this material will be about the data-model extension that closes Robbins&apos;s gap, or the architectural successor that arrives in its place.&lt;/p&gt;
&lt;h2&gt;12. Frequently Asked Questions and Closing&lt;/h2&gt;
&lt;p&gt;Three classes of question come up every time this material is taught. The first is conceptual (&quot;what does eligible actually mean?&quot;). The second is operational (&quot;do I need MFA?&quot;). The third is adversarial (&quot;what about managed identities?&quot;). Each appears below.&lt;/p&gt;

No. Eligible assignments are permanent in most tenants -- they are the standing relationship between principal and role -- but they grant no privilege until you activate. Only the *active* state is bounded. Your admin rights still exist; they are simply not exercised continuously [@ms-learn-pim-configure].

Only if the role&apos;s activation policy is configured to require it. PIM&apos;s activation gates -- MFA at activation, approval, justification, ticket number, and activation maximum duration -- are per-role, per-scope flags the tenant sets independently. A role with `requires_mfa=false` and `requires_approval=false` is a valid (if loose) PIM configuration [@ms-learn-pim-change-default-settings].

One hour for the highest-privileged Entra directory roles, including Global Administrator and Privileged Role Administrator. The configurable range is one to twenty-four hours per role per scope [@ms-learn-pim-change-default-settings]. Tighten where you can; the activation cost is small, the standing-active surface saving is large.

No. Conditional Access gates the sign-in event. PIM bounds the assignment state. A compromised CA-gated GA still has GA privileges once they sign in -- the gate that mattered (activation) was never traversed. CA and PIM compose; PIM is not a substitute for CA, and CA is not a substitute for PIM.

No. PIM alerts via the High-severity &quot;Roles are being assigned outside of Privileged Identity Management&quot; alert when a direct assignment happens [@ms-learn-pim-alerts]. The detection is intentional rather than preventive: blocking direct assignment would break the Microsoft Graph integration surface every legitimate administrative tool uses. Preventive controls -- Conditional Access on the Graph endpoint, Azure Policy at the management-group scope, or entitlement-management workflows -- are added separately based on the tenant&apos;s tooling estate.

No. PIM&apos;s eligible/active state machine is defined over user and group principals. Service principals, managed identities, and OAuth consent grants route around PIM activation entirely. Andy Robbins&apos;s June 2022 *Managed Identity Attack Paths* series [@robbins-mip-part1], [@robbins-mip-part2], [@robbins-mip-part3] is the canonical demonstration; MITRE ATT&amp;amp;CK T1078.004 [@mitre-t1078-004] cites Robbins as primary reference. Workload ID Premium plus Conditional Access for workload identities extends sign-in-time controls to service principals (with managed identities still excluded), but does not yet introduce an eligible category for workload-identity role assignments [@ms-learn-ca-workload-identity], [@ms-learn-workload-identity-risk].

Microsoft has shifted the framing to the Enterprise Access Model: control plane, management plane, and data/workload plane [@ms-learn-eam]. The retirement of Tier-0/1/2 is partial; the practitioner community still uses the legacy terms day to day. The underlying principle -- privilege boundaries you do not cross with a single credential -- is preserved across both framings.
&lt;h3&gt;Closing&lt;/h3&gt;
&lt;p&gt;Read the section 1 vignette again. The 2026 tenant where &lt;a href=&quot;mailto:alice@contoso.com&quot; rel=&quot;noopener&quot;&gt;alice@contoso.com&lt;/a&gt; is Global Administrator for exactly one hour, with an audit log so complete the SOC 2 auditor signs it without questions, is not a configuration choice. It is the visible behaviour of an identity system whose role-assignment object carries one more field than the 2015 version did. Standing admin did not retire because operators got more disciplined. Standing admin retired because the data model grew a second state.&lt;/p&gt;
&lt;p&gt;The forty years between Saltzer and Schroeder&apos;s 1975 paper and the 2015 Azure AD PIM Preview were not lost time. UNIX &lt;code&gt;sudo&lt;/code&gt;, Kerberos delegation, DACLs, AD groups, MIM PAM, Pass-the-Hash v1 and v2, the Securing Privileged Access roadmap -- each built up the structural understanding that least privilege required a temporal mechanism, not just a static one, and that the temporal mechanism had to live on the assignment object itself, not on the group, the credential, the session, or any indirection through a separate forest. The single new field on the role-assignment object is what those forty years were preparing.&lt;/p&gt;
&lt;p&gt;What remains undone is the application-identity boundary. The same role-assignment object Microsoft retrofitted to gate user activation does not yet gate the managed identity attached to a Function App. The IMDS endpoint at &lt;code&gt;169.254.169.254&lt;/code&gt; is the canonical 2026 bypass path that proves it. Closing that gap, when it comes, will not be a patch to the existing eligible/active state machine. It will be the next chapter -- the one where the state machine learns to apply to a principal that cannot perform an interactive MFA challenge, and the activation semantics are reinvented for the non-interactive case.&lt;/p&gt;
&lt;p&gt;The story is not finished. But the first chapter -- the chapter where standing admin became visibly the anti-pattern it had always been -- is.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;privileged-identity-management&quot; keyTerms={[
  { term: &quot;Standing admin&quot;, definition: &quot;A privileged identity whose role assignment is active and permanent. Standing admin is the deployed-reality default of any pre-PIM tenant and most AD-only environments through 2026.&quot; },
  { term: &quot;Eligible assignment&quot;, definition: &quot;A PIM-managed role assignment that grants no privilege until activated. Eligible is the standing relationship between principal and role; active is the time-bounded materialization.&quot; },
  { term: &quot;Active assignment&quot;, definition: &quot;A PIM-managed role assignment that grants the role&apos;s permissions for the assignment&apos;s duration. Active assignments are permanent (legacy posture) or time-bound (after activate()).&quot; },
  { term: &quot;Activation policy&quot;, definition: &quot;Per-role-per-scope configuration of activation gates: MFA, approval, justification, ticket number, and maximum duration. Gates are independent flags.&quot; },
  { term: &quot;Authentication context (PIM with Conditional Access)&quot;, definition: &quot;A label PIM attaches to the activation event so Conditional Access policies can target activation specifically, not just sign-in.&quot; },
  { term: &quot;Bastion forest&quot;, definition: &quot;A separate Active Directory forest dedicated to housing privileged accounts. The MIM 2016 PAM on-premises pattern; superseded for new deployments by cloud-first Entra PIM.&quot; },
  { term: &quot;Shadow principal&quot;, definition: &quot;An AD object (msDS-ShadowPrincipal, Windows Server 2016) carrying a production-forest SID that the bastion KDC injects into the user&apos;s Kerberos PAC for a TTL.&quot; },
  { term: &quot;Assignment-bypass alert&quot;, definition: &quot;The High-severity PIM Alert &apos;Roles are being assigned outside of Privileged Identity Management.&apos; Fires when a privileged role is assigned directly via Microsoft Graph rather than through PIM activation. Detective, not preventive.&quot; },
  { term: &quot;Enterprise Access Model (EAM)&quot;, definition: &quot;The post-2021 Microsoft reference architecture replacing Tier-0/1/2 with control plane, management plane, and data/workload plane.&quot; },
  { term: &quot;PIM for Groups&quot;, definition: &quot;The 2023 extension of PIM eligible/active assignment to security groups and Microsoft 365 groups. Gates both membership and ownership; excludes dynamic-membership groups and on-premises-synced groups.&quot; },
  { term: &quot;GDAP (Granular Delegated Admin Privileges)&quot;, definition: &quot;The May 2022 Microsoft Partner Center capability that replaces legacy DAP standing-Global-Administrator-on-every-customer-tenant with time-bound, role-scoped delegation between partner and customer tenants.&quot; },
  { term: &quot;Managed identity&quot;, definition: &quot;An Azure-managed service principal whose credentials are issued and rotated by Azure itself. Not currently in scope for PIM activation; role assignments are permanent and active.&quot; },
  { term: &quot;IMDS endpoint&quot;, definition: &quot;The Azure Instance Metadata Service endpoint at &lt;code&gt;http://169.254.169.254/metadata/identity/oauth2/token&lt;/code&gt;, reachable only from inside the Azure resource, that returns an OAuth token for the attached managed identity.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>privileged-identity-management</category><category>entra-id</category><category>just-in-time-admin</category><category>identity-security</category><category>azure</category><category>security-architecture</category><category>zero-trust</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><item><title>From Password-in-the-Pipe to Cloud-Issued Session: Twenty-Six Years of RDP Authentication</title><link>https://paragmali.com/blog/rdp-authentication-26-years/</link><guid isPermaLink="true">https://paragmali.com/blog/rdp-authentication-26-years/</guid><description>How five generations of Windows RDP authentication -- classic delegation, NLA via CredSSP, Restricted Admin, Remote Credential Guard, and PRT-over-RDP -- retreated from the 1998 design that gave attackers the keys to every target.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Remote Desktop Protocol** has spent twenty-six years retreating from one design decision: in 1998, &quot;the user&apos;s password becomes the target&apos;s credential.&quot; Five generations now coexist on a Windows estate. **Classic credential delegation** sends the user&apos;s NT one-way function into the target&apos;s `lsass.exe`. **Network Level Authentication via CredSSP** [@rdpbcgr-credssp] (Windows Vista, 2006) moves authentication before the session starts but still delivers credential material. **Restricted Admin mode** [@ms-adv-2871997] (Windows 8.1 / Server 2012 R2 RTM, October 17, 2013) stops delivering credentials and runs the user&apos;s session as the target&apos;s machine identity. **Remote Credential Guard** [@msl-rcg] (Windows 10 1607, August 2, 2016) forwards Kerberos operations back to the caller&apos;s `lsass.exe`, with VTL1 trustlet protection conditional on the caller having local Credential Guard enabled. **PRT-over-RDP** [@msl-prtrdp-mstsc] (October 11, 2022 cumulative updates) uses a Microsoft Entra ID Primary Refresh Token cookie scoped to the Conditional Access app `a4a365df-50f1-4397-bc59-1a1564b8bb9c`. **CVE-2018-0886** [@nvd-2018-0886] (CredSSP MITM) and **CVE-2019-0708** [@nvd-2019-0708] (BlueKeep, pre-auth channel-setup) are the canonical RDP CVEs. The residual classes are RBCD against `TERMSRV` [@shamir-wagging], PRT extraction at the session host [@mollema-prt2], and the architectural SYSTEM-on-target floor that no RDP mode can close.
&lt;h2&gt;1. Four sekurlsa Dumps, One Target&lt;/h2&gt;
&lt;p&gt;A red-team operator with &lt;code&gt;SYSTEM&lt;/code&gt; on a single Windows 11 25H2 host runs &lt;code&gt;mimikatz sekurlsa::logonpasswords&lt;/code&gt; four times in a row [@mimikatz-github]. Each time, a different user has just disconnected an RDP session. Each time, the dump looks different.&lt;/p&gt;
&lt;p&gt;The first dump shows the user&apos;s NT one-way function. The second dump shows the target machine&apos;s identity instead of the user&apos;s. The third dump shows the user&apos;s identity but no hash that can be replayed anywhere. The fourth dump shows no password-equivalent material for the user at all -- only a Primary Refresh Token bound to the target&apos;s own TPM.&lt;/p&gt;
&lt;p&gt;Four RDP sessions, one target, four entirely different post-exploitation pivots. The difference is not in what the attacker did. The difference is in which authentication mode the client negotiated before the session was established.&lt;/p&gt;
&lt;p&gt;This article is about those four modes -- classic, Restricted Admin, Remote Credential Guard, and PRT-over-RDP -- and the fifth (NLA via CredSSP) that sits underneath them all. It is the story of a twenty-six-year retreat from &quot;the user&apos;s password becomes the target&apos;s credential.&quot;&lt;/p&gt;
&lt;p&gt;The wire-protocol selectors are public, even if their names mostly are not. The &lt;code&gt;RDP_NEG_REQ&lt;/code&gt; structure [@rdpbcgr-negreq] sets &lt;code&gt;requestedProtocols&lt;/code&gt; to one of &lt;code&gt;PROTOCOL_RDP (0x00)&lt;/code&gt;, &lt;code&gt;PROTOCOL_SSL (0x01)&lt;/code&gt;, &lt;code&gt;PROTOCOL_HYBRID (0x02)&lt;/code&gt; for CredSSP-based NLA, &lt;code&gt;PROTOCOL_HYBRID_EX (0x08)&lt;/code&gt;, or &lt;code&gt;PROTOCOL_RDSAAD (0x10)&lt;/code&gt; for the Entra-based path. Inside &lt;code&gt;PROTOCOL_HYBRID&lt;/code&gt;, two &lt;code&gt;flags&lt;/code&gt; bits switch the sub-mode: &lt;code&gt;RESTRICTED_ADMIN_MODE_REQUIRED (0x01)&lt;/code&gt; and &lt;code&gt;REDIRECTED_AUTHENTICATION_MODE_REQUIRED (0x02)&lt;/code&gt;. Five wire-level paths. Five different answers to the same question: what does the target&apos;s &lt;code&gt;lsass.exe&lt;/code&gt; end up holding?&lt;/p&gt;
&lt;p&gt;The next twelve sections walk each mode end-to-end, the CVEs that broke each layer, the Microsoft Learn comparison matrix verbatim, the residual class that survives each generation, and the operational guide for the engineer who has to make these primitives interoperate on Monday morning. Before we can read the four dumps, we have to understand the one credential-delivery decision every later generation was built to correct. That decision shipped in 1998.&lt;/p&gt;
&lt;h2&gt;2. Terminal Services and the Password-in-the-Pipe Era (1998-2005)&lt;/h2&gt;
&lt;p&gt;In 1998 Microsoft shipped a remote-display product that solved an obvious problem -- run a Windows desktop over the network -- and a second, less obvious problem along the way: every machine that accepted an RDP connection now needed to be a credential reservoir for every user who connected to it.&lt;/p&gt;
&lt;p&gt;The product was Windows NT 4.0 Terminal Server Edition [@wiki-nt4tse], built on top of MultiWin technology that Microsoft had licensed from Citrix the previous year [@wiki-rdp]. The protocol that carried the display and the keystrokes was the Remote Desktop Protocol, version 4.0, listening on TCP port 3389 (and, much later, UDP port 3389 for the QUIC variant) [@wiki-rdp]. The protocol itself was an extension of the ITU-T T.128 application-sharing protocol [@wiki-rdp], with RC4 channel encryption layered on top using a 40-bit, 56-bit, or 128-bit session key (a FIPS-validated 3DES variant was added in Server 2003) [@rdpbcgr-index].&lt;/p&gt;

timeline
    title Twenty-six years of RDP authentication
    1998 : NT 4.0 Terminal Server Edition / RDP 4.0
         : Password delivered to target&apos;s lsass
    2001 : SMBRelay (cDc) names credential-in-motion attack
    2006 : Windows Vista / RDP 6.0 ships NLA via CredSSP
    2013 : Windows 8.1 / 2012 R2 ships Restricted Admin
    2014 : KB2871997 backports Restricted Admin to Win 7
    2016 : Windows 10 1607 / 2016 ships Remote Credential Guard
    2018 : CVE-2018-0886 CredSSP RCE; AllowEncryptionOracle
    2019 : CVE-2019-0708 BlueKeep pre-auth channel-setup RCE
    2022 : KB5018418 ships PRT-over-RDP / Entra SSO for RDP
    2025 : Win 11 24H2 KIR recovers from RDP-stack regression

Microsoft&apos;s multi-user remote-desktop subsystem, introduced in Windows NT 4.0 Terminal Server Edition (1998) [@wiki-rdp]. Renamed to Remote Desktop Services in Windows Server 2008 R2. Provides interactive Windows sessions over TCP/3389 using the Remote Desktop Protocol, an extension of the ITU-T T.128 application-sharing protocol family.
&lt;p&gt;Authentication, in 1998, looked nothing like authentication today. The client opened a TCP/3389 connection. The server sent a Proprietary Certificate containing an RSA public key. The client generated a 32-byte Client Random, encrypted it with that RSA public key, and both sides derived RC4 session keys from the shared random [@rdpbcgr-index].&lt;/p&gt;
&lt;p&gt;The user then typed a username and password into the remote login screen, and the password traveled into the target&apos;s &lt;code&gt;Winlogon&lt;/code&gt; process inside that RC4 channel. The target&apos;s &lt;code&gt;lsass.exe&lt;/code&gt; ran NTLM challenge-response against a domain controller (or against its local SAM) and, on success, materialised an interactive session for the user. The target now held the user&apos;s NT one-way function for the lifetime of that session.&lt;/p&gt;
&lt;p&gt;The architectural property was simple: the target was the credential reservoir. Five accounting clerks who RDP&apos;d into a Terminal Server during a shift left five NT-OWF entries in that host&apos;s &lt;code&gt;lsass.exe&lt;/code&gt; memory. An attacker who later got &lt;code&gt;SYSTEM&lt;/code&gt; on the Terminal Server held the credential material for all five.&lt;/p&gt;
&lt;p&gt;Two years later, Windows 2000 made Terminal Services a built-in server feature, and Windows XP Professional (October 2001) shipped the desktop-side variant under the brand &quot;Remote Desktop&quot; [@wiki-rdp]. The credential-aggregation surface, previously confined to dedicated Terminal Server hosts, now extended to every workstation in the estate.&lt;/p&gt;
&lt;p&gt;The first public articulation that &lt;em&gt;credential material in motion is itself an attack surface&lt;/em&gt; came on March 31, 2001, when Sir Dystic of Cult of the Dead Cow released SMBRelay at the &lt;code&gt;@lanta.con&lt;/code&gt; convention in Atlanta [@cdc-smbrelay]. SMBRelay was an SMB man-in-the-middle that hijacked an inbound NTLM authentication and relayed it onward.The Wikipedia SMBRelay article gives March 21, 2001 [@wiki-smbrelay], but the primary source -- Sir Dystic&apos;s own publication at cultdeadcow.com -- says March 31, 2001 [@cdc-smbrelay]. Primary-source dating wins. The Wikipedia article also says &quot;receives a connection on UDP port 139,&quot; which is incorrect; NetBIOS Session Service has always run over TCP/139, and the SMBRelay v0.98 source listing on cultdeadcow.com explicitly binds a TCP socket to port 139. RDP was not yet a direct target, but the principle was now public: pass-the-hash and credential relay would work against any protocol that put credential material on the wire.&lt;/p&gt;
&lt;p&gt;RDP 5.2 (Server 2003, XP SP2) responded to the wire-confidentiality problem in 2003 by adding an optional &lt;code&gt;Security Layer = SSL&lt;/code&gt; setting that wrapped the RDP traffic in TLS [@rdpbcgr-index]. The header byte called &lt;code&gt;selectedProtocol&lt;/code&gt; could now take the value &lt;code&gt;PROTOCOL_SSL = 0x01&lt;/code&gt;, meaning the channel was TLS-encrypted before the legacy basic-settings exchange ran. TLS solved the wire-snooping problem.&quot;Security Layer = SSL&quot; in RDP 5.2 was &lt;em&gt;transport confidentiality only&lt;/em&gt;. The TLS handshake authenticated the server&apos;s certificate (or did not, in many production configurations); the user authentication still happened later, inside the basic-settings exchange and the &lt;code&gt;Logon_Info&lt;/code&gt; PDU. The credential the target ended up with was identical to classic-RDP credential delivery. The distinction between &quot;TLS protects the wire&quot; and &quot;TLS protects the credential&quot; is the load-bearing precision the next section will need. It did not solve the credential-aggregation problem.&lt;/p&gt;
&lt;p&gt;By 2005 the architectural problem was named. Microsoft&apos;s eventual response would shape the next two decades of Windows authentication strategy. It would also, on its first attempt, fix the wrong half of the problem.&lt;/p&gt;
&lt;h2&gt;3. Network Level Authentication via CredSSP (2006-2013)&lt;/h2&gt;
&lt;p&gt;Network Level Authentication is the policy that requires authentication before the RDP session is established. NLA is not a protocol. NLA is implemented by CredSSP -- the Credential Security Support Provider -- running over TLS [@rdpbcgr-credssp]. The difference between those two sentences is the one that matters.&lt;/p&gt;
&lt;p&gt;Windows Vista (November 2006) shipped RDP 6.0, the first Windows release to include NLA and CredSSP support. NLA was selectable in System Properties but did not become the Remote Desktop default until Windows 7 / Server 2008 R2 [@wiki-rdp]. The MS-CSSP open specification documents CredSSP&apos;s role and its lineage, with the protocol summary listing its first revision in December 2006 [@mscssp-index] -- the same shipping window as Vista.&lt;/p&gt;
&lt;p&gt;MS-CSSP defines CredSSP as a protocol that &quot;enables an application to securely delegate a user&apos;s credentials from a client to a target server&quot; [@mscssp-index]. That phrasing matters: from CredSSP&apos;s own design statement, the credential is delegated &lt;em&gt;to&lt;/em&gt; the target. The credential still ends up on the target. The protocol design did not change that property; it changed &lt;em&gt;when&lt;/em&gt; the delivery happens and &lt;em&gt;what&lt;/em&gt; protects it on the wire.&lt;/p&gt;

A policy on the RDP server that requires the connecting user to authenticate before the RDP session is established (before the basic-settings exchange and the virtual-channel binding). Implemented by CredSSP over TLS [@rdpbcgr-credssp]. NLA reduces denial-of-service exposure and gates the pre-auth channel-setup attack surface (the failure mode BlueKeep would later exploit) but does not change the credential material the target receives.

The Microsoft authentication protocol, specified in MS-CSSP [@mscssp-index] and used by RDP and Windows Remote Management, that &quot;amalgamates&quot; TLS with Kerberos and NT LAN Manager [@rdpbcgr-credssp]. CredSSP runs SPNEGO inside a TLS channel, performs Kerberos or NTLM authentication inside SPNEGO, and finally delivers a `TSCredentials` payload to the target. CredSSP is what NLA *is*, at the wire.

The CredSSP payload structure that carries the user&apos;s credential material to the target after the SPNEGO and Kerberos/NTLM phases complete [@mscssp-index]. The classic form is `TSPasswordCreds` (username + domain + plaintext password). Two later forms -- the credential-less form for Restricted Admin and the redirected form (`TSRemoteGuardCreds`) for Remote Credential Guard -- change what travels in this slot without changing the surrounding exchange.
&lt;p&gt;The wire choreography is documented verbatim in section 5.4.5.2 of MS-RDPBCGR. CredSSP is &quot;essentially the amalgamation of TLS with Kerberos and NT LAN Manager (NTLM)&quot; [@rdpbcgr-credssp]. The exchange runs in this order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Client opens TCP/3389.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RDP_NEG_REQ&lt;/code&gt; carries &lt;code&gt;requestedProtocols &amp;amp; PROTOCOL_HYBRID = 0x02&lt;/code&gt; (or &lt;code&gt;PROTOCOL_HYBRID_EX = 0x08&lt;/code&gt;, which adds an Early User Authorization Result PDU) [@rdpbcgr-negreq].&lt;/li&gt;
&lt;li&gt;TLS handshake completes inside the RDP transport.&lt;/li&gt;
&lt;li&gt;SPNEGO negotiates Kerberos or NTLM inside the TLS channel.&lt;/li&gt;
&lt;li&gt;Kerberos or NTLM authenticates the user (and, for Kerberos, the server as well).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;TSCredentials&lt;/code&gt; payload is sent to the target inside the still-open TLS channel. &quot;Once Kerberos or NTLM has completed successfully, the user&apos;s credentials are sent to the server&quot; [@rdpbcgr-credssp].&lt;/li&gt;
&lt;li&gt;The RDP basic-settings exchange and virtual-channel binding proceed.&lt;/li&gt;
&lt;/ol&gt;

sequenceDiagram
    autonumber
    participant C as Client (mstsc.exe)
    participant T as Target (lsass.exe + termsrv)
    participant K as KDC / domain controller
    C-&amp;gt;&amp;gt;T: TCP/3389 + RDP_NEG_REQ (PROTOCOL_HYBRID 0x02)
    T--&amp;gt;&amp;gt;C: RDP_NEG_RSP (PROTOCOL_HYBRID selected)
    C-&amp;gt;&amp;gt;T: TLS Client Hello
    T--&amp;gt;&amp;gt;C: TLS Server Hello + cert chain
    C-&amp;gt;&amp;gt;T: SPNEGO NegTokenInit inside TLS
    C-&amp;gt;&amp;gt;K: AS-REQ / TGS-REQ for target SPN
    K--&amp;gt;&amp;gt;C: AS-REP / TGS-REP
    C-&amp;gt;&amp;gt;T: AP-REQ inside SPNEGO inside TLS
    T--&amp;gt;&amp;gt;C: AP-REP (mutual auth)
    C-&amp;gt;&amp;gt;T: TSCredentials (TSPasswordCreds)
    T-&amp;gt;&amp;gt;T: lsass logs user on, NT-OWF cached
    C-&amp;gt;&amp;gt;T: RDP basic-settings exchange + session
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;Enable NLA&quot; in the System Properties dialog flips a server-side policy that requires the connecting client to authenticate via CredSSP before the RDP session is established. NLA is &lt;em&gt;that policy&lt;/em&gt;. CredSSP [@mscssp-index] is the wire protocol that satisfies it. When operators say &quot;we require NLA,&quot; they mean &quot;we accept only &lt;code&gt;requestedProtocols = PROTOCOL_HYBRID&lt;/code&gt; or &lt;code&gt;PROTOCOL_HYBRID_EX&lt;/code&gt;,&quot; which is enforced by Windows refusing the &lt;code&gt;RDP_NEG_REQ&lt;/code&gt; if those bits are not set. The protocol carrying the credential to the target is CredSSP, today and twenty years from now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What NLA accomplishes is real. The unauthenticated RDP channel-setup code -- basic-settings exchange, virtual-channel binding, the &lt;code&gt;MS_T120&lt;/code&gt; handler that BlueKeep would exploit a decade later -- is no longer reachable from the network. An attacker who reaches TCP/3389 must complete a CredSSP handshake (with valid Kerberos or NTLM credentials) before any RDP-stack code beyond the negotiation header runs. That is a denial-of-service mitigation and a pre-auth-RCE mitigation. It is not a credential-isolation mitigation.&lt;/p&gt;
&lt;p&gt;What NLA does not accomplish is what happens at step 6. The output of CredSSP is &lt;code&gt;TSPasswordCreds&lt;/code&gt;, which is the user&apos;s plaintext password (or its NTLM equivalent in some paths) delivered to the target&apos;s &lt;code&gt;lsass.exe&lt;/code&gt;. Mimikatz &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; against the target after the session ends returns the user&apos;s NT-OWF [@mimikatz-github, @wiki-mimikatz], exactly as it would against a 1998-era classic-RDP target. The 2014 Microsoft &lt;em&gt;Mitigating Pass-the-Hash&lt;/em&gt; whitepaper (version 2) [@msl-pthv2] named this failure mode eight years after NLA shipped: NLA is necessary but not sufficient.&lt;/p&gt;
&lt;p&gt;Mimikatz had given the failure mode a name. Benjamin Delpy released the first version in May 2011, initially closed-source [@wiki-mimikatz]. In September 2011 a version of the exploit was used in the DigiNotar incident [@wiki-mimikatz]. By 2012, on a Windows estate running NLA-mandatory RDP since 2007, attackers were still pivoting from a compromised admin&apos;s workstation to every server that admin had logged into, harvesting NT-OWFs as they went. NLA was on the wire. The Pass-the-Hash playbook still worked.&lt;/p&gt;
&lt;p&gt;NLA moved when the authentication happened. It did not change what the target ended up with. Pass-the-Hash against a 2012-era CredSSP-authenticated RDP target was identical to Pass-the-Hash against a 1998-era classic-RDP target. Microsoft&apos;s architectural response shipped in October 2013 -- and it was a structurally different idea.&lt;/p&gt;
&lt;h2&gt;4. BlueKeep, CVE-2018-0886, and the Two Failure Modes of CredSSP&lt;/h2&gt;
&lt;p&gt;The same NLA that mitigates BlueKeep also introduces CVE-2018-0886. The CVE class is not a coincidence. CredSSP is both the protocol that gates the pre-auth channel-setup code &lt;em&gt;and&lt;/em&gt; the protocol whose own logic now has to be correct. Two CVEs anchor this section, six years apart.&lt;/p&gt;
&lt;h3&gt;CVE-2018-0886: CredSSP Remote Code Execution&lt;/h3&gt;
&lt;p&gt;On March 13, 2018, Microsoft patched a CredSSP logical flaw discovered by Preempt (now CrowdStrike) [@crowdstrike-credssp]. The NVD record states the vulnerability allows &quot;a remote code execution vulnerability due to how CredSSP validates request during the authentication process&quot; [@nvd-2018-0886] and classifies it as CWE-287 (Improper Authentication). The affected matrix is wide: Windows Server 2008 SP2 and R2 SP1, Windows 7 SP1, Windows 8.1 / RT 8.1, Windows Server 2012 and R2, Windows 10 (1507 through 1709), Windows Server 2016, and Windows Server version 1709 [@nvd-2018-0886].&lt;/p&gt;
&lt;p&gt;The mechanism is a man-in-the-middle injection. Preempt&apos;s disclosure timeline (verbatim from the CrowdStrike advisory) is &quot;20/08/2017: Initial disclosure to MSRC; 30/08/2017: MS repro attack and acknowledge issue; 18/09/2017: Microsoft requested an extension on 90 days SLA; 12/03/2018: Microsoft fixes CVE-2018-0886 as part of March patch Tuesday&quot; [@crowdstrike-credssp]. The attack scenarios are real-world: ARP poisoning on a flat LAN, KRACK against a poorly-patched Wi-Fi network, or a vulnerable router on the path between client and target.&lt;/p&gt;

The vulnerability consists of a logical flaw in Credential Security Support Provider protocol (CredSSP), which is used by RDP (Remote Desktop Protocol) and Windows Remote Management (WinRM)... The vulnerability can be exploited by attackers by employing a man-in-the-middle attack. -- CrowdStrike (formerly Preempt) [@crowdstrike-credssp]
&lt;p&gt;The architectural lesson is that CredSSP&apos;s role as a security boundary creates a second security boundary inside the protocol itself. NLA gates the pre-auth code path &lt;em&gt;behind CredSSP&lt;/em&gt;. If CredSSP has a bug, NLA does not protect anything beyond it; the bug &lt;em&gt;is&lt;/em&gt; the pre-auth code path.&lt;/p&gt;
&lt;h3&gt;The AllowEncryptionOracle deployment incident&lt;/h3&gt;
&lt;p&gt;KB4093492 (May 8, 2018) is the worked example of how a protocol-layer compatibility shim becomes a deployment hazard [@msl-kb4093492]. The patch introduced a three-state registry value at &lt;code&gt;HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters\AllowEncryptionOracle&lt;/code&gt;. The states are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Force Updated Clients (0)&lt;/strong&gt; -- the client refuses to communicate with non-patched servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mitigated (1)&lt;/strong&gt; -- the client accepts patched servers and refuses unpatched servers (default after May 8, 2018).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vulnerable (2)&lt;/strong&gt; -- the client accepts both, preserving compatibility with unpatched servers (transitional default).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On May 8, 2018, Microsoft flipped the default from &lt;code&gt;Vulnerable (2)&lt;/code&gt; to &lt;code&gt;Mitigated (1)&lt;/code&gt;. The KB article states bluntly: &quot;By default, after this update is installed, patched clients cannot communicate with unpatched servers&quot; [@msl-kb4093492]. RDP between patched and unpatched estates broke worldwide for a week. The diagnostic Event ID 6041 (&lt;code&gt;LsaSrv&lt;/code&gt;, &quot;Error encountered while reading from the protected payload of the bilateral exchange&quot;) appeared in millions of system logs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A protocol-layer compatibility shim is itself a deployment surface. The May 2018 default-flip from &lt;code&gt;Vulnerable (2)&lt;/code&gt; to &lt;code&gt;Mitigated (1)&lt;/code&gt; [@msl-kb4093492] was correct as a security posture and disastrous as a rollout sequence. Patch the servers before the clients; verify with &lt;code&gt;Get-ItemProperty &apos;HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters&apos;&lt;/code&gt;; do not assume that &quot;GPO push to clients&quot; is the right first step.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;CVE-2019-0708: BlueKeep&lt;/h3&gt;
&lt;p&gt;On May 14, 2019, Microsoft shipped a fix for a use-after-free in the &lt;code&gt;MS_T120&lt;/code&gt; virtual-channel binding handler inside &lt;code&gt;termdd.sys&lt;/code&gt; -- the kernel-mode driver that handles RDP transport-layer code [@wiki-bluekeep]. The vulnerability, named BlueKeep by Kevin Beaumont on Twitter and discovered by the UK National Cyber Security Centre [@wiki-bluekeep], allowed pre-authentication remote code execution at &lt;code&gt;SYSTEM&lt;/code&gt; on the target. The NVD record (CVE-2019-0708) lists references to PacketStorm exploits, Siemens advisories, the MSRC vendor advisory, and the CISA Known Exploited Vulnerabilities catalog [@nvd-2019-0708].&lt;/p&gt;
&lt;p&gt;The affected matrix is &quot;Windows XP, Windows Vista, Windows 7, Windows Server 2003, Windows Server 2008, and Windows Server 2008 R2&quot; [@wiki-bluekeep]. Microsoft issued out-of-band patches for the end-of-life operating systems (Windows XP and Server 2003) [@wiki-bluekeep] -- a step the company reserves for vulnerabilities expected to be weaponised at scale. CISA, the NSA, and Microsoft all issued emergency advisories.&lt;/p&gt;

The common shorthand for BlueKeep is that it was &quot;a vulnerability in pre-NLA RDP.&quot; The shorthand is wrong, and the precision matters because it sets up the wrong intuition for every authentication mode that follows.&lt;p&gt;NLA existed natively in every BlueKeep-affected operating system from Windows Vista onward -- Windows 7, Server 2008, and Server 2008 R2 all shipped NLA support, and many of those estates ran with NLA on. Windows XP and Server 2003 did not ship NLA natively; CredSSP / NLA was retrofitted to both via KB951608 (March 2009) [@wiki-bluekeep]. BlueKeep is a vulnerability in the channel-setup code reachable when NLA is &lt;em&gt;not enforced&lt;/em&gt; -- the channel-setup code being the &lt;code&gt;MS_T120&lt;/code&gt; virtual-channel binder in &lt;code&gt;termdd.sys&lt;/code&gt;, which is reachable only after the negotiation header but before the basic-settings exchange when CredSSP is not gating the path [@wiki-bluekeep].&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;requestedProtocols = PROTOCOL_RDP (0x00)&lt;/code&gt; and the server permits it, the client skips the CredSSP gate and walks straight into the basic-settings exchange. The &lt;code&gt;MS_T120&lt;/code&gt; channel binding happens in that pre-authentication window. NLA closes that window by requiring &lt;code&gt;PROTOCOL_HYBRID (0x02)&lt;/code&gt; (or &lt;code&gt;_HYBRID_EX 0x08&lt;/code&gt;) before any RDP-stack code beyond the negotiation header runs. The mitigation guidance every operator received in May 2019 -- &quot;enable NLA&quot; -- worked because of this layering, not because BlueKeep was a vulnerability in a pre-NLA era.&lt;/p&gt;
&lt;p&gt;The framing matters because the same precision applies to every later mode. Restricted Admin, Remote Credential Guard, and PRT-over-RDP all sit on top of CredSSP gating (or, for PRT-over-RDP, on top of a different gate entirely). None of them protect anything in the channel-setup phase. NLA does.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The BlueKeep out-of-band patch for Windows XP and Server 2003 [@wiki-bluekeep] is one of the rare exceptions to Microsoft&apos;s end-of-life policy. The 2017 WannaCry / EternalBlue cycle made Microsoft cautious about wormable pre-auth RCE on end-of-life operating systems; BlueKeep met the same threshold. The pattern -- a pre-auth RCE in long-deployed Windows networking code, with end-of-life patches issued out-of-band -- recurs about once every five years.&lt;/p&gt;
&lt;p&gt;NLA mitigates BlueKeep but does not stop Pass-the-Hash. CredSSP itself has a non-trivial logical-attack surface. The architectural response to the Pass-the-Hash failure had already shipped five years before BlueKeep -- and it took the opposite approach. Instead of delivering the credential more securely, it stopped delivering the credential at all.&lt;/p&gt;
&lt;h2&gt;5. Restricted Admin: User Is the Machine (2013)&lt;/h2&gt;
&lt;p&gt;On October 17, 2013, Microsoft shipped a feature whose entire architectural insight fits in one sentence: stop delivering the user&apos;s credential to the target, and let the target log the user on as the target itself.&lt;/p&gt;
&lt;p&gt;The feature is Restricted Admin mode, part of the CredSSP protocol family. It first shipped with Windows 8.1, Windows Server 2012 R2, and Windows RT 8.1 at RTM. Microsoft Security Advisory 2871997 states this verbatim: &quot;Supported editions of Windows 8.1, Windows Server 2012 R2, and Windows RT 8.1 already include these features and do not need the 2871997 update&quot; [@ms-adv-2871997]. The advisory is the load-bearing primary that pins the October 2013 ship date, because the advisory itself was the &lt;em&gt;backport&lt;/em&gt; notice for older operating systems.&lt;/p&gt;

The shorthand &quot;Restricted Admin shipped with KB2871997 in 2014&quot; is a recurring misreading worth defusing. Restricted Admin shipped on three separate dates:&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;October 17, 2013&lt;/strong&gt; -- Windows 8.1, Windows Server 2012 R2, and Windows RT 8.1 RTM. Restricted Admin is in the box. No update required [@ms-adv-2871997].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;May 13, 2014&lt;/strong&gt; -- KB2871997 backported the &lt;em&gt;CredSSP-layer&lt;/em&gt; support to Windows 7, Windows Server 2008 R2, Windows 8, Windows Server 2012, and Windows RT [@ms-adv-2871997]. The advisory text reads verbatim: &quot;On May 13, 2014, Microsoft released the 2871997 update for supported editions of Windows 8, Windows RT, Windows Server 2012, Windows 7, and Windows Server 2008 R2 that improves credential protection and domain authentication controls to reduce credential theft. This update provides additional protection for the Local Security Authority (LSA), adds a restricted admin mode for Credential Security Support Provider (CredSSP)...&quot; [@ms-adv-2871997].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;October 14, 2014&lt;/strong&gt; -- the &lt;em&gt;RDP-client-side&lt;/em&gt; backport in KB2984972 (Win 7 / Server 2008 R2), KB2984976 (Win 7), KB2984981 (Win 7), and KB2973501 (Server 2012). Without the client-side update, Win 7 clients could not &lt;em&gt;initiate&lt;/em&gt; a Restricted Admin RDP connection even though their CredSSP layer supported the protocol after May 2014 [@ms-adv-2871997].&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you read &quot;2014 KB2871997&quot; as the introduction date, the feature looks like a Windows-7-and-back retrofit. It is the opposite: Restricted Admin was a Windows 8.1 RTM feature whose value to the existing fleet was unlocked by two later backports.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The wire protocol&lt;/h3&gt;
&lt;p&gt;Restricted Admin opt-in lives in two bytes on the wire, both documented verbatim in MS-RDPBCGR. The client sets &lt;code&gt;RDP_NEG_REQ.flags &amp;amp; RESTRICTED_ADMIN_MODE_REQUIRED = 0x01&lt;/code&gt; to signal the request: &quot;Indicates that the client requires credential-less logon over CredSSP (also known as &apos;restricted admin mode&apos;). If the server supports this mode then it is acceptable for the client to send empty credentials in the &lt;strong&gt;TSPasswordCreds&lt;/strong&gt; structure&quot; [@rdpbcgr-negreq]. The server confirms support by setting &lt;code&gt;RDP_NEG_RSP.flags &amp;amp; RESTRICTED_ADMIN_MODE_SUPPORTED = 0x08&lt;/code&gt; in the response: &quot;Indicates that the server supports credential-less logon over CredSSP&quot; [@rdpbcgr-negrsp].&lt;/p&gt;
&lt;p&gt;The CredSSP exchange itself runs end-to-end. TLS handshake completes; SPNEGO selects Kerberos or NTLM; the user authenticates. At step 6 -- where classic CredSSP would send a populated &lt;code&gt;TSPasswordCreds&lt;/code&gt; -- the client sends an &lt;em&gt;empty&lt;/em&gt; &lt;code&gt;TSPasswordCreds&lt;/code&gt;. No password, no NT-OWF, no forwarded TGT. The target receives no credential material.&lt;/p&gt;

A CredSSP sub-mode signalled by the `RDP_NEG_REQ.flags &amp;amp; RESTRICTED_ADMIN_MODE_REQUIRED 0x01` bit [@rdpbcgr-negreq]. The client completes authentication to the target but sends an empty `TSPasswordCreds` payload; the target then logs the user on using the target&apos;s own machine account, restricting the session to actions the local Administrators group can perform. Introduced at Windows 8.1 / Server 2012 R2 RTM (October 17, 2013) [@ms-adv-2871997]; backported to Windows 7 / Server 2008 R2 via KB2871997 (May 13, 2014) and the October 14, 2014 client-side KBs.
&lt;h3&gt;The mechanism: the user becomes the machine&lt;/h3&gt;
&lt;p&gt;When the target sees an empty &lt;code&gt;TSPasswordCreds&lt;/code&gt; and the &lt;code&gt;RESTRICTED_ADMIN_MODE_REQUIRED&lt;/code&gt; flag, it does not refuse the logon. It logs the user on by impersonating the target&apos;s own machine account (&lt;code&gt;&amp;lt;TARGETNAME&amp;gt;$&lt;/code&gt;). The machine account is in the local Administrators group by default; the user requesting the session must already be a member of the same group on the target. Microsoft Learn states the access prerequisite for Restricted Admin as &quot;Membership of &lt;strong&gt;Administrators&lt;/strong&gt; group on remote host&quot; [@msl-rcg].&lt;/p&gt;

sequenceDiagram
    autonumber
    participant C as Client
    participant T as Target
    participant K as KDC
    C-&amp;gt;&amp;gt;T: RDP_NEG_REQ (PROTOCOL_HYBRID 0x02 + flags 0x01)
    T--&amp;gt;&amp;gt;C: RDP_NEG_RSP (flags 0x08 supported)
    C-&amp;gt;&amp;gt;T: TLS handshake
    C-&amp;gt;&amp;gt;K: Kerberos AS / TGS for target SPN
    C-&amp;gt;&amp;gt;T: AP-REQ inside SPNEGO (user authenticated)
    C-&amp;gt;&amp;gt;T: TSPasswordCreds (EMPTY)
    T-&amp;gt;&amp;gt;T: LogonUser as TARGET$ machine account
    T-&amp;gt;&amp;gt;T: Token in local Administrators
    C-&amp;gt;&amp;gt;T: RDP session as machine identity
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The breakthrough in Restricted Admin is not a new crypto primitive. It is the realisation that the target can act on the user&apos;s behalf by impersonating &lt;em&gt;itself&lt;/em&gt; -- the target&apos;s own machine account -- which is already a local Administrator. The session has access to local resources, the user has full administrative use of the target, and the target&apos;s &lt;code&gt;lsass.exe&lt;/code&gt; holds no user credential material. The trade is the user&apos;s downstream identity: the session cannot use the user&apos;s credentials for SSO, because the session is not the user. The session is the target.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The trade-offs, verbatim from Microsoft Learn&lt;/h3&gt;
&lt;p&gt;Microsoft Learn publishes a comparison matrix that lays out the trade-offs across Remote Desktop (classic), Remote Credential Guard, and Restricted Admin [@msl-rcg]. For Restricted Admin, the row entries are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prevent use of user&apos;s identity during connection: yes.&lt;/li&gt;
&lt;li&gt;Prevent use of credentials after disconnection: yes.&lt;/li&gt;
&lt;li&gt;Prevent Pass-the-Hash: yes.&lt;/li&gt;
&lt;li&gt;Single sign-on to other systems: no.&lt;/li&gt;
&lt;li&gt;Multi-hop RDP: no.&lt;/li&gt;
&lt;li&gt;Supported authentication: any negotiated by CredSSP (Kerberos or NTLM).&lt;/li&gt;
&lt;li&gt;Credentials supported from the client: signed-in credentials, supplied creds, saved creds.&lt;/li&gt;
&lt;li&gt;RDP access granted with: membership of Administrators on the remote host [@msl-rcg].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the architectural trade. The user is gone the moment the session ends. The attacker who compromises the target during the session gets local Administrator -- but via the machine identity, not via the user&apos;s credential.&lt;/p&gt;
&lt;h3&gt;The RBCD residual class&lt;/h3&gt;
&lt;p&gt;There is no free lunch. Restricted Admin&apos;s &quot;user becomes the machine identity&quot; design creates a different residual surface. If the attacker can write the &lt;code&gt;msDS-AllowedToActOnBehalfOfOtherIdentity&lt;/code&gt; attribute on the target&apos;s computer object in Active Directory, the attacker can use Resource-Based Constrained Delegation to S4U2self + S4U2proxy a Kerberos service ticket for &lt;code&gt;TERMSRV/&amp;lt;target&amp;gt;&lt;/code&gt; and RDP in as &lt;em&gt;anyone&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Elad Shamir documented this in his January 28, 2019 essay &quot;Wagging the Dog&quot; [@shamir-wagging]. The TL;DR points are precise: &quot;Resource-based constrained delegation does not require a forwardable TGS when invoking S4U2Proxy. S4U2Self works on any account that has an SPN, regardless of the state of the &lt;code&gt;TrustedToAuthForDelegation&lt;/code&gt; attribute... if an attacker can control a computer object in Active Directory, then it may be possible to abuse it to compromise the host. S4U2Proxy always produces a forwardable TGS, even if the provided additional TGS in the request was not forwardable&quot; [@shamir-wagging].&lt;/p&gt;

A Kerberos delegation model introduced in Windows Server 2012 where the *target* resource controls which principals can delegate to it via the `msDS-AllowedToActOnBehalfOfOtherIdentity` attribute on its own computer object. Combined with S4U2self and S4U2proxy, RBCD lets any principal that can write that attribute on a target&apos;s computer object obtain a forwardable Kerberos service ticket for any service on the target, including `TERMSRV` (the RDP service principal) [@shamir-wagging]. See the companion *NTLMless* article on this site for the wider S4U and Kerberos-relay discussion.

Computer accounts just got a lot more interesting. Start hunting for more primitives to trigger attack chains! -- Elad Shamir, *Wagging the Dog* (2019) [@shamir-wagging]
&lt;p&gt;Dec0ne&apos;s KrbRelayUp (2022) productionised the chain as &quot;essentially a universal no-fix local privilege escalation in windows domain environments where LDAP signing is not enforced (the default settings)&quot; [@krbrelayup]. The tool wraps Rubeus, KrbRelay, and ADCSPwn for a kerberos-relay-to-RBCD-to-ShadowCred-to-S4U2self-to-SCMUACBypass chain that ends in &lt;code&gt;SYSTEM&lt;/code&gt; on the target. Restricted Admin&apos;s machine-identity-as-session design is exactly what this chain rewards: the attacker walks away with Administrator on the target via the machine identity, with no need to harvest the user&apos;s credential.&lt;/p&gt;
&lt;p&gt;Restricted Admin solves the credential-aggregation problem for jump servers. It does so by eliminating SSO and requiring local Administrator on the target -- which means it cannot be the answer for regular user RDP. If the target needs to act as the user during the session but never holds the user&apos;s credentials, the only place those credentials can live is back on the caller. That insight took three more years to ship.&lt;/p&gt;
&lt;h2&gt;6. Remote Credential Guard: Challenges Redirect to Caller (2016)&lt;/h2&gt;
&lt;p&gt;Remote Credential Guard is what you get if you take Restricted Admin&apos;s &quot;no credential material delivered to the target&quot; rule and add &quot;but the target still needs to act as the user during the session.&quot; Both halves are achievable. The cost is one extra RPC round-trip for every Kerberos operation the session performs -- and a hard dependency on Kerberos itself.&lt;/p&gt;
&lt;p&gt;On August 2, 2016, Microsoft shipped Windows 10 1607 (the Anniversary Update) and Windows Server 2016 with Remote Credential Guard [@msl-rcg]. The Microsoft Learn page states the design property in one sentence: &quot;Remote Credential Guard helps protecting credentials over a Remote Desktop (RDP) connection by redirecting Kerberos requests back to the device that&apos;s requesting the connection. If the target device is compromised, the credentials aren&apos;t exposed because both credential and credential derivatives are never passed over the network to the target device&quot; [@msl-rcg].&lt;/p&gt;
&lt;h3&gt;The wire protocol&lt;/h3&gt;
&lt;p&gt;Opt-in lives in two more flag bits on the CredSSP negotiation. The client sets &lt;code&gt;RDP_NEG_REQ.flags &amp;amp; REDIRECTED_AUTHENTICATION_MODE_REQUIRED = 0x02&lt;/code&gt; to signal the request: the flag indicates &quot;the client can send a redirected logon buffer in the TSRemoteGuardCreds structure&quot; [@rdpbcgr-negreq]. The server confirms by setting &lt;code&gt;RDP_NEG_RSP.flags &amp;amp; REDIRECTED_AUTHENTICATION_MODE_SUPPORTED = 0x10&lt;/code&gt;: &quot;Indicates that the server supports credential-less logon over CredSSP with credential redirection (also known as &apos;Remote Credential Guard&apos;)&quot; [@rdpbcgr-negrsp].&lt;/p&gt;
&lt;p&gt;The credential payload changes. Instead of an empty &lt;code&gt;TSPasswordCreds&lt;/code&gt; (Restricted Admin) or a populated one (classic CredSSP), the client sends a &lt;code&gt;TSRemoteGuardCreds&lt;/code&gt;. The ASN.1 structure is documented verbatim in MS-CSSP [@mscssp-trgcreds]:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TSRemoteGuardCreds ::= SEQUENCE {
    logonCred         [0] TSRemoteGuardPackageCred,
    supplementalCreds [1] SEQUENCE OF TSRemoteGuardPackageCred OPTIONAL
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The payload does not carry a password or a hash. It carries a &lt;em&gt;handle&lt;/em&gt; that lets the target forward authentication challenges back to the caller. MS-CSSP states that &quot;The logon credential is passed to the Negotiate package, which in turn passes the credential to the default authentication package&quot; [@mscssp-trgcreds] -- the negotiate package on the &lt;em&gt;target&lt;/em&gt; receives a redirect-handle, not a credential.&lt;/p&gt;

A CredSSP sub-mode signalled by the `RDP_NEG_REQ.flags &amp;amp; REDIRECTED_AUTHENTICATION_MODE_REQUIRED 0x02` bit [@rdpbcgr-negreq]. The session preserves the user&apos;s identity (unlike Restricted Admin), but every downstream Kerberos operation is performed by the caller&apos;s `lsass.exe` on behalf of the session, forwarded via RPC. Kerberos-only -- no NTLM fallback. Introduced in Windows 10 1607 and Windows Server 2016 (August 2, 2016) [@msl-rcg].

The CredSSP payload used by Remote Credential Guard [@mscssp-trgcreds]. A `SEQUENCE` of a `logonCred` and zero or more `supplementalCreds`, each of type `TSRemoteGuardPackageCred`. The handle is opaque to the target&apos;s authentication package; the target uses it only to request operations from the caller.
&lt;h3&gt;The runtime mechanism&lt;/h3&gt;
&lt;p&gt;The interesting half of Remote CG runs &lt;em&gt;after&lt;/em&gt; the session establishes. Suppose the user, now logged into the target as themselves, opens a file from a network share, mounts a database connection, or starts a second RDP hop. Each of those actions needs a Kerberos service ticket. The session host does not call &lt;code&gt;KerbCreateTicket()&lt;/code&gt; itself. It packages an RPC request and sends it back to the caller&apos;s machine, where the caller&apos;s &lt;code&gt;lsass.exe&lt;/code&gt; performs the operation using the caller&apos;s TGT and session key, then returns the resulting service ticket to the target session.&lt;/p&gt;

sequenceDiagram
    autonumber
    participant C as Client + caller lsass
    participant T as Target session host
    participant K as KDC
    participant F as File server (TGS target)
    C-&amp;gt;&amp;gt;T: RDP_NEG_REQ (flags 0x02)
    T--&amp;gt;&amp;gt;C: RDP_NEG_RSP (flags 0x10)
    C-&amp;gt;&amp;gt;T: TLS handshake
    C-&amp;gt;&amp;gt;T: TSRemoteGuardCreds (handle)
    T-&amp;gt;&amp;gt;T: Session start as user identity
    T-&amp;gt;&amp;gt;C: RPC: TGS-REQ for cifs/fileserver
    C-&amp;gt;&amp;gt;K: TGS-REQ
    K--&amp;gt;&amp;gt;C: TGS-REP
    C--&amp;gt;&amp;gt;T: TGS-REP forwarded back
    T-&amp;gt;&amp;gt;F: AP-REQ with forwarded TGS
    F--&amp;gt;&amp;gt;T: AP-REP, SMB session
&lt;p&gt;The cost is a round-trip per Kerberos operation. Over a LAN that is a few milliseconds; over a WAN that can be 50 milliseconds or more.About 5ms LAN, 50ms WAN, per operation.Remote CG is the only RDP authentication mode with runtime overhead proportional to downstream Kerberos hops. Classic CredSSP, Restricted Admin, and PRT-over-RDP all complete their credential exchange before the session starts; the session then talks to downstream services using credentials that already live on the target. Remote CG keeps the credentials at home and pays an RPC round-trip every time the session needs one. On a chatty workload -- mounting multiple SMB shares, opening many database connections, or chaining RDP hops -- this overhead is observable.&lt;/p&gt;
&lt;h3&gt;The trustlet conditional&lt;/h3&gt;
&lt;p&gt;A common belief about Remote Credential Guard is that it stores the redirected-authentication signing material in the caller&apos;s VBS trustlet &lt;code&gt;LsaIso.exe&lt;/code&gt;. That is &lt;em&gt;partly&lt;/em&gt; true and worth getting precise.&lt;/p&gt;
&lt;p&gt;Remote Credential Guard always redirects challenges to the caller&apos;s &lt;code&gt;lsass.exe&lt;/code&gt;. Where the long-lived signing material lives on the caller is a separate question:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the caller has local Credential Guard enabled, the secrets that back the redirected operations live in the caller&apos;s VTL1 &lt;code&gt;LsaIso.exe&lt;/code&gt; trustlet, isolated from VTL0 user-mode code on the caller.&lt;/li&gt;
&lt;li&gt;If the caller does &lt;em&gt;not&lt;/em&gt; have local Credential Guard, the same material lives in regular user-mode &lt;code&gt;lsass.exe&lt;/code&gt; on the caller, with no VTL1 isolation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The protocol still works in both cases. The redirection still happens. What changes is the protection the caller&apos;s lsass gets from an attacker who later compromises the caller. See the companion &lt;em&gt;Credential Guard&lt;/em&gt; article on this site for the LsaIso internals.&lt;/p&gt;

The &quot;isolated LSA&quot; trustlet that runs in VTL1 under Virtualization-Based Security when Credential Guard is enabled. Stores credential-derived secrets (NTLM hash, Kerberos session key, TGT key, certificate private keys) outside the reach of VTL0 code, including kernel-mode rootkits. Documented in detail in the companion *Credential Guard* article on this site.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Remote Credential Guard always protects the credential from the &lt;em&gt;target&lt;/em&gt; -- the target never sees it, regardless of caller configuration. Remote Credential Guard protects the credential from a &lt;em&gt;post-compromise of the caller&lt;/em&gt; only if the caller has local Credential Guard enabled. The shorthand &quot;Remote CG runs in a VBS trustlet&quot; is true only for the second protection; for the first, the protection lives in the protocol, not in VBS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Kerberos-only -- and what that means for NTLM-mixed estates&lt;/h3&gt;
&lt;p&gt;Remote CG depends on Kerberos. Microsoft Learn is explicit: &quot;Must use Kerberos authentication to connect to the remote host. If the client can&apos;t connect to a domain controller, then RDP attempts to fall back to NTLM. Remote Credential Guard doesn&apos;t allow NTLM fallback because it would expose credentials to risk&quot; [@msl-rcg].&lt;/p&gt;
&lt;p&gt;The reason is structural. NTLM challenge-response sends a chosen-plaintext-style hash of the user&apos;s NT-OWF to the target. Remote CG&apos;s redirection scheme works for Kerberos because the target can ask the caller to mint a service ticket; there is no equivalent operation in NTLM. Remote CG has nothing to redirect that does not eventually require the caller to hand over usable hash material.&lt;/p&gt;
&lt;p&gt;Operationally, this means a 2026 estate that has not finished NTLM deprecation work cannot universally deploy Remote CG. Any RDP path that would fall back to NTLM -- a workgroup machine, a target whose Kerberos SPN registration is broken, a destination behind a DC-isolating firewall rule -- refuses to negotiate Remote CG and either fails the session or falls back to Restricted Admin (depending on the &lt;code&gt;RestrictedRemoteAdministration&lt;/code&gt; GPO setting). See the companion &lt;em&gt;NTLMless&lt;/em&gt; article on this site for the wider context.The UWP Remote Desktop client (the modern Microsoft Store variant) does not support Remote Credential Guard [@msl-rcg]. Only the classic &lt;code&gt;mstsc.exe&lt;/code&gt; Win32 client implements the negotiation. Operators planning to enforce Remote CG must inventory which client binaries connect to which targets; mixing UWP clients into a Remote-CG-mandatory target pool produces silent connection failures.&lt;/p&gt;
&lt;p&gt;Remote CG closes credential-after-disconnection and Pass-the-Hash without sacrificing SSO. It does so by demanding Kerberos and one extra RPC per downstream hop. But the model assumes the user has on-prem-AD-routable Kerberos credentials -- which a Microsoft-Entra-joined laptop without hybrid join does not. The next generation throws away Kerberos entirely.&lt;/p&gt;
&lt;h2&gt;7. PRT-over-RDP: No Password, No Hash, No Ticket (2022)&lt;/h2&gt;
&lt;p&gt;On October 11, 2022, Microsoft shipped the first RDP authentication mode that has no &lt;code&gt;lsass.exe&lt;/code&gt;-credential-delivery story to tell -- because there is no NTLM hash, no Kerberos ticket, and no password in the entire exchange. The credential is a JSON Web Token issued by Microsoft Entra ID.&lt;/p&gt;
&lt;p&gt;The shipping vehicle was the October 2022 cumulative updates: KB5018418 (Windows 11), KB5018410 (Windows 10 20H2+), and KB5018421 (Windows Server 2022). KB5018418&apos;s release notes pin the date precisely: &quot;Release Date: 10/11/2022. Version: OS Build 22000.1098&quot; [@msl-kb5018418]. The Microsoft Learn page for the feature lists the same prerequisites: &quot;The remote PC and your local device must be running one of the following operating systems: Windows 11 with 2022-10 Cumulative Updates for Windows 11 (KB5018418) or later installed&quot; [@msl-prtrdp-mstsc].&lt;/p&gt;

PRT-over-RDP (Microsoft&apos;s official name: &quot;Microsoft Entra single sign-on for Remote Desktop&quot;) is sometimes attributed to &quot;2025.&quot; The misreading is worth defusing because the date affects what one believes is on the deployment frontier.&lt;p&gt;The feature shipped in October 2022 [@msl-prtrdp-mstsc, @msl-kb5018418]. The 2025 angles are two: Windows 11 24H2 reached broad consumer GA in October 2024, and a CredSSP/RDP regression in 24H2 produced session hangs and 65-second disconnects in early 2025; Microsoft rolled out a Known Issue Rollback (&quot;KIR&quot;) via Group Policy in March 2025, and the Windows 11 24H2 release-health page now reads &quot;There are no active known issues at this time&quot; as of March 27, 2025 [@msl-24h2-status]. PRT-over-RDP itself has been a generally-available feature since October 2022; the 2025 events were deployment-stack stability work, not feature ship dates.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The wire protocol&lt;/h3&gt;
&lt;p&gt;Microsoft introduced a new value for &lt;code&gt;requestedProtocols&lt;/code&gt; -- &lt;code&gt;PROTOCOL_RDSAAD = 0x10&lt;/code&gt; [@rdpbcgr-negreq] -- and an entirely new sub-protocol underneath it, &quot;RDS AAD Auth.&quot; Section 5.4.5.4 of MS-RDPBCGR documents it verbatim: &quot;RDS AAD Auth is a variation of Enhanced RDP Security that is used to authenticate a user to an Azure AD-joined device or to a Hybrid Azure AD-joined device. Server authentication, encryption, decryption, and data integrity checks are implemented by using the TLS security protocol, while user authentication is accomplished by exchanging RDS AAD Auth PDUs directly following the TLS handshake&quot; [@rdpbcgr-rdsaad].&lt;/p&gt;
&lt;p&gt;The CredSSP exchange is &lt;em&gt;replaced entirely&lt;/em&gt;. No SPNEGO. No Kerberos. No NTLM. No &lt;code&gt;TSCredentials&lt;/code&gt;. The TLS handshake still happens, because TLS is providing the wire confidentiality and server authentication. After the TLS handshake, the new RDS-AAD-Auth PDU stream takes over. The user-side credential becomes a PRT-cookie scoped to a Conditional Access application.&lt;/p&gt;

A long-lived OAuth refresh token bound to a Microsoft-Entra-joined or hybrid-joined device. The PRT holds proof of the user&apos;s primary authentication (typically including a Windows Hello for Business gesture or FIDO2 ceremony) and signs short-lived JWT cookies that authenticate the user to Entra-ID-integrated applications. The PRT&apos;s signing key is held in the device&apos;s TPM where possible. See the companion *Entra ID and the Primary Refresh Token* article on this site for the full mechanism.

The Cloud Authentication Provider, an LSA authentication package introduced for Microsoft Entra ID (then Azure AD) sign-in. CloudAP runs inside `lsass.exe` and manages PRT issuance, refresh, and the minting of PRT-cookies for downstream authentication. On the target side of a PRT-over-RDP session, CloudAP validates the inbound PRT-cookie and produces the user&apos;s Windows session token without ever contacting a domain controller [@mollema-prt2].
&lt;h3&gt;The mechanism: from &lt;code&gt;.rdp&lt;/code&gt; file to Entra-issued session&lt;/h3&gt;
&lt;p&gt;The client-side flow starts with an &lt;code&gt;.rdp&lt;/code&gt; connection file that includes &lt;code&gt;enablerdsaadauth:i:1&lt;/code&gt; [@msl-rdp-files]. The official MSTSC user-facing label is &quot;Use a web account to sign in to the remote computer.&quot; When the user clicks Connect:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The local CloudAP plugin generates a PRT-cookie scoped to the Conditional Access application &lt;code&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/code&gt; (&quot;Microsoft Remote Desktop&quot;). The Microsoft Learn documentation pins this app ID verbatim: &quot;Conditional Access policies can be applied to the application &lt;strong&gt;Microsoft Remote Desktop&lt;/strong&gt; with ID &lt;strong&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/strong&gt; to control access to the remote PC when single sign-on is enabled&quot; [@msl-prtrdp-mstsc].&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mstsc.exe&lt;/code&gt; opens TCP/3389 (or UDP/3389 for Shortpath) and sends &lt;code&gt;RDP_NEG_REQ&lt;/code&gt; with &lt;code&gt;requestedProtocols = PROTOCOL_RDSAAD (0x10)&lt;/code&gt; [@rdpbcgr-negreq].&lt;/li&gt;
&lt;li&gt;TLS handshake completes; the target&apos;s server certificate is validated against the Entra device-identity.&lt;/li&gt;
&lt;li&gt;The client sends RDS-AAD-Auth PDUs containing the PRT-cookie [@rdpbcgr-rdsaad].&lt;/li&gt;
&lt;li&gt;The target&apos;s CloudAP plugin validates the cookie against Microsoft Entra ID. Entra ID evaluates the Conditional Access policies that target &lt;code&gt;a4a365df-...&lt;/code&gt;, including device compliance, location, sign-in risk, and Authentication Strength.&lt;/li&gt;
&lt;li&gt;On success, Entra ID issues an access token scoped to the target. The target&apos;s CloudAP plugin signs the user in. The RDP session starts.&lt;/li&gt;
&lt;/ol&gt;

sequenceDiagram
    autonumber
    participant C as Caller (CloudAP + mstsc)
    participant T as Target (CloudAP + termsrv)
    participant E as Microsoft Entra ID
    C-&amp;gt;&amp;gt;C: CloudAP mints PRT-cookie for a4a365df
    C-&amp;gt;&amp;gt;T: RDP_NEG_REQ (PROTOCOL_RDSAAD 0x10)
    T--&amp;gt;&amp;gt;C: RDP_NEG_RSP (PROTOCOL_RDSAAD selected)
    C-&amp;gt;&amp;gt;T: TLS handshake
    C-&amp;gt;&amp;gt;T: RDS-AAD-Auth PDU with PRT-cookie
    T-&amp;gt;&amp;gt;E: Validate cookie + apply CA policies
    E--&amp;gt;&amp;gt;T: Access token for target SPN
    T-&amp;gt;&amp;gt;T: CloudAP signs user in
    C-&amp;gt;&amp;gt;T: RDP session as Entra principal

The `.rdp` connection-file property that opts into RDS-AAD-Auth (PRT-over-RDP) [@msl-rdp-files]. Verbatim from Microsoft Learn: &quot;Determines whether the client will use Microsoft Entra ID to authenticate to the remote PC. When used with Azure Virtual Desktop, this provides a single sign-on experience. This property replaces the property `targetisaadjoined`.&quot; The two valid values are `0` (disable) and `1` (enable).
&lt;h3&gt;Two GUIDs, two purposes&lt;/h3&gt;
&lt;p&gt;PRT-over-RDP brings two distinct Microsoft Entra IDs into play, and the article must keep them separate.The user-facing Conditional Access application &quot;Microsoft Remote Desktop&quot; has the application ID &lt;code&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/code&gt; [@msl-prtrdp-mstsc]. The AVD-side configuration uses a distinct service principal &quot;Windows Cloud Login&quot; with ID &lt;code&gt;270efc09-cd0d-444b-a71f-39af4910ec45&lt;/code&gt; [@msl-avdsso], configured via &lt;code&gt;Update-MgServicePrincipal ... -Settings @{ isRemoteDesktopProtocolEnabled = $true }&lt;/code&gt;. Both IDs exist for legitimate reasons; both pages are alive; they configure different layers. The Conditional Access policy in the admin console targets &lt;code&gt;a4a365df-...&lt;/code&gt;; the AVD-side enablement flag flips on &lt;code&gt;270efc09-...&lt;/code&gt;. The &lt;code&gt;a4a365df-...&lt;/code&gt; application is the user-facing PRT-cookie audience; Conditional Access policies that gate RDP sign-in (require WHfB, require compliant device, block from foreign geographies) target this app ID. The &lt;code&gt;270efc09-...&lt;/code&gt; service principal is the AVD-side enablement flag for the configuration described in Azure Virtual Desktop documentation: &quot;Your session hosts must be Microsoft Entra joined or Microsoft Entra hybrid joined. Session hosts joined to Microsoft Entra Domain Services or to Active Directory Domain Services only aren&apos;t supported&quot; [@msl-avdsso].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PRT-over-RDP is the first RDP authentication mode whose wire-protocol selector does not have a CredSSP underneath it. &lt;code&gt;PROTOCOL_RDSAAD (0x10)&lt;/code&gt; [@rdpbcgr-negreq] is its own path. The user&apos;s credential is a JWT cookie [@msl-prtrdp-mstsc] signed by a key derived from the device&apos;s session key (and bound to the TPM where possible). There is no NTLM hash to extract from the target; there is no Kerberos session key to relay. The residual surface is PRT-extraction from the &lt;em&gt;caller&apos;s&lt;/em&gt; CloudAP cache -- not from the target.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PRT-over-RDP is the first RDP authentication mode that is intrinsically phishing-resistant when paired with Windows Hello for Business [@msl-prtrdp-mstsc]. It is also the first mode that requires both endpoints to be Microsoft-Entra-joined (or hybrid-joined) [@msl-avdsso]. A 2026 enterprise estate ends up running all five modes in parallel -- none retiring the others -- because no single Pareto-optimal point dominates the others.&lt;/p&gt;
&lt;h2&gt;8. Five Modes, One Matrix, 2026 Operational Reality&lt;/h2&gt;
&lt;p&gt;By 2026 the RDP authentication stack ships five compositional modes, negotiated by two distinct mechanisms in MS-RDPBCGR. The &lt;code&gt;requestedProtocols&lt;/code&gt; field [@rdpbcgr-negreq] selects between classic RDP, TLS, CredSSP, and RDS-AAD-Auth. The &lt;code&gt;flags&lt;/code&gt; byte signals the Restricted Admin and Remote Credential Guard sub-modes inside CredSSP. A real estate runs all five in different connection paths. None of them retire the others.&lt;/p&gt;
&lt;p&gt;The Microsoft Learn page for Remote Credential Guard publishes the canonical practitioner-grade comparison matrix [@msl-rcg]. Reproducing it verbatim:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Remote Desktop&lt;/th&gt;
&lt;th&gt;Remote Credential Guard&lt;/th&gt;
&lt;th&gt;Restricted Admin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Single sign-on (SSO) for sessions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-hop RDP&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prevent use of user&apos;s identity during connection&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prevent use of credentials after disconnection&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prevent Pass-the-Hash&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supported authentication&lt;/td&gt;
&lt;td&gt;Any negotiable by SSP&lt;/td&gt;
&lt;td&gt;Kerberos only&lt;/td&gt;
&lt;td&gt;Any negotiable by SSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credentials supported from the client device&lt;/td&gt;
&lt;td&gt;Signed-in creds, supplied creds, saved creds&lt;/td&gt;
&lt;td&gt;Signed-in creds only&lt;/td&gt;
&lt;td&gt;Signed-in creds, supplied creds, saved creds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDP access granted with&lt;/td&gt;
&lt;td&gt;Remote Desktop Users (target)&lt;/td&gt;
&lt;td&gt;Remote Desktop Users (target)&lt;/td&gt;
&lt;td&gt;Administrators (target)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The matrix anchors three points of precision. First, &quot;Prevent use of user&apos;s identity during connection&quot; is &lt;code&gt;No&lt;/code&gt; for both classic Remote Desktop &lt;em&gt;and&lt;/em&gt; Remote Credential Guard. Only Restricted Admin can promise that the user&apos;s identity is not exercised on the target -- because only Restricted Admin replaces the user&apos;s identity with the target&apos;s machine identity.&lt;/p&gt;
&lt;p&gt;Second, &quot;Prevent use of credentials after disconnection&quot; is &lt;code&gt;Yes&lt;/code&gt; for Remote CG and Restricted Admin but &lt;code&gt;No&lt;/code&gt; for classic. Third, &quot;RDP access granted with&quot; is the &lt;em&gt;access control&lt;/em&gt; primitive, not the &lt;em&gt;credential&lt;/em&gt; primitive: Restricted Admin demands Administrators-group membership on the target because the user is impersonating the machine account.&lt;/p&gt;
&lt;p&gt;The matrix excludes the two non-CredSSP modes -- classic-RDP (&lt;code&gt;PROTOCOL_RDP = 0x00&lt;/code&gt;) and PRT-over-RDP (&lt;code&gt;PROTOCOL_RDSAAD = 0x10&lt;/code&gt;). Extending the matrix to all five modes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Classic RDP&lt;/th&gt;
&lt;th&gt;NLA (CredSSP)&lt;/th&gt;
&lt;th&gt;Restricted Admin&lt;/th&gt;
&lt;th&gt;Remote Credential Guard&lt;/th&gt;
&lt;th&gt;PRT-over-RDP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Wire selector&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PROTOCOL_RDP 0x00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PROTOCOL_HYBRID 0x02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CredSSP + &lt;code&gt;0x01&lt;/code&gt; flag&lt;/td&gt;
&lt;td&gt;CredSSP + &lt;code&gt;0x02&lt;/code&gt; flag&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PROTOCOL_RDSAAD 0x10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential delivered to target&lt;/td&gt;
&lt;td&gt;Password in RC4 channel&lt;/td&gt;
&lt;td&gt;Password in TLS (TSPasswordCreds)&lt;/td&gt;
&lt;td&gt;Empty TSPasswordCreds&lt;/td&gt;
&lt;td&gt;TSRemoteGuardCreds handle&lt;/td&gt;
&lt;td&gt;Entra PRT-cookie (JWT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target session identity&lt;/td&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;Machine (&lt;code&gt;TARGET$&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;User (Entra principal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User credential at target lsass&lt;/td&gt;
&lt;td&gt;Yes (NT-OWF + plaintext)&lt;/td&gt;
&lt;td&gt;Yes (NT-OWF)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSO to downstream services&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (via caller RPC)&lt;/td&gt;
&lt;td&gt;Yes (Entra token cache)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-hop RDP&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (Entra)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requires Administrators on target&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NTLM fallback allowed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-auth-RCE protection (NLA)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (via TLS-first gate)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First shipped&lt;/td&gt;
&lt;td&gt;RDP 4.0 / 1998&lt;/td&gt;
&lt;td&gt;RDP 6.0 / Vista / Nov 2006&lt;/td&gt;
&lt;td&gt;Win 8.1 / Server 2012 R2 / Oct 17, 2013&lt;/td&gt;
&lt;td&gt;Win 10 1607 / Server 2016 / Aug 2, 2016&lt;/td&gt;
&lt;td&gt;KB5018418 / Oct 11, 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canonical CVE&lt;/td&gt;
&lt;td&gt;BlueKeep (channel-setup)&lt;/td&gt;
&lt;td&gt;CVE-2018-0886 (CredSSP RCE)&lt;/td&gt;
&lt;td&gt;RBCD against TERMSRV&lt;/td&gt;
&lt;td&gt;Kerberos RBCD residual&lt;/td&gt;
&lt;td&gt;PRT-extraction at session host&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; The five modes are not strictly ordered. Each occupies a different point on the Pareto frontier of (no-credential-leak × SSO × universality × Kerberos-compatibility). An operator&apos;s job is to choose two of {Entra-issued sessions, on-prem Kerberos SSO, no credential delegation, no local-admin requirement} per RDP pair -- the matrix exists because nobody gets all four.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The 2026 operational defaults split along clean lines. The universal baseline is &lt;code&gt;NLA mandatory&lt;/code&gt; (refuse &lt;code&gt;PROTOCOL_RDP = 0x00&lt;/code&gt;), &lt;code&gt;AllowEncryptionOracle = 0&lt;/code&gt; (Force Updated Clients) [@msl-kb4093492], NTLMv1 disabled at the domain controller, and the target-side GPO &quot;Remote host allows delegation of nonexportable credentials&quot; set so Remote CG can opt in without prompting. On top of that baseline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Admin-tier jump servers: Restricted Admin (&lt;code&gt;RestrictedRemoteAdministration&lt;/code&gt; GPO mode 1, &quot;Require Restricted Admin&quot;) [@msl-rcg].&lt;/li&gt;
&lt;li&gt;User-tier domain RDP: Remote Credential Guard (&lt;code&gt;RestrictedRemoteAdministration&lt;/code&gt; GPO mode 2 for &quot;Require Remote Credential Guard&quot;, or mode 3 for &quot;Restrict credential delegation&quot; -- the composite that prefers Remote CG and falls back to Restricted Admin when Remote CG cannot complete) [@msl-rcg].&lt;/li&gt;
&lt;li&gt;Entra-joined estates and AVD / Cloud-PC: PRT-over-RDP via &lt;code&gt;enablerdsaadauth:i:1&lt;/code&gt; [@msl-rdp-files], gated by Conditional Access on the &lt;code&gt;a4a365df-...&lt;/code&gt; app [@msl-prtrdp-mstsc].&lt;/li&gt;
&lt;li&gt;Legacy compatibility lane: NLA-mandatory classic CredSSP, accepting that the target&apos;s lsass will hold credential material for the session duration.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Regardless of which higher-level mode you choose, four settings should be on every Windows target before you read further: NLA mandatory; &lt;code&gt;AllowEncryptionOracle = 0&lt;/code&gt; [@msl-kb4093492]; NTLMv1 disabled; &quot;Remote host allows delegation of nonexportable credentials&quot; set so Remote CG opt-in does not prompt. These four are the floor. Everything else is a per-target choice on top of them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The matrix is the load-bearing artifact. The next four sections walk what it does not solve: the perimeter mitigations the matrix excludes, the theoretical limits no mode can close, the open problems still in the literature, and the practical guide an operator runs on Monday.&lt;/p&gt;
&lt;h2&gt;9. Adjacent Mitigations: What Else Protects RDP (And What It Does Not Protect)&lt;/h2&gt;
&lt;p&gt;Six adjacent mechanisms get called &quot;RDP security&quot; in vendor marketing. None of them are credential-protection mechanisms. They solve different problems at different layers, and conflating them with the five-mode matrix is the load-bearing operational confusion in modern RDP deployments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remote Desktop Gateway / RDS Gateway.&lt;/strong&gt; A network-layer reverse-proxy that tunnels RDP inside HTTPS on TCP/443. The Gateway terminates the TLS connection from the internet, authenticates the user (often via smart card or Microsoft Entra), and forwards the inner RDP session to the back-end target. Gateway solves &quot;RDP exposed to the internet on TCP/3389.&quot; It does not change which CredSSP sub-mode the inner session negotiates. If the inner session is classic-NLA, the target&apos;s lsass receives the user&apos;s NT-OWF, gateway or not.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A common misconception is that an RDS Gateway &quot;protects credentials.&quot; It does not. The Gateway proxies the RDP session, which still negotiates classic, Restricted Admin, Remote CG, or PRT-over-RDP between the client and the back-end target. The Gateway is a perimeter primitive, not an authentication mode. An operator who deploys RDS Gateway &lt;em&gt;and&lt;/em&gt; enforces Remote CG on the back-end pool has stacked two complementary mitigations. An operator who deploys RDS Gateway and leaves the back-end pool on classic NLA has hardened the perimeter without changing the post-exploitation pivot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Just-Enough Administration (JEA) over PowerShell Remoting.&lt;/strong&gt; The &quot;don&apos;t use RDP for admin in the first place&quot; answer. JEA constrains a PowerShell Remoting session to a curated set of cmdlets executed under a managed virtual account. The transport is WSMan over HTTPS (TCP/5986 with TLS, or TCP/5985 plain), not RDP.&lt;/p&gt;
&lt;p&gt;WSMan has its own CredSSP exposure -- CVE-2018-0886 affects WinRM as well as RDP [@nvd-2018-0886] -- but a JEA endpoint configured for &lt;code&gt;RunAsVirtualAccount&lt;/code&gt; does not delegate the operator&apos;s credentials at all. JEA combined with Remote Credential Guard for the remaining graphical-admin paths is the de facto 2026 reference architecture for admin-tier work.&lt;/p&gt;

A PowerShell-Remoting feature that constrains a remote session to a whitelisted set of commands executed under a managed virtual account, eliminating credential delegation for the operator. JEA is not an RDP mode; it is the &quot;use a different protocol entirely&quot; alternative for administrative tasks that do not need a graphical session. Configured via PowerShell session-configuration files (`.pssc`) and role-capability files (`.psrc`).
&lt;p&gt;&lt;strong&gt;Smart-card and FIDO2 as the underlying credential.&lt;/strong&gt; A user can authenticate to NLA / CredSSP using a smart card (Kerberos with PKINIT) or, for PRT-over-RDP, a Windows Hello for Business or FIDO2 ceremony. The credential type matters for &lt;em&gt;phishing resistance&lt;/em&gt; and for &lt;em&gt;replayability&lt;/em&gt;. It does not change what the target ends up with after the CredSSP exchange completes. A smart-card-backed NLA session still ends with a Kerberos session key in the target&apos;s &lt;code&gt;lsass.exe&lt;/code&gt;. Phishing-resistant authentication is necessary but not sufficient; the post-exchange credential isolation lives in Restricted Admin, Remote CG, or PRT-over-RDP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Azure Bastion / AWS Session Manager.&lt;/strong&gt; Cloud-managed jump-host services that present a web UI and proxy RDP / SSH connections to back-end VMs without exposing 3389 to the internet. Bastion handles the network-layer exposure problem. The credential-protection guarantees still depend on which CredSSP sub-mode the inner RDP session negotiates. Bastion does not magic away the requirement to also configure Restricted Admin or Remote CG on the back-end pool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RDP-over-QUIC / AVD RDP Shortpath.&lt;/strong&gt; The Microsoft Azure Virtual Desktop &quot;Shortpath&quot; feature carries RDP traffic over UDP, with four variants documented verbatim on Microsoft Learn: &quot;RDP Shortpath for managed networks; RDP Shortpath for managed networks with ICE/STUN; RDP Shortpath for public networks with ICE/STUN; RDP Shortpath for public networks via TURN&quot; [@msl-shortpath]. The transport sits on QUIC, the UDP-based transport standardised in RFC 9000 [@rfc-9000].&lt;/p&gt;
&lt;p&gt;The Tech Community announcement of public-networks GA states verbatim: &quot;We are pleased to announce the general availability of RDP Shortpath for public networks... We started deploying RDP Shortpath in September and now the feature is 100% rolled out&quot; [@tc-shortpath-blog]; the announcement is dated late 2022. The TURN-relayed variant is GA in late 2024 per the Microsoft AVD blog series.&lt;/p&gt;

Microsoft&apos;s QUIC/UDP transport for RDP, used inside Azure Virtual Desktop and Windows 365 [@msl-shortpath]. Four variants cover managed networks, ICE/STUN-mediated traversal, public-network NAT traversal, and TURN-relayed paths through restrictive NAT. The authentication stack above the transport is unchanged from TCP-based RDP; QUIC replaces TCP/TLS with UDP/QUIC at the bottom of the stack only.
&lt;p&gt;What changes with QUIC transport is the detection surface. Network sensors keyed on &lt;code&gt;TCP/3389 + TLS handshake + CredSSP TS Request/Response&lt;/code&gt; see nothing for an AVD Shortpath session; what they should look for instead is &lt;code&gt;UDP/&amp;amp;lt;ephemeral&amp;amp;gt; + QUIC handshake + STUN/ICE candidate exchange&lt;/code&gt;. The credential-protection guarantees are &lt;em&gt;identical&lt;/em&gt; to the underlying RDP mode; the visibility-engineering work is real.FreeRDP and xrdp -- the two widely-used non-Microsoft RDP stacks -- implement a subset of the five modes. xrdp does not implement CredSSP server-side, so xrdp targets are reachable only via &lt;code&gt;PROTOCOL_RDP&lt;/code&gt; or &lt;code&gt;PROTOCOL_SSL&lt;/code&gt;; an operator who enables NLA on an xrdp host has effectively prevented Windows clients from connecting. FreeRDP supports classic, NLA / CredSSP, and Restricted Admin as a client, but does not implement Remote Credential Guard or PRT-over-RDP. Detection engineering for mixed-stack estates must account for these limits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conditional Access, Microsoft Entra PIM, Just-In-Time access.&lt;/strong&gt; Workflow-level controls that &lt;em&gt;grant&lt;/em&gt; RDP access (the user must satisfy a PIM activation request before the JIT-provisioned admin group adds them; the Conditional Access policy must pass before the PRT-cookie is minted). These are orthogonal to what happens &lt;em&gt;during&lt;/em&gt; the RDP session. A PIM-elevated admin session that runs classic-NLA RDP still leaves the admin&apos;s credential in the target&apos;s lsass when the session ends.&lt;/p&gt;
&lt;p&gt;None of these mechanisms change the credential the target ends up with. That is the load-bearing fact. The next section walks the four classes of attack that no RDP mode -- regardless of how many adjacent mitigations are stacked -- can close.&lt;/p&gt;
&lt;h2&gt;10. Theoretical Limits: The Four Things RDP Authentication Cannot Close&lt;/h2&gt;
&lt;p&gt;The five modes between them close credential delegation (Restricted Admin), credential-after-disconnection (Restricted Admin and Remote CG), the password-and-hash entirely (PRT-over-RDP), and the pre-auth channel-setup surface (NLA). The four limits below are what is left. Each has a formal lower bound. None is closable by a sixth mode.&lt;/p&gt;
&lt;h3&gt;Limit 1: The SYSTEM-on-target floor&lt;/h3&gt;
&lt;p&gt;Any RDP mode that grants the user an actionable identity on the target permits a &lt;code&gt;SYSTEM&lt;/code&gt;-on-target adversary to use that identity for as long as the user stays connected. The session host kernel must materialise the user&apos;s security token to enforce ACL checks. An attacker with &lt;code&gt;SYSTEM&lt;/code&gt; on the target can call &lt;code&gt;OpenProcessToken&lt;/code&gt; against any process in the user&apos;s session, then &lt;code&gt;DuplicateTokenEx&lt;/code&gt; and &lt;code&gt;ImpersonateLoggedOnUser&lt;/code&gt; to perform actions as the user. Microsoft Learn encodes this as &quot;Prevent use of user&apos;s identity during connection: No&quot; for both Remote Desktop and Remote Credential Guard [@msl-rcg].&lt;/p&gt;

Remote Credential Guard helps protecting credentials over a Remote Desktop (RDP) connection by redirecting Kerberos requests back to the device that&apos;s requesting the connection. If the target device is compromised, the credentials aren&apos;t exposed because both credential and credential derivatives are never passed over the network to the target device. -- Microsoft Learn [@msl-rcg]
&lt;p&gt;That sentence promises protection of &lt;em&gt;credentials&lt;/em&gt;. It does not promise protection of the user&apos;s identity in-session. The two properties are different and only one is closable by an authentication-protocol redesign.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; No RDP mode can simultaneously give the target the ability to act as the user &lt;em&gt;and&lt;/em&gt; prevent a SYSTEM adversary on the target from acting as the user. The defense is not to close in-session impersonation; the defense is to not put the user on a compromised target. Restricted Admin closes it by giving up the user&apos;s identity entirely. Every mode that preserves the user&apos;s identity (classic, NLA, Remote CG, PRT-over-RDP) leaves the SYSTEM-on-target floor open.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Limit 2: The CredSSP residue&lt;/h3&gt;
&lt;p&gt;Even on a 2026 Entra-joined estate using PRT-over-RDP, CredSSP remains on the wire for every RDP path touching a non-Entra-joined endpoint. Legacy admin tooling, multi-hop sessions into on-prem servers, RDS deployments, hybrid scenarios -- all of them fall back to CredSSP. CredSSP has shipped at least one logical-flaw RCE (CVE-2018-0886) [@nvd-2018-0886] and the &lt;code&gt;AllowEncryptionOracle&lt;/code&gt; compatibility window [@msl-kb4093492] remains a deployment hazard whenever a new CredSSP patch ships.&lt;/p&gt;
&lt;p&gt;The architectural answer (deprecate CredSSP wholly, replace it with RDS-AAD-Auth for every RDP path) is not on Microsoft&apos;s public roadmap. MS-CSSP&apos;s current revision is 21.0, dated April 23, 2024 [@mscssp-index] -- the protocol is actively maintained, not deprecated. Operationally, CredSSP is a permanent fixture of any Windows estate that includes on-premises identity.&lt;/p&gt;
&lt;h3&gt;Limit 3: The RBCD-against-TERMSRV class&lt;/h3&gt;
&lt;p&gt;Restricted Admin closes credential delegation. It does not close the machine-identity-as-attack-primitive class. Any principal that can write &lt;code&gt;msDS-AllowedToActOnBehalfOfOtherIdentity&lt;/code&gt; on a target&apos;s computer object can S4U2self + S4U2proxy a &lt;code&gt;TERMSRV/&amp;lt;target&amp;gt;&lt;/code&gt; ticket and RDP in as any account -- including a Domain Admin [@shamir-wagging]. Dec0ne&apos;s KrbRelayUp productionises this as &quot;a universal no-fix local privilege escalation in windows domain environments where LDAP signing is not enforced (the default settings)&quot; [@krbrelayup].&lt;/p&gt;
&lt;p&gt;The phrase &quot;no-fix&quot; is doing real work. The chain depends on default LDAP-signing settings, default kerberos delegation behaviour for resource-based constrained delegation, and the user-creatable computer object quota of &lt;code&gt;ms-DS-MachineAccountQuota = 10&lt;/code&gt; [@shamir-wagging] -- four configurations that are independently defensible per protocol but that compose into the chain. Tightening any one of them mitigates KrbRelayUp; Microsoft has not chosen to do so by default.&lt;/p&gt;
&lt;h3&gt;Limit 4: The Entra-only / on-prem-only gap&lt;/h3&gt;
&lt;p&gt;PRT-over-RDP requires both endpoints to be Microsoft-Entra-joined or hybrid-joined [@msl-prtrdp-mstsc, @msl-avdsso]. Restricted Admin and Remote CG require Kerberos and on-premises Active Directory. The two architectures do not compose: a non-hybrid Entra-joined estate cannot deploy Restricted Admin or Remote CG; an on-prem-AD estate cannot deploy PRT-over-RDP. Hybrid join is the bridging primitive, but it has its own configuration cost (Entra Connect, device-write-back, certificate trust, password hash sync), and many estates have hybrid-join enabled for only a subset of devices.&lt;/p&gt;

The five RDP modes occupy different points on a four-axis Pareto frontier: (no-credential-leak × SSO × universality × Kerberos-compatibility). No mode dominates any other on all four axes simultaneously.&lt;ul&gt;
&lt;li&gt;Classic credential delegation gives universality and SSO and Kerberos-compatibility, at the cost of credential-leak protection.&lt;/li&gt;
&lt;li&gt;NLA via CredSSP adds pre-auth-RCE gating, but the credential-leak axis is identical to classic.&lt;/li&gt;
&lt;li&gt;Restricted Admin gives credential-leak protection and Kerberos-compatibility, at the cost of SSO and the universality of the &quot;any user&quot; property (because of the Administrators-group requirement).&lt;/li&gt;
&lt;li&gt;Remote Credential Guard gives credential-leak protection and SSO, at the cost of universality (Kerberos-only).&lt;/li&gt;
&lt;li&gt;PRT-over-RDP gives credential-leak protection and SSO and modern phishing-resistant authentication, at the cost of Kerberos-compatibility and the Entra-joined-everywhere requirement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The operator&apos;s job is composition: pick the mode that matches each RDP pair&apos;s constraints, accept that two of the four axes will fail for that pair, and use the other modes for the other pairs. A typical 2026 enterprise pool runs (at least) NLA-mandatory classic for legacy, Restricted Admin for admin-tier, Remote CG for user-tier domain RDP, and PRT-over-RDP for AVD. Four authentication modes on one estate is the normal state.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Three of the four limits are bounded: they may shrink as Microsoft deprecates NTLM, ships VBS-trustlet protection for CloudAP on session hosts, or unifies the Entra and on-prem identity surfaces. One is architectural and applies to every authentication protocol ever proposed: &lt;code&gt;SYSTEM&lt;/code&gt; on the target equals the user&apos;s identity for as long as the user stays connected. The next section walks the active research where the bounded limits are still moving.&lt;/p&gt;
&lt;h2&gt;11. Open Problems: Where Research and Operations Are Still Moving&lt;/h2&gt;
&lt;p&gt;Five open problems sit on the RDP authentication stack right now. Three are research-class. Two are operational-class. None has a &quot;just ship a patch&quot; answer.&lt;/p&gt;
&lt;h3&gt;Open Problem 1: PRT extraction at the session host&lt;/h3&gt;
&lt;p&gt;A session host that serves N concurrent RDP-in connections with PRT-over-RDP caches at least N CloudAP entries. A SYSTEM adversary on the session host can dump all of them. Dirk-jan Mollema&apos;s August 2020 follow-up to his original PRT abuse research [@mollema-prt2] describes the underlying primitive in collaboration with Benjamin Delpy (Mimikatz author). The verbatim quote is decisive:&lt;/p&gt;

Around the same time Benjamin Delpy took up my &apos;challenge&apos; of recovering PRT data from lsass with mimikatz. We combined forces and ended up with tooling that is not only able to extract the PRT and associated cryptographic keys (such as the session key) from memory, but can also use these keys to create new SSO cookies or modify existing ones. Interesting enough, it turns out that 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. -- Dirk-jan Mollema, *Digging Further Into the Primary Refresh Token* (2020) [@mollema-prt2]
&lt;p&gt;That property -- the PRT and session key are recoverable from CloudAP &lt;code&gt;lsass.exe&lt;/code&gt; memory irrespective of TPM protection -- holds on the &lt;em&gt;receiving&lt;/em&gt; session host the same way it holds on the &lt;em&gt;issuing&lt;/em&gt; device. This is the 2026 mirror of the 2008 &quot;RDP into the Citrix jump server and dump credentials&quot; problem.&lt;/p&gt;
&lt;p&gt;The architectural answer is to run CloudAP inside a VBS trustlet (analogous to &lt;code&gt;LsaIso.exe&lt;/code&gt; for NTLM and Kerberos) on every session host. Microsoft has not publicly committed to that work. The original PRT post [@mollema-prt1] notes that &quot;if there is no TPM the keys are stored in software... If a TPM is present, the keys required to request or use the PRT are protected by the TPM and can&apos;t be extracted under normal circumstances&quot; -- but &quot;under normal circumstances&quot; excludes the &lt;code&gt;SYSTEM&lt;/code&gt;-on-target adversary the session-host scenario assumes.&lt;/p&gt;
&lt;h3&gt;Open Problem 2: CredSSP is not deprecable&lt;/h3&gt;
&lt;p&gt;MS-CSSP revision 21.0 (April 23, 2024) [@mscssp-index] is a maintained protocol; no Microsoft roadmap deprecates it. The role transitions from &quot;the authentication layer for RDP and WinRM&quot; to &quot;the compatibility shim for everything that does not speak RDS-AAD-Auth,&quot; but compatibility shims do not retire in the absence of a forcing function. Every CredSSP CVE in the next decade lands on a still-deployed surface. The &lt;code&gt;AllowEncryptionOracle&lt;/code&gt; deployment pattern [@msl-kb4093492] is not the last of its kind.&lt;/p&gt;
&lt;h3&gt;Open Problem 3: Remote CG NTLM exclusion and the hybrid-estate user experience&lt;/h3&gt;
&lt;p&gt;Remote Credential Guard is Kerberos-only [@msl-rcg]. The &lt;code&gt;RestrictedRemoteAdministration&lt;/code&gt; GPO mode 3 is the composite &quot;Restrict credential delegation&quot; policy that prefers Remote CG and falls back to Restricted Admin when Remote CG cannot complete [@msl-rcg].&lt;/p&gt;
&lt;p&gt;The result is a fragmented user experience: on Monday a user RDPs to &lt;code&gt;server01&lt;/code&gt; and gets Remote CG (SSO works); on Tuesday the user RDPs to &lt;code&gt;server02&lt;/code&gt; whose DC trust path has a transient routing problem and gets Restricted Admin (SSO does not work). The user observes &quot;RDP to server02 is broken&quot; and files a ticket; the platform team observes &quot;the GPO fall-back is working as designed.&quot; This pattern persists until NTLM is universally removed from the estate, which is itself an open multi-year project. See the companion &lt;em&gt;NTLMless&lt;/em&gt; article on this site for the wider deprecation arc.&lt;/p&gt;
&lt;h3&gt;Open Problem 4: The Entra / on-prem composition gap&lt;/h3&gt;
&lt;p&gt;Real enterprise estates are a Venn diagram of Entra-only desktops, hybrid-joined Windows 11 laptops, and on-prem-AD-only servers. PRT-over-RDP works between Entra-joined and hybrid-joined endpoints [@msl-prtrdp-mstsc]; Restricted Admin and Remote CG work between Kerberos-routable endpoints; the four cells in the 2x2 ({client-Entra, client-AD} x {target-Entra, target-AD}) have different authentication paths. Hybrid join bridges the two architectures for devices that support it. A conjecture worth stating plainly: the two architectures will not fully converge before 2030. The intermediate state -- four authentication modes running on the same estate at the same time -- is the steady state, not a transition.&lt;/p&gt;
&lt;h3&gt;Open Problem 5: Windows 11 24H2 KIR as a deployment-fault recovery surface&lt;/h3&gt;
&lt;p&gt;In early 2025, Windows 11 24H2 shipped an authentication-stack change that broke CredSSP-mediated RDP in a subset of configurations, manifesting as session hangs at the login screen or 65-second disconnects mid-session. Microsoft rolled out a Known Issue Rollback (&quot;KIR&quot;) via Group Policy in March 2025; the policy name is widely attributed in industry press as &quot;Windows 11 24H2 and Windows Server 2025 KB5053598 250314_20401 Known Issue Rollback.&quot; The Windows 11 24H2 release-health page now reads &quot;There are no active known issues at this time&quot; as of March 27, 2025 [@msl-24h2-status], indicating the KIR cycle has resolved.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Known Issue Rollback is now a primary operational tool for managing authentication-stack regressions in Windows. When a feature update breaks CredSSP, RDP, or any other load-bearing authentication primitive, the response is no longer &quot;wait for the next monthly patch&quot;; it is &quot;push the KIR-disable Group Policy through your management plane.&quot; Operators should be familiar with the KIR mechanism &lt;em&gt;before&lt;/em&gt; the next regression cycle. The 2025 24H2 RDP regression [@msl-24h2-status] is the worked example.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Open Problem 6: QUIC transport and the network-layer detection surface&lt;/h3&gt;
&lt;p&gt;AVD RDP Shortpath for public networks has been GA since late 2022 [@tc-shortpath-blog, @msl-shortpath]; the TURN-relayed variant is GA from late 2024. QUIC is specified in RFC 9000 [@rfc-9000] and replaces TCP/3389 + TLS with UDP/&amp;lt;ephemeral&amp;gt; + QUIC. The IOC surface for &quot;RDP session in progress&quot; changes from a TLS handshake plus CredSSP TS Request/Response on TCP/3389 to a QUIC handshake plus STUN/ICE candidate exchange on a UDP port assigned at runtime.&lt;/p&gt;
&lt;p&gt;Network-detection engineering for this surface has not finished re-tuning. A reasonable conjecture: the QUIC detection surface stabilises by 2027 around per-tenant Conditional Access app-ID telemetry (sign-in log entries for &lt;code&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/code&gt; [@msl-prtrdp-mstsc]) rather than network-layer packet signatures.&lt;/p&gt;
&lt;p&gt;These open problems share a common shape. They are all about &lt;em&gt;composition&lt;/em&gt;. The five modes have to interoperate; the credential-isolation has to extend to the session host; the detection has to span TCP and QUIC. None of them has a feature-shaped answer. The next section walks the practical guide for making the existing primitives work in your environment today.&lt;/p&gt;
&lt;h2&gt;12. Practical Guide, FAQ, and Closing&lt;/h2&gt;
&lt;p&gt;Here is what to do with this stack this week, depending on which side of the wire you live on.&lt;/p&gt;
&lt;h3&gt;For the administrator / platform engineer&lt;/h3&gt;
&lt;p&gt;The four configurations below are the universal baseline. Set them everywhere; then choose a higher-level mode per RDP pair.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enforce NLA.&lt;/strong&gt; GPO: &lt;em&gt;Computer Configuration -&amp;gt; Administrative Templates -&amp;gt; Windows Components -&amp;gt; Remote Desktop Services -&amp;gt; Remote Desktop Session Host -&amp;gt; Security -&amp;gt; Require user authentication for remote connections by using Network Level Authentication&lt;/em&gt;. Verify with PowerShell: &lt;code&gt;(Get-WmiObject -Class Win32_TSGeneralSetting -Namespace root\CIMV2\TerminalServices -Filter &quot;TerminalName=&apos;RDP-Tcp&apos;&quot;).UserAuthenticationRequired&lt;/code&gt; returns &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enforce &lt;code&gt;AllowEncryptionOracle = 0&lt;/code&gt;.&lt;/strong&gt; Set &quot;Encryption Oracle Remediation&quot; to &quot;Force Updated Clients&quot; under &lt;em&gt;Computer Configuration -&amp;gt; Administrative Templates -&amp;gt; System -&amp;gt; Credentials Delegation&lt;/em&gt;. Verify with &lt;code&gt;Get-ItemProperty &apos;HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters&apos;&lt;/code&gt; -- the &lt;code&gt;AllowEncryptionOracle&lt;/code&gt; value should be &lt;code&gt;0&lt;/code&gt; [@msl-kb4093492].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy Restricted Admin or Remote CG.&lt;/strong&gt; Set the &lt;code&gt;RestrictedRemoteAdministration&lt;/code&gt; GPO mode under &quot;Restrict delegation of credentials to remote servers&quot; to &lt;code&gt;1&lt;/code&gt; (Require Restricted Admin), &lt;code&gt;2&lt;/code&gt; (Require Remote Credential Guard), or &lt;code&gt;3&lt;/code&gt; (Restrict credential delegation -- the composite that prefers Remote CG and falls back to Restricted Admin when Remote CG cannot complete). Pair with the target-side &quot;Remote host allows delegation of nonexportable credentials&quot; GPO [@msl-rcg].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable PRT-over-RDP for Entra-joined estates.&lt;/strong&gt; Distribute &lt;code&gt;.rdp&lt;/code&gt; files with &lt;code&gt;enablerdsaadauth:i:1&lt;/code&gt; [@msl-rdp-files]; gate sign-in via Conditional Access policy on the application &lt;code&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/code&gt; [@msl-prtrdp-mstsc]; pair with Windows Hello for Business or FIDO2 for phishing-resistant primary authentication.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before enabling Remote CG on any target, inventory the NTLM-only paths into it. Workgroup machines, machines whose target SPN registration is incomplete, machines reachable only across DC-isolating firewall rules -- all of them will refuse the Remote CG negotiation when Kerberos cannot complete.&lt;/p&gt;

The exact GPO and registry settings for the four-baseline configuration:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NLA&lt;/strong&gt;: &lt;code&gt;Computer Configuration -&amp;gt; Administrative Templates -&amp;gt; Windows Components -&amp;gt; Remote Desktop Services -&amp;gt; Remote Desktop Session Host -&amp;gt; Security -&amp;gt; Require user authentication for remote connections by using Network Level Authentication&lt;/code&gt;. Registry: &lt;code&gt;HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\UserAuthentication = 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AllowEncryptionOracle&lt;/strong&gt;: &lt;code&gt;Computer Configuration -&amp;gt; Administrative Templates -&amp;gt; System -&amp;gt; Credentials Delegation -&amp;gt; Encryption Oracle Remediation -&amp;gt; Force Updated Clients&lt;/code&gt;. Registry: &lt;code&gt;HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters\AllowEncryptionOracle = 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restricted Admin / Remote CG&lt;/strong&gt;: &lt;code&gt;Computer Configuration -&amp;gt; Administrative Templates -&amp;gt; System -&amp;gt; Credentials Delegation -&amp;gt; Restrict delegation of credentials to remote servers&lt;/code&gt;. Registry: &lt;code&gt;HKLM\Software\Policies\Microsoft\Windows\CredentialsDelegation\RestrictedRemoteAdministration = 1|2|3&lt;/code&gt; (1 = Require Restricted Admin; 2 = Require Remote Credential Guard; 3 = Restrict credential delegation -- the composite that prefers Remote CG and falls back to Restricted Admin when Remote CG cannot complete) [@msl-rcg].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Target-side trust&lt;/strong&gt;: &lt;code&gt;Computer Configuration -&amp;gt; Administrative Templates -&amp;gt; System -&amp;gt; Credentials Delegation -&amp;gt; Remote host allows delegation of nonexportable credentials -&amp;gt; Enabled&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apply via Group Policy, Intune ADMX, or local policy. Reboot is not required for these settings to take effect, but new RDP connections must be opened after the policy applies for the negotiation to reflect the new state.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;For the security researcher&lt;/h3&gt;
&lt;p&gt;The wire-level distinction between modes is observable. Capture an RDP handshake with Wireshark (the RDP dissector is built-in). The &lt;code&gt;RDP_NEG_REQ&lt;/code&gt; and &lt;code&gt;RDP_NEG_RSP&lt;/code&gt; packets show the &lt;code&gt;requestedProtocols&lt;/code&gt; and &lt;code&gt;selectedProtocol&lt;/code&gt; fields and the Restricted Admin / Remote CG flag bits. &lt;code&gt;PROTOCOL_HYBRID = 0x02&lt;/code&gt; plus &lt;code&gt;RESTRICTED_ADMIN_MODE_SUPPORTED = 0x08&lt;/code&gt; in the response indicates the negotiated CredSSP sub-mode.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// MS-RDPBCGR selectedProtocol bitmask: see learn.microsoft.com/.../b2975bdc function decodeNegRsp(selectedProtocol, flags) {   const protocols = [];   if ((selectedProtocol &amp;amp; 0x01) !== 0) protocols.push(&quot;PROTOCOL_SSL (TLS)&quot;);   if ((selectedProtocol &amp;amp; 0x02) !== 0) protocols.push(&quot;PROTOCOL_HYBRID (CredSSP)&quot;);   if ((selectedProtocol &amp;amp; 0x04) !== 0) protocols.push(&quot;PROTOCOL_RDSTLS&quot;);   if ((selectedProtocol &amp;amp; 0x08) !== 0) protocols.push(&quot;PROTOCOL_HYBRID_EX&quot;);   if ((selectedProtocol &amp;amp; 0x10) !== 0) protocols.push(&quot;PROTOCOL_RDSAAD (Entra)&quot;);   if (protocols.length === 0) protocols.push(&quot;PROTOCOL_RDP (classic)&quot;);   const subModes = [];   if ((flags &amp;amp; 0x08) !== 0) subModes.push(&quot;Restricted Admin supported&quot;);   if ((flags &amp;amp; 0x10) !== 0) subModes.push(&quot;Remote Credential Guard supported&quot;);   console.log(&quot;Protocols:&quot;, protocols.join(&quot; + &quot;));   console.log(&quot;Sub-modes:&quot;, subModes.length ? subModes.join(&quot;, &quot;) : &quot;none&quot;); } // Example: PROTOCOL_HYBRID + Remote CG flag decodeNegRsp(0x02, 0x10); // Example: PROTOCOL_RDSAAD (PRT-over-RDP) decodeNegRsp(0x10, 0x00);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;On the target, the distinction between Restricted Admin and the other modes shows up in the Windows event log. Event 4624 (logon-type 10, RemoteInteractive) records the subject; for Restricted Admin the subject is &lt;code&gt;TARGET$&lt;/code&gt; (the machine account) while for classic, Remote CG, and PRT-over-RDP it is the user&apos;s account. A quick check: open an elevated &lt;code&gt;cmd&lt;/code&gt; inside the RDP session and run &lt;code&gt;whoami&lt;/code&gt;. Returns &lt;code&gt;&amp;lt;target&amp;gt;$&lt;/code&gt; under Restricted Admin; returns the user&apos;s domain or Entra account otherwise.&lt;/p&gt;
&lt;p&gt;{`&lt;/p&gt;
Mirrors four PowerShell checks an operator runs on a Windows target.
&lt;p&gt;checks = {
    &quot;NLA required&quot;: &quot;(Get-WmiObject Win32_TSGeneralSetting -Namespace root/CIMV2/TerminalServices -Filter \&quot;TerminalName=&apos;RDP-Tcp&apos;\&quot;).UserAuthenticationRequired&quot;,
    &quot;AllowEncryptionOracle == 0&quot;: &quot;(Get-ItemProperty &apos;HKLM:/Software/Microsoft/Windows/CurrentVersion/Policies/System/CredSSP/Parameters&apos;).AllowEncryptionOracle&quot;,
    &quot;Restricted Admin GPO&quot;: &quot;(Get-ItemProperty &apos;HKLM:/Software/Policies/Microsoft/Windows/CredentialsDelegation&apos;).RestrictedRemoteAdministration&quot;,
    &quot;PRT-over-RDP opt-in&quot;: &quot;Select-String -Pattern &apos;enablerdsaadauth:i:1&apos; (Get-ChildItem -Recurse -Filter *.rdp)&quot;,
}
expected = {&quot;NLA required&quot;: 1, &quot;AllowEncryptionOracle == 0&quot;: 0,
            &quot;Restricted Admin GPO&quot;: &quot;1, 2, or 3&quot;, &quot;PRT-over-RDP opt-in&quot;: &quot;present in .rdp file&quot;}
for label, cmd in checks.items():
    print(f&quot;{label}: expect {expected[label]}; run -&amp;gt; {cmd}&quot;)
`}&lt;/p&gt;
&lt;p&gt;For PRT-over-RDP detection, the highest-fidelity signal lives in Microsoft Entra ID&apos;s sign-in logs: a successful sign-in entry against the application ID &lt;code&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/code&gt; [@msl-prtrdp-mstsc] indicates an RDP session was authenticated using the Entra path. Correlate with the target&apos;s local event log for the same user / timestamp to verify session establishment.&lt;/p&gt;
&lt;h3&gt;For the red-team operator&lt;/h3&gt;
&lt;p&gt;The post-exploitation pivots differ by mode, as the four &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; dumps in §1 illustrated:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Classic / NLA&lt;/strong&gt;: Pass-the-Hash against the target&apos;s &lt;code&gt;lsass.exe&lt;/code&gt; cache is trivial. &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; returns user&apos;s NT-OWF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restricted Admin&lt;/strong&gt;: PtH yields the target&apos;s machine account (&lt;code&gt;TARGET$&lt;/code&gt;), not the user. Pivot: RBCD against &lt;code&gt;TERMSRV&lt;/code&gt; via the Wagging-the-Dog chain [@shamir-wagging] or KrbRelayUp [@krbrelayup].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote Credential Guard&lt;/strong&gt;: PtH yields nothing useful for the user; the credential never reached the target. Pivot: in-session impersonation via SYSTEM-on-target while the user is connected; use &lt;code&gt;OpenProcessToken&lt;/code&gt; against a user-session process.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PRT-over-RDP&lt;/strong&gt;: No NTLM hash, no Kerberos ticket. Pivot: PRT extraction (&lt;code&gt;sekurlsa::cloudap&lt;/code&gt;) from the target&apos;s CloudAP cache; the Dirk-jan Mollema PRT-cookie-mint flow [@mollema-prt2] applies.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;For the detection engineer&lt;/h3&gt;
&lt;p&gt;Five signal sources, ranked by fidelity:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;selectedProtocol&lt;/code&gt; and &lt;code&gt;flags&lt;/code&gt; fields in the &lt;code&gt;RDP_NEG_RSP&lt;/code&gt; packet identify the negotiated mode at the wire.&lt;/li&gt;
&lt;li&gt;Event 4624 (logon-type 10) on the target identifies the session principal: machine-account name under Restricted Admin, user name otherwise.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational&lt;/code&gt; Event ID 1149 records successful RDP session connections.&lt;/li&gt;
&lt;li&gt;RDS Gateway connection logs, if a Gateway is in the path.&lt;/li&gt;
&lt;li&gt;Microsoft Entra ID sign-in logs for the application ID &lt;code&gt;a4a365df-50f1-4397-bc59-1a1564b8bb9c&lt;/code&gt; [@msl-prtrdp-mstsc] for PRT-over-RDP sessions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For BlueKeep-class detection on patched estates: the &lt;code&gt;MS_T120&lt;/code&gt; channel name appearing in an RDP &lt;code&gt;Erect Domain Request&lt;/code&gt; or &lt;code&gt;Attach User Request&lt;/code&gt; is a high-fidelity IOC. Legitimate clients do not request that channel by name [@wiki-bluekeep].&lt;/p&gt;

No. NLA is the *policy* on the RDP server that requires authentication before the RDP session is established (before the basic-settings exchange and virtual-channel binding). NLA is *implemented by* CredSSP -- the Credential Security Support Provider, specified in MS-CSSP [@mscssp-index] -- running over TLS. When operators say &quot;we require NLA,&quot; they mean &quot;we accept only `requestedProtocols = PROTOCOL_HYBRID` or `PROTOCOL_HYBRID_EX`,&quot; both of which select CredSSP under the hood [@rdpbcgr-credssp].

No. NLA prevents pre-authentication remote code execution in the RDP channel-setup code (mitigating BlueKeep [@wiki-bluekeep]) and reduces denial-of-service exposure. NLA does not change what the target&apos;s `lsass.exe` ends up holding at the end of the CredSSP exchange. A CredSSP `TSPasswordCreds` exchange ends with the user&apos;s password or NT-OWF cached on the target. Mimikatz `sekurlsa::logonpasswords` works against a 2012-era NLA-authenticated target identically to a 1998-era classic-RDP target.

No. Restricted Admin makes the target act as *itself*: the user&apos;s session runs as the target&apos;s machine account, the target sees no user credentials, but the connecting user must already be a local Administrator on the target [@msl-rcg]. Remote Credential Guard makes the target forward operations *back to the caller*: the session runs as the user, SSO works, the connecting user only needs Remote Desktop Users group membership on the target, but the path is Kerberos-only [@msl-rcg]. Restricted Admin gave up SSO to gain universality. Remote CG kept SSO at the cost of NTLM compatibility.

Partly. Remote Credential Guard always redirects Kerberos challenges to the caller&apos;s `lsass.exe`. The long-lived signing material lives in the caller&apos;s VTL1 `LsaIso.exe` trustlet *only if* the caller has local Credential Guard enabled. Without local Credential Guard, the material is in regular user-mode `lsass.exe` on the caller. The protocol works in both cases; the protection from a post-compromise of the caller is stronger with local Credential Guard on.

No. BlueKeep (CVE-2019-0708) is a use-after-free in the `MS_T120` virtual-channel binding inside `termdd.sys`, reachable in the RDP channel-setup phase *before* NLA gating completes [@wiki-bluekeep, @nvd-2019-0708]. NLA shipped natively in every BlueKeep-affected operating system from Windows Vista onward (Windows 7, Server 2008, Server 2008 R2); Windows XP and Server 2003 did not ship NLA natively but could be retrofitted via KB951608 (March 2009). The mitigation guidance &quot;enable NLA&quot; worked because NLA *gates* that pre-auth code path behind a successful CredSSP handshake. BlueKeep is &quot;a vulnerability in the channel-setup code reachable when NLA is not enforced,&quot; not &quot;a vulnerability in an era predating NLA.&quot;

No. Microsoft Entra single sign-on for Remote Desktop shipped in the October 11, 2022 cumulative updates: KB5018418 for Windows 11, KB5018410 for Windows 10 20H2 and later, and KB5018421 for Windows Server 2022 [@msl-prtrdp-mstsc, @msl-kb5018418]. The 2025 angles are the Windows 11 24H2 broad GA wave (October 2024) and the March 2025 Known Issue Rollback for a 24H2 RDP regression [@msl-24h2-status]. The Entra-PRT-backed RDP authentication mode itself has been generally available since late 2022.

Only for Azure Virtual Desktop and Windows 365. On-premises RDP defaults to TCP/3389 with TLS. RDP-over-QUIC is implemented in AVD&apos;s RDP Shortpath feature, with four variants documented in Microsoft Learn covering managed networks, ICE/STUN traversal, public networks via ICE/STUN, and TURN-relayed paths [@msl-shortpath]. Public-networks GA was announced in late 2022 [@tc-shortpath-blog]; the TURN-relayed variant is GA in late 2024. The QUIC transport itself is RFC 9000 [@rfc-9000]. The authentication stack above the transport is unchanged from TCP-based RDP.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The five modes coexist because each makes a different Pareto trade-off, and no estate has the luxury of running just one. The matrix is the artifact you live with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From password-in-the-pipe to cloud-issued session, every generation of RDP authentication has closed exactly the failure mode that motivated the next. Classic delegation made the credential-aggregation problem visible; NLA gated the pre-auth channel and inadvertently introduced its own CVE class. Restricted Admin gave up the user&apos;s identity to stop delivering credentials; Remote Credential Guard recovered the identity at the cost of Kerberos-only routing; PRT-over-RDP abandoned both Kerberos and NTLM in favour of Entra-issued cookies bound to per-application Conditional Access policies.&lt;/p&gt;
&lt;p&gt;The residual classes -- RBCD against &lt;code&gt;TERMSRV&lt;/code&gt;, PRT extraction at the session host, the in-session SYSTEM-on-target floor -- are what is left, and at least one of them is architectural rather than fixable.&lt;/p&gt;
&lt;p&gt;The four &lt;code&gt;sekurlsa::logonpasswords&lt;/code&gt; dumps in §1 are no longer four mysteries. They are four readings of a single matrix: which &lt;code&gt;requestedProtocols&lt;/code&gt; byte the client offered, which &lt;code&gt;flags&lt;/code&gt; bits the server accepted, and which credential payload (or which absence of a credential payload) the target received before it built the user&apos;s session. The protocol selectors are public. The trade-offs are public. The matrix is yours to compose, RDP pair by RDP pair, with full knowledge of what each choice puts on the wire and what it leaves in &lt;code&gt;lsass.exe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;rdp-security-nla-restricted-admin-remote-credential-guard-and-the-prt-over-rdp-pivot&quot; keyTerms={[
  { term: &quot;NLA&quot;, definition: &quot;Network Level Authentication: the server-side policy that requires the client to authenticate via CredSSP before the RDP session is established.&quot; },
  { term: &quot;CredSSP&quot;, definition: &quot;Credential Security Support Provider, the wire protocol that NLA uses: TLS + SPNEGO + Kerberos or NTLM + TSCredentials.&quot; },
  { term: &quot;TSPasswordCreds&quot;, definition: &quot;The CredSSP payload carrying the user&apos;s password or NT-OWF to the target; empty in Restricted Admin mode.&quot; },
  { term: &quot;TSRemoteGuardCreds&quot;, definition: &quot;The CredSSP payload used by Remote Credential Guard, carrying a redirect handle rather than a credential.&quot; },
  { term: &quot;Restricted Admin&quot;, definition: &quot;CredSSP sub-mode that sends empty credentials and runs the user&apos;s session as the target&apos;s machine account.&quot; },
  { term: &quot;Remote Credential Guard&quot;, definition: &quot;CredSSP sub-mode that forwards every Kerberos operation back to the caller&apos;s lsass via RPC, keeping credentials off the target.&quot; },
  { term: &quot;PRT&quot;, definition: &quot;Primary Refresh Token, the long-lived Entra refresh token bound to a Microsoft-Entra-joined device, signed by a TPM-protected key where possible.&quot; },
  { term: &quot;PROTOCOL_RDSAAD&quot;, definition: &quot;RDP_NEG_REQ.requestedProtocols value 0x10 selecting the RDS-AAD-Auth protocol used by PRT-over-RDP.&quot; },
  { term: &quot;RBCD&quot;, definition: &quot;Resource-Based Constrained Delegation; lets a target&apos;s computer object specify which principals can delegate to it via the msDS-AllowedToActOnBehalfOfOtherIdentity attribute.&quot; },
  { term: &quot;BlueKeep&quot;, definition: &quot;CVE-2019-0708, a pre-auth RCE in the MS_T120 virtual-channel handler in termdd.sys, reachable when NLA is not enforced.&quot; }
]} questions={[
  { q: &quot;Why does enabling NLA not prevent Pass-the-Hash?&quot;, a: &quot;Because NLA controls when authentication happens, not what is delivered. The CredSSP exchange still ends with the user&apos;s NT-OWF in the target&apos;s lsass.&quot; },
  { q: &quot;What is the central trade-off between Restricted Admin and Remote Credential Guard?&quot;, a: &quot;Restricted Admin gives up SSO and multi-hop in exchange for universality (any negotiable authentication); Remote CG keeps SSO and multi-hop in exchange for Kerberos-only routing and an extra RPC per downstream hop.&quot; },
  { q: &quot;Why does PRT-over-RDP not need CredSSP?&quot;, a: &quot;Because RDS-AAD-Auth replaces the CredSSP exchange with TLS plus RDS-AAD-Auth PDUs carrying an Entra-issued PRT-cookie; there is no SPNEGO, no Kerberos, and no NTLM on the wire.&quot; },
  { q: &quot;What is the residual attack class for Restricted Admin?&quot;, a: &quot;RBCD against TERMSRV: any principal that can write msDS-AllowedToActOnBehalfOfOtherIdentity on the target&apos;s computer object can S4U2self+S4U2proxy a TERMSRV ticket and RDP in as anyone.&quot; },
  { q: &quot;What is the SYSTEM-on-target floor?&quot;, a: &quot;The architectural property that any RDP mode preserving the user&apos;s identity on the target permits a SYSTEM adversary on the target to impersonate the user via OpenProcessToken and ImpersonateLoggedOnUser for the connection&apos;s duration.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>rdp</category><category>credssp</category><category>remote-credential-guard</category><category>restricted-admin</category><category>entra-id</category><category>pass-the-hash</category><category>windows-security</category><category>authentication</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>