Entra ID Device-Bound PRT Replay via First-Party App from Unusual IP


Description

Detects a first-party FOCI tooling client (Azure CLI, PowerShell, VS Code, Graph CLI, Azure AD PowerShell, or Visual Studio) redeeming a device-bound Primary Refresh Token (PRT) for Microsoft Graph, SharePoint/OneDrive, or Exchange Online from an IP that is not among that user and device's Windows Sign-In or WAM addresses. Replay events are limited to compliant or Intune-managed devices: the stolen cookie keeps the workstation deviceid, so compliant-device Conditional Access can succeed off-box.

Query · esql

from logs-azure.signinlogs-*
// 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

// filter for device sign-in events from Windows Sign-In or WAM (login session)
| 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
    )

// device session or PRT replay event have to exist
| where Esql.is_device_session or Esql.is_prt_replay

// aggregate entities for both device session and PRT replay events
// aggregate by user and device
| stats
    Esql.source_ip_device_values = values(source.ip) where Esql.is_device_session,
    Esql.source_ip_replay_values = values(source.ip) where Esql.is_prt_replay,
    Esql.event_count_replay = count(*) where Esql.is_prt_replay,
    Esql.event_count_device_session = count(*) where Esql.is_device_session,
    Esql.user_principal_name_values = values(azure.signinlogs.properties.user_principal_name),
    Esql.app_display_name_values = values(azure.signinlogs.properties.app_display_name) where Esql.is_prt_replay,
    Esql.resource_id_values = values(azure.signinlogs.properties.resource_id) where Esql.is_prt_replay,
    Esql.resource_display_name_values = values(azure.signinlogs.properties.resource_display_name) where Esql.is_prt_replay,
    Esql.device_display_name_values = values(azure.signinlogs.properties.device_detail.display_name),
    Esql.user_agent_original_values = values(user_agent.original) where Esql.is_prt_replay,
    Esql.device_is_compliant_values = values(azure.signinlogs.properties.device_detail.is_compliant) where Esql.is_prt_replay,
    Esql.device_is_managed_values = values(azure.signinlogs.properties.device_detail.is_managed) where Esql.is_prt_replay,
    Esql.authentication_requirement_values = values(azure.signinlogs.properties.authentication_requirement) where Esql.is_prt_replay,
    Esql.conditional_access_status_values = values(azure.signinlogs.properties.conditional_access_status) where Esql.is_prt_replay,
    Esql.earliest_timestamp = min(@timestamp),
    Esql.latest_timestamp = max(@timestamp)
  by azure.signinlogs.properties.user_id, azure.signinlogs.properties.device_detail.device_id

// filter for PRT replay events that have at least one replay IP
| where Esql.event_count_replay > 0
    and Esql.event_count_device_session > 0
    and Esql.source_ip_replay_values is not null
    and Esql.source_ip_device_values is not null

// expand the replay IP list and keep only IPs that are not in the device-session set
| mv_expand Esql.source_ip_replay_values
| where not mv_contains(Esql.source_ip_device_values, Esql.source_ip_replay_values)
| eval Esql.source_ip_replay = Esql.source_ip_replay_values,
       user.id = azure.signinlogs.properties.user_id,
       source.ip = Esql.source_ip_replay_values
| keep
    user.id,
    source.ip,
    azure.signinlogs.properties.user_id,
    azure.signinlogs.properties.device_detail.device_id,
    Esql.*

Investigation fields

Pivot points the source recommends for triage.

  • user.id
  • source.ip
  • azure.signinlogs.properties.user_id
  • azure.signinlogs.properties.device_detail.device_id
  • Esql.user_principal_name_values
  • Esql.device_display_name_values
  • Esql.source_ip_device_values
  • Esql.source_ip_replay_values
  • Esql.app_display_name_values
  • Esql.resource_display_name_values
  • Esql.user_agent_original_values
  • Esql.device_is_compliant_values
  • Esql.device_is_managed_values

Implementation guide

