Query Details

28 ROPC Impossible Travel

Query

id: e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b
name: ROPC Impossible Travel - Multiple Countries via Password Grant
version: 1.0.0
kind: Scheduled
description: |
  Detects the same user successfully authenticating via ROPC from two or more distinct
  countries inside a 1-hour window. Because ROPC requires the cleartext password on every
  call, multi-country activity within an hour implies parallel use of stolen credentials
  from different attacker locations (or a compromised script running from multiple egress
  points).

  Trigger: >= 2 distinct Location values for the same UPN inside 1 hour.

  Tuning:
    - LegitRopcApps suppresses known-good ROPC clients.
    - Allowlist IPs are stripped via ExcludeAllowlistedIPs_AADNI(). Add corporate
      VPN egress IPs to NetworkAllowlist if multi-country VPN clusters cause noise.
  MITRE ATT&CK: T1078 (Valid Accounts), T1550 (Use Alternate Auth Material)
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CredentialAccess
relevantTechniques:
  - T1078
  - T1550
query: |
  let LegitRopcApps = dynamic([
      "Microsoft Authentication Broker",
      "Microsoft Intune Company Portal",
      "Azure AD Connect",
      "Microsoft Office",
      "Microsoft Office Authentication Broker"
  ]);
  AADNonInteractiveUserSignInLogs
  | invoke ExcludeAllowlistedIPs_AADNI()
  | where TimeGenerated > ago(1h)
  | where AuthenticationProtocol =~ "ropc"
  | where ResultType == 0
  | where AppDisplayName !in~ (LegitRopcApps)
  | where isnotempty(Location)
  | summarize
      Count        = count(),
      Countries    = make_set(Location),
      CountryCount = dcount(Location),
      IPs          = make_set(IPAddress),
      Apps         = make_set(AppDisplayName),
      FirstSeen    = min(TimeGenerated),
      LastSeen     = max(TimeGenerated)
    by UserPrincipalName
  | where CountryCount >= 2
  | extend IPAddress = tostring(IPs[0])
  | order by CountryCount desc, Count desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
customDetails:
  SignInCount: Count
  CountryCount: CountryCount
  Countries: Countries
  Apps: Apps
alertDetailsOverride:
  alertDisplayNameFormat: "ROPC impossible travel for {{UserPrincipalName}} ({{CountryCount}} countries / 1h)"
  alertDescriptionFormat: "User {{UserPrincipalName}} authenticated via ROPC from {{CountryCount}} countries ({{Countries}}) within 1 hour. ROPC bypasses MFA - strong account-takeover signal."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AnyAlert
    groupByEntities:
      - Account
    groupByAlertDetails: []
    groupByCustomDetails: []

Explanation

This query is designed to detect suspicious login activity using the Resource Owner Password Credentials (ROPC) flow in Azure Active Directory. Here's a simple breakdown of what it does:

  1. Purpose: It identifies cases where the same user successfully logs in from two or more different countries within a one-hour period using the ROPC authentication method. This could indicate that the user's credentials have been stolen and are being used by attackers in different locations.

  2. How It Works:

    • The query looks at non-interactive user sign-in logs from Azure Active Directory.
    • It filters out known legitimate applications that use ROPC and excludes IPs that are on an allowlist (such as corporate VPNs).
    • It checks for successful logins (ResultType == 0) using ROPC within the last hour.
    • It groups the data by user and counts the number of distinct countries from which the user has logged in.
    • If a user has logged in from two or more different countries within an hour, it flags this as suspicious.
  3. Alerting:

    • If the conditions are met, an alert is generated with details about the user, the number of countries, and the applications involved.
    • The alert is classified as high severity and is linked to specific MITRE ATT&CK techniques related to credential access and initial access.
  4. Additional Features:

    • The query includes mechanisms to suppress noise from legitimate applications and known IP addresses.
    • It creates incidents for detected cases, with the option to group related alerts by user account.

Overall, this query helps security teams identify potential account compromises by detecting unusual login patterns that suggest credential theft and misuse.

Details

David Alonso profile picture

David Alonso

Released: May 29, 2026

Tables

AADNonInteractiveUserSignInLogs

Keywords

AzureActiveDirectoryAADNonInteractiveUserSignInLogsUserAccountIPLocationAuthenticationProtocolAppDisplayNameTimeGeneratedUserPrincipalNameIPAddressCountriesAppsCountryCountCount

Operators

letdynamicinvokeago=~==!in~isnotemptysummarizecountmake_setdcountminmaxby>=extendtostringorder bydesc

Actions