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.
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