What a Primary Refresh Token Is and Why Attackers Want It
A Primary Refresh Token (PRT) is a long-lived cryptographic artefact issued by Microsoft Entra ID to registered Windows devices after a user authenticates. Once issued, it functions as a master session credential: Entra ID uses it to issue access tokens and refresh tokens for any cloud application without prompting for credentials or MFA. From an attacker’s perspective, stealing a PRT is functionally equivalent to owning the user’s identity — with no further authentication required.
PRTs live in the Local Security Authority process (lsass.exe on Windows) and in the CloudAP key storage provider, protected by the device’s TPM on modern hardware. On devices without TPM binding, they are accessible to a process running as SYSTEM or via certain userland abuse paths.
Storm-2372, a threat actor tracked by Microsoft throughout 2025, ran a large-scale PRT theft campaign targeting government, defence, and NGO organisations. The campaign combined device code phishing to register attacker-controlled devices, PRT harvesting using the RequestAADRefreshToken technique, and lateral movement across the Microsoft 365 tenancy. It is the clearest documented case of PRT theft at operational scale.
Attack Paths
RequestAADRefreshToken / ROADtoken
The most documented PRT theft tool is ROADtoken (part of the AADInternals and ROAD toolset). Running on a device as an interactive user, it calls the internal Windows API RequestAADRefreshToken to extract the device’s PRT directly from CloudAP. No elevated privileges required beyond normal user access. The extracted PRT can then be used from any other machine via the ROADtools library to request access tokens.
ROADtoken.exe -e
The MITRE technique is T1528 (Steal Application Access Token) but also intersects with T1557 (Adversary-in-the-Middle) when used in conjunction with device code phishing.
AiTM via EvilGinx / Evilginx3
An alternative path doesn’t steal the PRT itself but achieves the same outcome: capturing session cookies via adversary-in-the-middle phishing proxies. Evilginx3 and similar frameworks sit between the user and the legitimate Entra ID login page, forwarding credentials and MFA proofs to the real service while capturing the resulting session cookies. The attacker replays these cookies from their own browser, bypassing MFA entirely.
This technique generates a different detection signal from direct PRT extraction and requires separate hunting queries.
Pass-the-PRT
Once an attacker has a PRT (extracted or from a compromised device), they inject it into a new session using the PrtUpdateCookie mechanism or via BrowserCore. Tools like AADInternals’s Join-AADIntDeviceToAzureAD and Get-AADIntAccessTokenForIntuneMDM are commonly used here.
Sigma Rule: ROADtoken PRT Extraction
title: ROADtoken PRT Extraction via RequestAADRefreshToken
id: a1c9e2d4-f3b8-4e7a-9c5d-1e2f3a4b5c6d
status: experimental
description: Detects invocation of the RequestAADRefreshToken API, commonly used by ROADtoken and similar tools to extract Entra ID Primary Refresh Tokens without elevated privileges.
references:
- https://o365blog.com/post/prt/
author: SOC Analyst Hub
date: 2026/06/15
tags:
- attack.credential_access
- attack.t1528
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith:
- '\ROADtoken.exe'
CommandLine|contains:
- 'RequestAADRefreshToken'
selection_generic:
CommandLine|contains|all:
- 'RequestAADRefreshToken'
- 'CloudAP'
condition: selection or selection_generic
falsepositives:
- Microsoft diagnostic tooling (rare)
level: high
KQL: Entra ID Sign-in Anomalies After PRT Abuse
This query hunts for sign-in events from unfamiliar device IDs after a period of normal activity — a key signal when a stolen PRT is used from a new device.
let lookback = 14d;
let recent = 1d;
// Establish baseline device IDs per user
let baselineDevices = SigninLogs
| where TimeGenerated between (ago(lookback) .. ago(recent))
| where ResultType == 0
| summarize BaselineDevices = make_set(DeviceDetail.deviceId) by UserPrincipalName;
// Find sign-ins from devices not in baseline
SigninLogs
| where TimeGenerated > ago(recent)
| where ResultType == 0
| join kind=leftouter baselineDevices on UserPrincipalName
| where not(DeviceDetail.deviceId in (BaselineDevices))
| where isnotempty(DeviceDetail.deviceId)
| extend
DeviceId = tostring(DeviceDetail.deviceId),
DeviceOS = tostring(DeviceDetail.operatingSystem),
IsCompliant = tostring(DeviceDetail.isCompliant),
Location = strcat(LocationDetails.city, ", ", LocationDetails.countryOrRegion)
| project
TimeGenerated,
UserPrincipalName,
DeviceId,
DeviceOS,
IsCompliant,
AppDisplayName,
IPAddress,
Location,
AuthenticationRequirement,
ConditionalAccessStatus
| sort by TimeGenerated desc
A high-confidence refinement: filter for AuthenticationRequirement == "singleFactorAuthentication" on accounts that previously always required MFA — this directly surfaces MFA bypass via PRT replay.
KQL: AiTM Phishing Session Cookie Replay Detection
After AiTM phishing, attacker-controlled sessions typically originate from different IP geographies than the victim’s normal access patterns.
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| summarize
SessionCount = count(),
IPList = make_set(IPAddress),
Countries = make_set(LocationDetails.countryOrRegion),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by UserPrincipalName, CorrelationId
| where array_length(Countries) > 1
| where SessionCount < 5
| extend SuspiciousGeoHop = array_length(Countries) >= 2
| where SuspiciousGeoHop == true
| project
UserPrincipalName,
CorrelationId,
IPList,
Countries,
FirstSeen,
LastSeen
| sort by FirstSeen desc
For higher fidelity, cross-reference with AADUserRiskEvents for unfamiliarFeatures or tokenIssuerAnomaly risk detections, which Entra ID Protection generates for suspicious PRT usage.
KQL: Device Code Authentication Abuse
Device code phishing precedes many PRT theft campaigns. Hunt for device code flows from users who do not normally use them.
SigninLogs
| where TimeGenerated > ago(30d)
| where AuthenticationProtocol == "deviceCode"
| summarize
DeviceCodeAttempts = count(),
IPList = make_set(IPAddress),
AppList = make_set(AppDisplayName),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by UserPrincipalName
| where DeviceCodeAttempts > 0
| sort by DeviceCodeAttempts desc
// Cross-reference with your list of privileged accounts and service accounts
Flag service accounts and privileged roles using device code authentication — there is no legitimate reason for these accounts to authenticate interactively via device code flows.
KQL: AADInternals / ROADtools Service Principal Activity
Tool-specific hunting: AADInternals and ROADtools access Graph API endpoints that legitimate applications do not use in the same combination.
MicrosoftGraphActivityLogs
| where TimeGenerated > ago(7d)
| where RequestUri has_any (
"oauth2/devicecode",
"oauth2/token",
"/me/registeredDevices",
"/devices?",
"joinedTeams"
)
| where UserAgent has_any (
"python-requests",
"AADInternals",
"roadtools",
"Go-http-client"
)
| project TimeGenerated, UserAgent, RequestUri, IPAddress, UserId
| sort by TimeGenerated desc
Key Investigation Steps When PRT Theft Is Suspected
-
Identify the compromised device. Check
DeviceDetail.deviceIdin sign-in logs. If the PRT was stolen and replayed, a new unregistered device ID will appear. Compare against Intune/Entra registered device list. -
Review authentication methods. Pull
AuthenticationDetailsfrom the sign-in record. Legitimate PRT-based authentication showsprimaryAuthenticationMethod: PrimaryRefreshToken. If a PRT was used from a different device, the device compliance state will often show as non-compliant or not evaluated. -
Revoke sessions immediately. Use
Revoke-MgUserSignInSession(Graph PowerShell) or the Entra admin portal to revoke all refresh tokens for the affected user. Note that this revokes existing sessions but does not prevent re-issuance if the device itself is still compromised. -
Rotate device certificate. On the originating device, use
dsregcmd /leaveand re-register to force a new PRT to be issued, invalidating the stolen token. -
Check for new registered apps and OAuth consents. Attackers who have obtained persistent access via PRT often register OAuth applications or grant consent to existing apps to maintain access after token revocation.
Detection Coverage Assessment
| Attack Step | Detection Signal | Coverage |
|---|---|---|
| Device code phishing | SigninLogs — deviceCode protocol | Medium |
| ROADtoken execution | Process creation — ROADtoken.exe | High (if EDR deployed) |
| PRT extracted and replayed | New device ID in sign-in logs | Medium |
| AiTM cookie capture | Geo-impossible sessions | Medium |
| Post-access app registration | AuditLogs — application consent | High |
TPM-backed PRT binding (available on Entra-joined devices with TPM 2.0 and token protection Conditional Access policy) prevents direct PRT extraction. Enabling the Conditional Access token protection policy for Exchange Online, SharePoint, and Teams is the most effective preventive control, though as of mid-2026 it requires devices to be Entra-joined and TPM-capable.