The Azure Fleet integration (or Filebeat Azure module) with Microsoft Entra ID sign-in logs is required. Ingest SignInLogs and NonInteractiveUserSignInLogs into logs-azure.signinlogs-* so Windows Sign-In / WAM device activity and FOCI Graph, SharePoint, and Exchange token issuance are both available for the join.

See Microsoft Entra ID sign-in logs and the Azure integration.

Known false positives

  • A user running Azure CLI, Azure PowerShell, VS Code, Graph CLI, or Visual Studio on a jump host, Cloud Shell, or VPN egress that differs from the workstation's Windows Sign-In IP can match if that activity still presents a compliant or managed workstation deviceid. Validate whether the Graph client ran on the enrolled device before treating the event as cookie theft.
  • Split-tunnel or dual-homed devices may present different egress IPs for WAM versus Azure CLI. Confirm both IPs belong to the same physical device before raising severity.
  • Microsoft Teams, Microsoft Office, OneDrive SyncEngine, Microsoft Authentication Broker, Outlook Mobile, Bing, Azure Portal, ADIbizaUX, and Office 365 Management are omitted. The rest of the Secureworks known-foci-clients.csv family (Edge, OneDrive, Intune Company Portal, Windows Search, Authenticator, SharePoint, Planner, Power BI, and similar) is omitted for the same reason: routine workstation or mobile Graph whose Microsoft 365 egress often differs from the Windows Sign-In IP without cookie theft. Hunt those app_ids separately if harvest is already confirmed.

Analyst notes

Investigating Entra ID Device-Bound PRT Replay via First-Party App from Unusual IP

Adversaries who steal a WAM PRT SSO cookie (BrowserCore / InteractiveToken harvest) redeem it off-box for Microsoft Graph, SharePoint/OneDrive, or Exchange Online using a first-party FOCI client. PRTremote uses Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46); TokenTactics and AADInternals can use Azure AD PowerShell (1b730954-1685-4b74-9bfd-dac224a7b894) or Microsoft Office (d3590ed6-52b3-4102-aeff-aad2292ab01c) when refreshing a PRT into a Graph, Outlook, or SharePoint token. This rule keys on CLI / PowerShell / VS Code / Visual Studio, not Teams, Office, OneDrive SyncEngine, Authentication Broker, Outlook Mobile, Bing, or Azure Portal — those clients routinely egress on a different IP than Windows Sign-In. Hunt Office-client PRT separately if harvest is already confirmed. The issued access token carries the workstation deviceid, so Conditional Access policies that require a compliant device can succeed even though the redeeming IP is not the device. Replay events must be is_compliant or is_managed so the join tracks a stolen enrolled workstation PRT, not an unmanaged ROADtx device.

This is not ConsentFix / OAuth-code phishing. Those flows show OAuth2:Authorize with Redirect and often lack a bound compliant device on the Graph token. Do not close this alert by following the first-party OAuth phishing playbook alone.

