Query Details

04 SIGNIN Impossible Travel

Query

id: f4a5b6c7-d8e9-4f0a-1b2c-3d4e5f6a7b8c
name: "SigninLogs — Impossible Travel (3+ Countries in 1 Hour)"
version: 1.0.0
kind: Scheduled
description: |
  Detects users authenticating from 3 or more distinct geographic locations within a 1-hour window — physically impossible travel. Indicates account compromise, credential sharing, or VPN/proxy abuse. MITRE ATT&CK: T1078 (Valid Accounts).
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: PT6H
queryPeriod: P7D
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |

    // ---- Network Allowlist (exclude trusted IPs / CIDR / ranges) --------------
    let _allow = materialize(union isfuzzy=true (print R="" | take 0), (_GetWatchlist('NetworkAllowlist') | project R = tostring(IPOrRange)) | where isnotempty(R));
    let _allowCIDR  = toscalar(_allow | where not(R matches regex @'^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$') | extend R = iff(R has '/', R, strcat(R, '/32')) | summarize make_list(R));
    let _allowRange = toscalar(_allow | where R matches regex @'^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$' | summarize make_list(R));
    let _ExcludeAllowlistedIPs = (T:(IPAddress:string)) {
        T
        | extend IPAddress = tostring(IPAddress)
        | where array_length(_allowCIDR) == 0 or isnull(ipv4_is_in_any_range(IPAddress, _allowCIDR)) or not(ipv4_is_in_any_range(IPAddress, _allowCIDR))
        | mv-apply _r = _allowRange to typeof(string) on (
            extend _lo = tostring(split(_r,'-')[0]), _hi = tostring(split(_r,'-')[1])
            | extend _inRange = ipv4_compare(IPAddress, _lo) >= 0 and ipv4_compare(IPAddress, _hi) <= 0
            | summarize _anyInRange = max(toint(_inRange)))
        | where isnull(_anyInRange) or _anyInRange == 0
        | project-away _anyInRange
    };
    // ---------------------------------------------------------------------------
    SigninLogs
    | invoke _ExcludeAllowlistedIPs()
    | where TimeGenerated > ago(7d)
    | where isnotempty(Location) and isnotempty(IPAddress)
    | summarize
        UniqueLocations = dcount(Location),
        Locations       = make_set(Location),
        IPs             = make_set(IPAddress),
        FailedCount     = countif(ResultType != "0"),
        SuccessCount    = countif(ResultType == "0"),
        StartTime       = min(TimeGenerated),
        EndTime         = max(TimeGenerated)
      by UserPrincipalName
    | where UniqueLocations >= 3
    | project
        TimeGenerated = StartTime,
        UserPrincipalName, UniqueLocations,
        Locations, IPs, FailedCount, SuccessCount, EndTime
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
customDetails:
  UniqueLocations: UniqueLocations
  FailedCount: FailedCount
alertDetailsOverride:
  alertDisplayNameFormat: "Impossible Travel — {{UserPrincipalName}} across {{UniqueLocations}} countries"
  alertDescriptionFormat: "User authenticated from {{UniqueLocations}} distinct countries — impossible physical travel detected."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT1H
    matchingMethod: AllEntities
    groupByEntities:
  - Account



Explanation

This query is designed to detect suspicious login activity that suggests "impossible travel." It identifies users who have logged in from three or more different geographic locations within a one-hour period, which is physically impossible without some form of account compromise, credential sharing, or misuse of VPN/proxy services. Here's a simple breakdown of the query:

  1. Purpose: To identify potential security threats by detecting logins from multiple countries in a short time frame.

  2. Data Source: The query uses data from Azure Active Directory's SigninLogs.

  3. Exclusions: It excludes logins from trusted IP addresses or ranges, which are specified in a "Network Allowlist."

  4. Time Frame: The query looks at login data from the past 7 days.

  5. Conditions: It checks for users who have logged in from at least three different locations.

  6. Output: For each user meeting the criteria, it provides:

    • The number of unique locations.
    • The list of locations and IP addresses.
    • Counts of successful and failed login attempts.
    • The time range of these logins.
  7. Alerting: If such activity is detected, an alert is generated with details about the user and the number of countries involved. This alert is considered medium severity.

  8. Incident Management: The system can create an incident for each alert, grouping similar alerts together based on the user account.

Overall, this query helps security teams quickly identify and respond to potential unauthorized access attempts that involve suspicious geographic login patterns.

Details

David Alonso profile picture

David Alonso

Released: April 20, 2026

Tables

SigninLogs

Keywords

SigninLogsUserAccountIPAddressLocationNetworkTimeGenerated

Operators

letmaterializeunionisfuzzyprinttakeprojecttostringwhereisnotemptymatchesregexextendiffhasstrcatsummarizemake_listtoscalarnotarray_lengthisnullipv4_is_in_any_rangemv-applytotypeofonsplitipv4_comparemaxtointinvokeagodcountmake_setcountifminmaxbyproject-away

Actions