Adversaries may be using compromised accounts to perform rapid, coordinated sign-ins from multiple geographic locations to evade detection. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential credential compromise or lateral movement attempts.
KQL Query
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let common_locations = (SigninLogs
| where TimeGenerated between(starttime..endtime)
| extend locationString= strcat(tostring(LocationDetails["countryOrRegion"]), "/",
tostring(LocationDetails["state"]))
| where locationString != "//"
| summarize count() by locationString
| take 100
| project locationString);
let signIns = (SigninLogs
| where TimeGenerated between(starttime..endtime)
| extend locationString= strcat(tostring(LocationDetails["countryOrRegion"]), "/",
tostring(LocationDetails["state"]))
| where locationString != "//" and locationString !endswith "/"
| where locationString !in (common_locations));
// Adjust these to tune query
let lookupWindow = 10m;
let lookupBin = lookupWindow / 2.0; // lookup bin = equal to 1/2 of the lookup window
let threshold = 2;
let users = (signIns
| summarize dcount(locationString) by Identity
| where dcount_locationString > threshold
| project Identity);
signIns
| where Identity in (users)
| project-rename Start=TimeGenerated
| extend TimeKey = bin(Start, lookupBin)
| join kind = inner (
signIns
| project-rename End=TimeGenerated, EndLocationString=locationString
// TimeKey on the right side of the join - emulates this authentication appearing several times
| extend TimeKey = range(bin(End - lookupWindow, lookupBin),
bin(End, lookupBin), lookupBin)
| mvexpand TimeKey to typeof(datetime) // translate TimeKey arrange range to a column
) on Identity, TimeKey
| where End > Start
| project tostring(Start), tostring(End), locationString, EndLocationString, UserPrincipalName, timeSpan = End - Start, Identity, IPAddress, UserAgent
| where locationString != EndLocationString
| summarize ips=makeset(IPAddress), UAs=makeset(UserAgent) by timeSpan, Identity, locationString, EndLocationString, Start, End, UserPrincipalName
| extend timestamp = Start, AccountCustomEntity = UserPrincipalName
| order by Identity
id: 745a22ec-fed8-49b9-9f62-4570b7709da4
name: Microsoft Entra ID sign-in burst from multiple locations
description: |
'Highlights accounts associated with multiple authentications from different geographical locations in a short period of time.'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let common_locations = (SigninLogs
| where TimeGenerated between(starttime..endtime)
| extend locationString= strcat(tostring(LocationDetails["countryOrRegion"]), "/",
tostring(LocationDetails["state"]))
| where locationString != "//"
| summarize count() by locationString
| take 100
| project locationString);
let signIns = (SigninLogs
| where TimeGenerated between(starttime..endtime)
| extend locationString= strcat(tostring(LocationDetails["countryOrRegion"]), "/",
tostring(LocationDetails["state"]))
| where locationString != "//" and locationString !endswith "/"
| where locationString !in (common_locations));
// Adjust these to tune query
let lookupWindow = 10m;
let lookupBin = lookupWindow / 2.0; // lookup bin = equal to 1/2 of the lookup window
let threshold = 2;
let users = (signIns
| summarize dcount(locationString) by Identity
| where dcount_locationString > threshold
| project Identity);
signIns
| where Identity in (users)
| project-rename Start=TimeGenerated
| extend TimeKey = bin(Start, lookupBin)
| join kind = inner (
signIns
| project-rename End=TimeGenerated, EndLocationString=locationString
// TimeKey on the right side of the join - emulates this authentication appearing several times
| extend TimeKey = range(bin(End - lookupWindow, lookupBin),
bin(End, lookupBin), lookupBin)
| mvexpand TimeKey to typeof(datetime) // translate TimeKey arrange range to a column
) on Identity, TimeKey
| where End > Start
| project tostring(Start), tostring(End), locationString, EndLocationString, UserPrincipalName, timeSpan = End - Start, Identity, IPAddress, UserAgent
| where locationString != EndLocationString
| summarize ips=makeset(IPAddress), UAs=makeset(UserAgent) by timeSpan, Identity, locationString, EndLocationString, Start, End, UserPrincipalName
| extend timestamp = Start, AccountCustomEntity = UserPrincipalName
| order by Identity
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.0
metadata:
source:
kind: Community
author:
name: Shain
support:
tier: Community
categories:
domains: [ "Security - Other", "Identity" ]
| Sentinel Table | Notes |
|---|---|
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled Job Execution Across Multiple Data Centers
Description: A system maintenance job runs across multiple Azure regions, triggering sign-ins from different geographical locations within a short timeframe.
Filter/Exclusion: Exclude sign-ins associated with known system accounts (e.g., svc-azure-scheduler, azure-sql-agent) or filter by userPrincipalName containing svc- or sql-.
Scenario: Remote Desktop Session with Multiple Users
Description: A remote desktop session is shared among multiple users, leading to sign-ins from different IP locations within a short period.
Filter/Exclusion: Filter by deviceName or ipAddress associated with the remote desktop server, or use clientApp to exclude RDP-related clients.
Scenario: Admin Task Using Microsoft Entra ID PowerShell Cmdlets
Description: An admin runs a script using PowerShell to bulk assign licenses or reset passwords, which may result in multiple sign-ins from different locations.
Filter/Exclusion: Exclude sign-ins with clientApp set to PowerShell or filter by userPrincipalName matching admin accounts (e.g., [email protected]).
Scenario: Multi-Factor Authentication (MFA) Verification from Different Locations
Description: A user receives MFA prompts from different locations as part of a verification process, triggering the rule due to multiple sign-in attempts.
Filter/Exclusion: Filter by authenticationMethod to exclude MFA verification attempts, or use riskLevel to exclude low-risk or medium-risk events.
Scenario: Cloud Migration Tool Sign-In from Multiple Regions
Description: A cloud migration tool (e.g., Azure Migrate) authenticates to Microsoft Entra ID from multiple geographical regions during a migration process.
Filter/Exclusion: Exclude sign-ins with