Possible investigation steps

  • Review azure.signinlogs.properties.user_principal_name, azure.signinlogs.properties.device_detail.device_id, and source.ip. Confirm incoming_token_type on the replay events is primaryRefreshToken and device_detail.is_compliant / is_managed are true. Esql.resource_display_name_values should be Microsoft Graph, Office 365 SharePoint Online, OneDrive for Business, and/or Office 365 Exchange Online.
  • Treat Esql.source_ip_device_values as the workstation (Windows Sign-In / Windows-AzureAD-Authentication-Provider/1.0) — the device the PRT was bound to, and the likely theft origin. Treat source.ip / Esql.source_ip_replay_values as Graph / SharePoint / Exchange PRT IPs that are not in that device-session set (the replay actor). On-box FOCI from the workstation IP is excluded from the replay set. The harvest emulation showed workstation public IP versus attacker SNAT — including Azure ASN 8075, so a Microsoft-owned ASN does not clear the alert. The stolen PRT cookie nonce is typically valid for about five minutes; that is the attacker’s redeem window, not this rule’s lookback. The workstation may have signed in hours earlier, so the query correlates 24 hours of Windows Sign-In / WAM IPs with the replay.
  • Inspect Esql.app_display_name_values and Esql.user_agent_original_values. A cookie POST through WAM can show a Trident/MSIE user agent on the Azure CLI client ID; that is the same IE stack legitimate Windows az login uses when it brokers through WAM, so it is triage context, not a detection key. TokenTactics Graph refresh commonly presents as Microsoft Office (omitted here). A follow-on python-requests/* Graph token from the same session is tooling, not the first-party binary on the workstation.
  • Hunt endpoint telemetry for the same user or device_detail.display_name as host.name: svchost.exe (Schedule) → cmd.exe → BrowserCore.exe with < / > redirection, or files formatted_nonce.txt / prt_cookie.txt.
  • Intune audit (create-clientcertificate) is enrollment context only; it will not fire on the harvest. Use it as optional join on user OID, not as coverage.
  • Review Graph, SharePoint, and mailbox activity after the replay for directory, file, or mail enumeration.

False positive analysis

  • Developers who run Azure CLI, Graph CLI, or VS Code from a different egress than Windows Sign-In while WAM still attaches the workstation deviceid. Exception known Cloud Shell / jump-host IPs after confirming the client actually ran there.
  • Microsoft Teams, Office, OneDrive SyncEngine, Authentication Broker, Outlook Mobile, Bing, Azure Portal, and Office 365 Management Graph PRT are excluded. Do not treat their absence as a miss; they are omitted because split-tunnel M365 egress is routine.
  • First sign-in of a new device can have sparse Windows Sign-In history in the 24-hour window; widen the hunt before responding.
  • Hybrid-joined workstations that report both is_compliant and is_managed as false (no Intune) will not match the replay branch. Hunt those deviceids separately if harvest is already confirmed.

Response and remediation

  • Contact the user to confirm whether they ran the first-party client in Esql.app_display_name_values from the replay IP.
  • If unauthorized, revoke refresh tokens and primary refresh tokens for the user. The deviceid on the token is often the legitimate workstation — do not delete that device as if it were a ROADtx registration until you confirm otherwise.
  • Isolate the workstation, hunt for InteractiveToken scheduled tasks and BrowserCore harvest, and treat the admin identity that registered the task as a second compromised principal.
Raw source Entra ID Device-Bound PRT Replay via First-Party App from Unusual IP · Elastic TOML
Esc
Published by elastic/detection-rules ↗, licensed under Elastic License 2.0 ↗. Reproduced here unmodified.
[metadata]
creation_date = "2026/08/27"
integration = ["azure"]
maturity = "production"
min_stack_comments = "MV_CONTAINS was added to ES|QL in 9.2.0 and is not available on 8.19; 9.3.0 is the lowest supported 9.x release."
min_stack_version = "9.3.0"
updated_date = "2026/09/22"

[rule]
author = ["Elastic"]
description = """
Detects a first-party FOCI tooling client (Azure CLI, PowerShell, VS Code, Graph CLI, Azure AD PowerShell, or Visual
Studio) redeeming a device-bound Primary Refresh Token (PRT) for Microsoft Graph, SharePoint/OneDrive, or Exchange
Online from an IP that is not among that user and device's Windows Sign-In or WAM addresses. Replay events are limited
to compliant or Intune-managed devices: the stolen cookie keeps the workstation deviceid, so compliant-device
Conditional Access can succeed off-box.
"""
false_positives = [
    """
    A user running Azure CLI, Azure PowerShell, VS Code, Graph CLI, or Visual Studio on a jump host, Cloud Shell, or VPN
    egress that differs from the workstation's Windows Sign-In IP can match if that activity still presents a compliant
    or managed workstation deviceid. Validate whether the Graph client ran on the enrolled device before treating the
    event as cookie theft.
    """,
    """
    Split-tunnel or dual-homed devices may present different egress IPs for WAM versus Azure CLI. Confirm both IPs
    belong to the same physical device before raising severity.
    """,
    """
    Microsoft Teams, Microsoft Office, OneDrive SyncEngine, Microsoft Authentication Broker, Outlook Mobile, Bing, Azure
    Portal, ADIbizaUX, and Office 365 Management are omitted. The rest of the Secureworks known-foci-clients.csv family
    (Edge, OneDrive, Intune Company Portal, Windows Search, Authenticator, SharePoint, Planner, Power BI, and similar)
    is omitted for the same reason: routine workstation or mobile Graph whose Microsoft 365 egress often differs from
    the Windows Sign-In IP without cookie theft. Hunt those app_ids separately if harvest is already confirmed.
    """,
]
from = "now-24h"
interval = "1h"
language = "esql"
license = "Elastic License v2"
name = "Entra ID Device-Bound PRT Replay via First-Party App from Unusual IP"
note = """## Triage and analysis

### Investigating Entra ID Device-Bound PRT Replay via First-Party App from Unusual IP

Adversaries who steal a WAM PRT SSO cookie (BrowserCore / InteractiveToken harvest) redeem it off-box for Microsoft Graph, SharePoint/OneDrive, or Exchange Online using a first-party FOCI client. PRTremote uses Azure CLI (`04b07795-8ddb-461a-bbee-02f9e1bf7b46`); TokenTactics and AADInternals can use Azure AD PowerShell (`1b730954-1685-4b74-9bfd-dac224a7b894`) or Microsoft Office (`d3590ed6-52b3-4102-aeff-aad2292ab01c`) when refreshing a PRT into a Graph, Outlook, or SharePoint token. This rule keys on CLI / PowerShell / VS Code / Visual Studio, not Teams, Office, OneDrive SyncEngine, Authentication Broker, Outlook Mobile, Bing, or Azure Portal — those clients routinely egress on a different IP than Windows Sign-In. Hunt Office-client PRT separately if harvest is already confirmed. The issued access token carries the *workstation* `deviceid`, so Conditional Access policies that require a compliant device can succeed even though the redeeming IP is not the device. Replay events must be `is_compliant` or `is_managed` so the join tracks a stolen enrolled workstation PRT, not an unmanaged ROADtx device.

This is not ConsentFix / OAuth-code phishing. Those flows show `OAuth2:Authorize` with `Redirect` and often lack a bound compliant device on the Graph token. Do not close this alert by following the first-party OAuth phishing playbook alone.


### Possible investigation steps

- Review `azure.signinlogs.properties.user_principal_name`, `azure.signinlogs.properties.device_detail.device_id`, and `source.ip`. Confirm `incoming_token_type` on the replay events is `primaryRefreshToken` and `device_detail.is_compliant` / `is_managed` are true. `Esql.resource_display_name_values` should be Microsoft Graph, Office 365 SharePoint Online, OneDrive for Business, and/or Office 365 Exchange Online.
- Treat `Esql.source_ip_device_values` as the workstation (Windows Sign-In / `Windows-AzureAD-Authentication-Provider/1.0`) — the device the PRT was bound to, and the likely theft origin. Treat `source.ip` / `Esql.source_ip_replay_values` as Graph / SharePoint / Exchange PRT IPs that are not in that device-session set (the replay actor). On-box FOCI from the workstation IP is excluded from the replay set. The harvest emulation showed workstation public IP versus attacker SNAT — including Azure ASN 8075, so a Microsoft-owned ASN does not clear the alert. The stolen PRT cookie nonce is typically valid for about five minutes; that is the attacker’s redeem window, not this rule’s lookback. The workstation may have signed in hours earlier, so the query correlates 24 hours of Windows Sign-In / WAM IPs with the replay.
- Inspect `Esql.app_display_name_values` and `Esql.user_agent_original_values`. A cookie POST through WAM can show a Trident/MSIE user agent on the Azure CLI client ID; that is the same IE stack legitimate Windows `az login` uses when it brokers through WAM, so it is triage context, not a detection key. TokenTactics Graph refresh commonly presents as Microsoft Office (omitted here). A follow-on `python-requests/*` Graph token from the same session is tooling, not the first-party binary on the workstation.
- Hunt endpoint telemetry for the same user or `device_detail.display_name` as `host.name`: `svchost.exe` (Schedule) → `cmd.exe` → `BrowserCore.exe` with `<` / `>` redirection, or files `formatted_nonce.txt` / `prt_cookie.txt`.
- Intune audit (`create-clientcertificate`) is enrollment context only; it will not fire on the harvest. Use it as optional join on user OID, not as coverage.
- Review Graph, SharePoint, and mailbox activity after the replay for directory, file, or mail enumeration.

### False positive analysis

- Developers who run Azure CLI, Graph CLI, or VS Code from a different egress than Windows Sign-In while WAM still attaches the workstation deviceid. Exception known Cloud Shell / jump-host IPs after confirming the client actually ran there.
- Microsoft Teams, Office, OneDrive SyncEngine, Authentication Broker, Outlook Mobile, Bing, Azure Portal, and Office 365 Management Graph PRT are excluded. Do not treat their absence as a miss; they are omitted because split-tunnel M365 egress is routine.
- First sign-in of a new device can have sparse Windows Sign-In history in the 24-hour window; widen the hunt before responding.
- Hybrid-joined workstations that report both `is_compliant` and `is_managed` as false (no Intune) will not match the replay branch. Hunt those deviceids separately if harvest is already confirmed.

### Response and remediation

- Contact the user to confirm whether they ran the first-party client in `Esql.app_display_name_values` from the replay IP.
- If unauthorized, revoke refresh tokens and primary refresh tokens for the user. The deviceid on the token is often the *legitimate* workstation — do not delete that device as if it were a ROADtx registration until you confirm otherwise.
- Isolate the workstation, hunt for InteractiveToken scheduled tasks and BrowserCore harvest, and treat the admin identity that registered the task as a second compromised principal.
"""
references = [
    "https://www.armadin.com/blog-posts/prtremote-extract-prt-cookies-remotely-with-interactivetoken-scheduled-task",
    "https://github.com/armadin-public/PRTremote",
    "https://github.com/dmcxblue/ANIMO/blob/master/helpers/scripts/GrabTokenAzureAD/PrtExtractor.cs",
    "https://github.com/rvrsh3ll/TokenTactics",
    "https://github.com/Gerenios/AADInternals",
]
risk_score = 73
rule_id = "6a9fdaab-b50f-408d-b4da-54719f256d41"
setup = """The Azure Fleet integration (or Filebeat Azure module) with Microsoft Entra ID sign-in logs is required. Ingest `SignInLogs` and `NonInteractiveUserSignInLogs` into `logs-azure.signinlogs-*` so Windows Sign-In / WAM device activity and FOCI Graph, SharePoint, and Exchange token issuance are both available for the join.

See [Microsoft Entra ID sign-in logs](https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-ins) and the [Azure integration](https://docs.elastic.co/integrations/azure).
"""
severity = "high"
tags = [
    "Domain: Cloud",
    "Domain: Identity",
    "Use Case: Threat Detection",
    "Use Case: Identity and Access Audit",
    "Data Source: Azure",
    "Data Source: Microsoft Entra ID",
    "Data Source: Microsoft Entra ID Sign-In Logs",
    "Platform: Entra ID",
    "Tactic: Credential Access",
    "Tactic: Defense Evasion",
    "Tactic: Initial Access",
    "Resources: Investigation Guide",
    "Rule Type: ES|QL",
]
timestamp_override = "event.ingested"
type = "esql"

query = '''
from logs-azure.signinlogs-*
// 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

// filter for device sign-in events from Windows Sign-In or WAM (login session)
| 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
    )

// device session or PRT replay event have to exist
| where Esql.is_device_session or Esql.is_prt_replay

// aggregate entities for both device session and PRT replay events
// aggregate by user and device
| stats
    Esql.source_ip_device_values = values(source.ip) where Esql.is_device_session,
    Esql.source_ip_replay_values = values(source.ip) where Esql.is_prt_replay,
    Esql.event_count_replay = count(*) where Esql.is_prt_replay,
    Esql.event_count_device_session = count(*) where Esql.is_device_session,
    Esql.user_principal_name_values = values(azure.signinlogs.properties.user_principal_name),
    Esql.app_display_name_values = values(azure.signinlogs.properties.app_display_name) where Esql.is_prt_replay,
    Esql.resource_id_values = values(azure.signinlogs.properties.resource_id) where Esql.is_prt_replay,
    Esql.resource_display_name_values = values(azure.signinlogs.properties.resource_display_name) where Esql.is_prt_replay,
    Esql.device_display_name_values = values(azure.signinlogs.properties.device_detail.display_name),
    Esql.user_agent_original_values = values(user_agent.original) where Esql.is_prt_replay,
    Esql.device_is_compliant_values = values(azure.signinlogs.properties.device_detail.is_compliant) where Esql.is_prt_replay,
    Esql.device_is_managed_values = values(azure.signinlogs.properties.device_detail.is_managed) where Esql.is_prt_replay,
    Esql.authentication_requirement_values = values(azure.signinlogs.properties.authentication_requirement) where Esql.is_prt_replay,
    Esql.conditional_access_status_values = values(azure.signinlogs.properties.conditional_access_status) where Esql.is_prt_replay,
    Esql.earliest_timestamp = min(@timestamp),
    Esql.latest_timestamp = max(@timestamp)
  by azure.signinlogs.properties.user_id, azure.signinlogs.properties.device_detail.device_id

// filter for PRT replay events that have at least one replay IP
| where Esql.event_count_replay > 0
    and Esql.event_count_device_session > 0
    and Esql.source_ip_replay_values is not null
    and Esql.source_ip_device_values is not null

// expand the replay IP list and keep only IPs that are not in the device-session set
| mv_expand Esql.source_ip_replay_values
| where not mv_contains(Esql.source_ip_device_values, Esql.source_ip_replay_values)
| eval Esql.source_ip_replay = Esql.source_ip_replay_values,
       user.id = azure.signinlogs.properties.user_id,
       source.ip = Esql.source_ip_replay_values
| keep
    user.id,
    source.ip,
    azure.signinlogs.properties.user_id,
    azure.signinlogs.properties.device_detail.device_id,
    Esql.*
'''


[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1528"
name = "Steal Application Access Token"
reference = "https://attack.mitre.org/techniques/T1528/"

[[rule.threat.technique]]
id = "T1539"
name = "Steal Web Session Cookie"
reference = "https://attack.mitre.org/techniques/T1539/"


[rule.threat.tactic]
id = "TA0006"
name = "Credential Access"
reference = "https://attack.mitre.org/tactics/TA0006/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1550"
name = "Use Alternate Authentication Material"
reference = "https://attack.mitre.org/techniques/T1550/"
[[rule.threat.technique.subtechnique]]
id = "T1550.001"
name = "Application Access Token"
reference = "https://attack.mitre.org/techniques/T1550/001/"



[rule.threat.tactic]
id = "TA0005"
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1078"
name = "Valid Accounts"
reference = "https://attack.mitre.org/techniques/T1078/"
[[rule.threat.technique.subtechnique]]
id = "T1078.004"
name = "Cloud Accounts"
reference = "https://attack.mitre.org/techniques/T1078/004/"



[rule.threat.tactic]
id = "TA0001"
name = "Initial Access"
reference = "https://attack.mitre.org/tactics/TA0001/"

[rule.investigation_fields]
field_names = [
    "user.id",
    "source.ip",
    "azure.signinlogs.properties.user_id",
    "azure.signinlogs.properties.device_detail.device_id",
    "Esql.user_principal_name_values",
    "Esql.device_display_name_values",
    "Esql.source_ip_device_values",
    "Esql.source_ip_replay_values",
    "Esql.app_display_name_values",
    "Esql.resource_display_name_values",
    "Esql.user_agent_original_values",
    "Esql.device_is_compliant_values",
    "Esql.device_is_managed_values",
]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.