[New Rule] Entra ID device-bound PRT cookie replay from unusual IP - #6706
Conversation
Detect FOCI tooling clients redeeming a stolen workstation PRT off-box, where the token keeps the original deviceid so compliant-device CA can still succeed. Co-authored-by: Cursor <cursoragent@cursor.com>
…sual_ip rule Removed outdated references from the Azure credential access rule.
Updated minimum stack version to the latest supported for 9.X releases.
There was a problem hiding this comment.
Pull request overview
Adds two new Microsoft Entra ID sign-in detection rules intended to identify device-bound PRT (WAM PRT SSO cookie) replay activity (PRTremote/TokenTactics-style), focusing on first-party FOCI tooling clients redeeming PRTs for high-value Microsoft 365 resources from unusual IPs.
Changes:
- Adds a new-terms (KQL) rule to flag first-seen
(device_id, source.ip)pairs for PRT redemptions by selected first-party tooling clients. - Adds an ES|QL correlation rule that compares Windows Sign-In / WAM “device session” IPs to replay IPs for the same user+device.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| rules/integrations/azure/credential_access_entra_id_prt_from_unusual_device_ip.toml | New-terms rule for first-seen device-bound PRT redemption IPs per device. |
| rules/integrations/azure/credential_access_entra_id_prt_cookie_replay_unusual_ip.toml | ES |
Suppressed comments (3)
rules/integrations/azure/credential_access_entra_id_prt_from_unusual_device_ip.toml:94
[[rule.threat]]includes the Initial Access tactic (TA0001), but thetagslist is missing the correspondingTactic: Initial Accesstag; tags should include oneTactic:entry per tactic in the threat mapping.
"Data Source: Microsoft Entra ID Sign-in Logs",
"Platform: Entra ID",
"Tactic: Credential Access",
"Tactic: Defense Evasion",
"Resources: Investigation Guide",
"Rule Type: New Terms",
]
rules/integrations/azure/credential_access_entra_id_prt_cookie_replay_unusual_ip.toml:100
[[rule.threat]]includes the Initial Access tactic (TA0001), but thetagslist is missing the correspondingTactic: Initial Accesstag; tags should include oneTactic:entry per tactic in the threat mapping.
"Platform: Entra ID",
"Tactic: Credential Access",
"Tactic: Defense Evasion",
"Resources: Investigation Guide",
"Rule Type: ESQL",
]
timestamp_override = "event.ingested"
rules/integrations/azure/credential_access_entra_id_prt_cookie_replay_unusual_ip.toml:109
- The ES|QL query comment says "find successful sign-in events where a managed device exists", but the filter only checks that
device_idis not null (it does not check managed/compliant at that point). This makes the inline query documentation misleading.
// find successful sign-in events where a managed device exists
| where event.dataset == "azure.signinlogs"
and azure.signinlogs.properties.status.error_code == 0
and azure.signinlogs.properties.device_detail.device_id is not null
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| creation_date = "2026/08/27" | ||
| integration = ["azure"] | ||
| maturity = "production" | ||
| updated_date = "2026/08/28" |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| | eval Esql.is_device_session = azure.signinlogs.properties.app_display_name == "Windows Sign In" | ||
| or user_agent.original == "Windows-AzureAD-Authentication-Provider/1.0" | ||
|
|
||
| // filter for tooling FOCI clients that can redeem a PRT (replay) | ||
| | eval Esql.is_prt_replay = azure.signinlogs.properties.app_id in ( | ||
| "04b07795-8ddb-461a-bbee-02f9e1bf7b46", // Microsoft Azure CLI | ||
| "1950a258-227b-4e31-a9cf-717495945fc2", // Microsoft Azure PowerShell | ||
| "aebc6443-996d-45c2-90f0-388ff96faa56", // Visual Studio Code | ||
| "14d82eec-204b-4c2f-b7e8-296a70dab67e", // Microsoft Graph Command Line Tools | ||
| "1b730954-1685-4b74-9bfd-dac224a7b894", // Azure Active Directory PowerShell | ||
| "872cd9fa-d31f-45e0-9eab-6e460a02d1f1" // Visual Studio | ||
| ) | ||
|
|
||
| // target resource are common adversary targets for access | ||
| and azure.signinlogs.properties.resource_id in ( | ||
| "00000003-0000-0000-c000-000000000000", // Microsoft Graph | ||
| "00000003-0000-0ff1-ce00-000000000000", // Office 365 SharePoint Online | ||
| "6a9b9266-8161-4a7b-913a-a9eda19da220", // OneDrive for Business | ||
| "00000002-0000-0ff1-ce00-000000000000" // Office 365 Exchange Online | ||
| ) | ||
| and azure.signinlogs.properties.incoming_token_type == "primaryRefreshToken" | ||
| and ( | ||
| azure.signinlogs.properties.device_detail.is_compliant == true | ||
| or azure.signinlogs.properties.device_detail.is_managed == true | ||
| ) |
There was a problem hiding this comment.
| | eval Esql.is_device_session = azure.signinlogs.properties.app_display_name == "Windows Sign In" | |
| or user_agent.original == "Windows-AzureAD-Authentication-Provider/1.0" | |
| // filter for tooling FOCI clients that can redeem a PRT (replay) | |
| | eval Esql.is_prt_replay = azure.signinlogs.properties.app_id in ( | |
| "04b07795-8ddb-461a-bbee-02f9e1bf7b46", // Microsoft Azure CLI | |
| "1950a258-227b-4e31-a9cf-717495945fc2", // Microsoft Azure PowerShell | |
| "aebc6443-996d-45c2-90f0-388ff96faa56", // Visual Studio Code | |
| "14d82eec-204b-4c2f-b7e8-296a70dab67e", // Microsoft Graph Command Line Tools | |
| "1b730954-1685-4b74-9bfd-dac224a7b894", // Azure Active Directory PowerShell | |
| "872cd9fa-d31f-45e0-9eab-6e460a02d1f1" // Visual Studio | |
| ) | |
| // target resource are common adversary targets for access | |
| and azure.signinlogs.properties.resource_id in ( | |
| "00000003-0000-0000-c000-000000000000", // Microsoft Graph | |
| "00000003-0000-0ff1-ce00-000000000000", // Office 365 SharePoint Online | |
| "6a9b9266-8161-4a7b-913a-a9eda19da220", // OneDrive for Business | |
| "00000002-0000-0ff1-ce00-000000000000" // Office 365 Exchange Online | |
| ) | |
| and azure.signinlogs.properties.incoming_token_type == "primaryRefreshToken" | |
| and ( | |
| azure.signinlogs.properties.device_detail.is_compliant == true | |
| or azure.signinlogs.properties.device_detail.is_managed == true | |
| ) | |
| // filter for tooling FOCI clients that can redeem a PRT (replay) | |
| | eval Esql.is_prt_replay = azure.signinlogs.properties.app_id in ( | |
| "04b07795-8ddb-461a-bbee-02f9e1bf7b46", // Microsoft Azure CLI | |
| "1950a258-227b-4e31-a9cf-717495945fc2", // Microsoft Azure PowerShell | |
| "aebc6443-996d-45c2-90f0-388ff96faa56", // Visual Studio Code | |
| "14d82eec-204b-4c2f-b7e8-296a70dab67e", // Microsoft Graph Command Line Tools | |
| "1b730954-1685-4b74-9bfd-dac224a7b894", // Azure Active Directory PowerShell | |
| "872cd9fa-d31f-45e0-9eab-6e460a02d1f1" // Visual Studio | |
| ) | |
| // target resource are common adversary targets for access | |
| and azure.signinlogs.properties.resource_id in ( | |
| "00000003-0000-0000-c000-000000000000", // Microsoft Graph | |
| "00000003-0000-0ff1-ce00-000000000000", // Office 365 SharePoint Online | |
| "6a9b9266-8161-4a7b-913a-a9eda19da220", // OneDrive for Business | |
| "00000002-0000-0ff1-ce00-000000000000" // Office 365 Exchange Online | |
| ) | |
| and azure.signinlogs.properties.incoming_token_type == "primaryRefreshToken" | |
| and ( | |
| azure.signinlogs.properties.device_detail.is_compliant == true | |
| or azure.signinlogs.properties.device_detail.is_managed == true | |
| ) | |
| // device session from Windows Sign-In or WAM, excluding tooling clients so a | |
| // replay cannot vouch for its own source IP | |
| | eval Esql.is_device_session = ( | |
| azure.signinlogs.properties.app_display_name == "Windows Sign In" | |
| or user_agent.original == "Windows-AzureAD-Authentication-Provider/1.0" | |
| ) and not coalesce(Esql.is_prt_replay, false) |
The device-session set doubles as an IP allowlist, and user_agent.original is attacker-controlled. Replaying the cookie with User-Agent: Windows-AzureAD-Authentication-Provider/1.0 makes the event match both is_device_session and is_prt_replay, so the attacker IP lands in source_ip_device_values and the final mv_contains filters it out. One header and the rule never fires. Moving the replay eval first and gating the device-session eval on it closes that, while keeping the on-box exclusion, since a real workstation still populates the device set from its genuine Windows Sign In event.
Summary
related: #6705
Two Entra ID sign-in rules for PRT cookie replay (PRTremote / TokenTactics style): a stolen WAM PRT is redeemed off-box with a first-party FOCI tooling client, while the token keeps the workstation
deviceidso compliant-device CA can still succeed.(device_id, source.ip)for the same clients, for when Windows Sign-In is outside the correlation window.Teams, Office, OneDrive SyncEngine, Authentication Broker, Outlook Mobile, Bing, and Azure Portal are omitted (split-tunnel M365 egress). Replay events require
is_compliantoris_managed.Checklist
Rule: NewRule: New
nameanddescriptiondescriptivenoteincludes investigation guidethreatmapped to ATT&CK