Query Details

ROPC Sign-In from New Country or ASN for User

26 ROPC New Country Or ASN

Query

let LegitRopcApps = dynamic([
    "Microsoft Authentication Broker",
    "Microsoft Intune Company Portal",
    "Azure AD Connect",
    "Microsoft Office",
    "Microsoft Office Authentication Broker"
]);
let Lookback = 14d;
let Window   = 1h;
let Baseline =
    union isfuzzy=true
        (SigninLogs
            | where TimeGenerated between (ago(Lookback) .. ago(Window))),
        (AADNonInteractiveUserSignInLogs
            | where TimeGenerated between (ago(Lookback) .. ago(Window)))
    | where ResultType == 0
    | extend Upn = tolower(UserPrincipalName),
             Pair = strcat(Location, "|", tostring(AutonomousSystemNumber))
    | summarize KnownPairs = make_set(Pair) by Upn;
// ROPC sign-ins surface in SigninLogs more than the non-interactive table; correlate both.
let RopcSignIns =
    union isfuzzy=true
        (SigninLogs
            | where TimeGenerated > ago(Window)
            | where AuthenticationProtocol =~ "ropc"
            | project TimeGenerated, UserPrincipalName, IPAddress, Location, AppDisplayName, ResultType, AutonomousSystemNumber),
        (AADNonInteractiveUserSignInLogs
            | where TimeGenerated > ago(Window)
            | where AuthenticationProtocol =~ "ropc"
            | project TimeGenerated, UserPrincipalName, IPAddress, Location, AppDisplayName, ResultType, AutonomousSystemNumber);
RopcSignIns
| invoke ExcludeAllowlistedIPs_AADNI()
| where ResultType == 0
| where AppDisplayName !in~ (LegitRopcApps)
| where isnotempty(Location)
| extend Upn = tolower(UserPrincipalName),
         Pair = strcat(Location, "|", tostring(AutonomousSystemNumber))
| join kind=leftouter Baseline on Upn
| where isnull(KnownPairs) or not(set_has_element(KnownPairs, Pair))
| summarize
    Count     = count(),
    IPs       = make_set(IPAddress),
    Countries = make_set(Location),
    ASNs      = make_set(AutonomousSystemNumber),
    Apps      = make_set(AppDisplayName),
    FirstSeen = min(TimeGenerated),
    LastSeen  = max(TimeGenerated)
  by UserPrincipalName
| extend IPAddress = tostring(IPs[0])
| order by Count desc

Explanation

This query is designed to detect suspicious sign-in activities using the Resource Owner Password Credentials (ROPC) method, which bypasses Multi-Factor Authentication (MFA) and Conditional Access. Here's a simplified breakdown:

  1. Purpose: The query identifies successful ROPC sign-ins from a new country or Autonomous System Number (ASN) for a user that hasn't been observed in the past 14 days. This can indicate a potential credential replay attack from a new attacker location.

  2. Data Sources: It uses data from Azure Active Directory logs, specifically SigninLogs and AADNonInteractiveUserSignInLogs.

  3. Detection Logic:

    • It first establishes a baseline of known country and ASN pairs for each user over the past 14 days.
    • It then checks for new ROPC sign-ins within the last hour.
    • It filters out known legitimate ROPC applications and excludes IPs from an allowlist.
    • It identifies sign-ins from new country/ASN pairs that weren't seen in the baseline.
  4. Alerting:

    • If a new country/ASN pair is detected, it generates an alert with details such as the number of sign-ins, IP addresses, countries, ASNs, applications used, and the time range of the activity.
    • The alert is configured to create an incident in the system, grouping alerts by user account.
  5. Severity and Techniques: The alert is marked with medium severity and is associated with MITRE ATT&CK techniques T1078 (Valid Accounts) and T1550 (Use Alternate Authentication Material), focusing on tactics like Initial Access, Credential Access, and Defense Evasion.

In summary, this query helps security teams identify potentially unauthorized access attempts using ROPC from new locations, which could indicate compromised credentials being used by attackers.

Details

David Alonso profile picture

David Alonso

Released: July 16, 2026

Tables

SigninLogsAADNonInteractiveUserSignInLogs

Keywords

AzureActiveDirectoryAADNonInteractiveUserSignInLogsSigninLogsUserPrincipalNameIPAddressLocationAppDisplayNameAutonomousSystemNumberUserAccountIPAddress

Operators

letdynamicunionisfuzzywherebetweenagoextendtolowerstrcattostringsummarizemake_setbyprojectinvokeExcludeAllowlistedIPs_AADNIin~isnotemptyjoinkindleftouteronisnullnotset_has_elementcountminmaxorder bydesc

Severity

Medium

Tactics

InitialAccessCredentialAccessDefenseEvasion

MITRE Techniques

Frequency: 1h

Period: 14d

Actions

GitHub