Potential Impossible Travel
🔍 Overview
Sometimes corporations have policies against working outside of designated geographic regions, account sharing (this should be standard), or use of non-corporate VPNs. The following scenario will be used to detect unusual logon behavior by creating an incident if a user’s login patterns are too erratic. “Too erratic” can be defined as logging in from multiple geographic regions within a given time period. Whenever a user logs into Azure or authenticates with their main Azure account, logs will be created in the “SigninLogs” table, which is being forwarded to the Log Analytics Workspace being used by Microsoft Sentinel, our SIEM. Within Sentinel, we will define an alert to trigger whenever a user logs into more than one location in a 7 day time period. Not all triggers will be true positives, but it will give us a chance to investigate.
Tools Used
- Cloud Platform: Azure
- SIEM: Microsoft Sentinel
- VM: Windows 10
Create Alert Rule (Potential Impossible Travel)
KQL Query to Detect Impossible Travel
1
2
3
4
5
6
7
8
9
10
// Locate Instances of Potential Impossible Travel
let TimePeriodThreshold = timespan(7d); // Change to how far back you want to look
let NumberOfDifferentLocationsAllowed = 2;
SigninLogs
| where TimeGenerated > ago(TimePeriodThreshold)
| summarize Count = count() by UserPrincipalName, UserId, City = tostring(parse_json(LocationDetails).city), State = tostring(parse_json(LocationDetails).state), Country = tostring(parse_json(LocationDetails).countryOrRegion)
| project UserPrincipalName, UserId, City, State, Country
| summarize PotentialImpossibleTravelInstances = count() by UserPrincipalName, UserId
| where PotentialImpossibleTravelInstances > NumberOfDifferentLocationsAllowed
MITRE ATT&CK TTPs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
The Impossible Travel KQL query is best aligned with:
T1078 – Valid Accounts (most directly),
T1550 – Alternate Authentication Material, and
T1090 – Proxy (as a technique to obscure true origin).
These mappings are useful for:
Tagging incidents in Sentinel with ATT&CK IDs,
Automating response workflows (e.g., disable account, trigger MFA),
Reporting and compliance (e.g., MITRE-aligned dashboards).
The Detection Rule is Triggered
Entities:
1
2
3
4
5
6
7
8
9
10
11
Entities (9)
9f70b6b2ead907b656636d76ba0e504891f1d33097ba8d30cf1f955ab91f00d3@roguecorp.com
162e30755bc9a3e7dde8edf2a52dfa9340fbaa344c6758d2ecb5ae7836d3dadd@roguecorp.com
a9d973022d30582f4e23c56d3352b4722011bcb45fb5c7445ac6ae0002542086@roguecorp.com
06d32e153b231f2f9f2e0a499827f300aafa1b5096815a5837b32f34d6f267df@roguecorp.com
47e6322353e0e93adfcb5a7dcb9966df7ab17529a7e33267ebb5e469fa555254@roguecorp.com
9c9ecf443bab503c2016bba334a806279b07532911585da6a5b76432bb6877df@roguecorp.com
431810045c6e1a565df4a5992d2743312a030b2a6465647eb081ffc8d2bb9a15@roguecorp.com
05052212485141aa60d9344755217508aa48c758dff6d0061c43cb6366ac7fb9@roguecorp.com
eeb5d3da95689a0b777d86e0de3b2249137f3e56797825349376f183a79bd7c7@roguecorp.com
The KQL Query finds one of the nine entities is the biggest repeat offender:
1
2
3
4
5
6
7
8
9
// Locate Instances of Potential Impossible Travel
let TimePeriodThreshold = timespan(7d); // Change to how far back you want to look
let NumberOfDifferentLocationsAllowed = 2;
SigninLogs
| where TimeGenerated > ago(TimePeriodThreshold)
| summarize Count = count() by UserPrincipalName, UserId, City = tostring(parse_json(LocationDetails).city), State = tostring(parse_json(LocationDetails).state), Country = tostring(parse_json(LocationDetails).countryOrRegion)
| project UserPrincipalName, UserId, City, State, Country
| summarize PotentialImpossibleTravelInstances = count() by UserPrincipalName, UserId
| where PotentialImpossibleTravelInstances > NumberOfDifferentLocationsAllowed
This KQL Query finds out which locations the most offending entity has logged in from since the last 7 days:
1
2
3
4
5
6
let TimePeriodThreshold = timespan(7d);
SigninLogs
| where TimeGenerated > ago(TimePeriodThreshold)
| where UserPrincipalName == "eeb5d3da95689a0b777d86e0de3b2249137f3e56797825349376f183a79bd7c7@roguecorp.com"
| summarize Count = count() by UserPrincipalName, UserId, City = tostring(parse_json(LocationDetails).city), State = tostring(parse_json(LocationDetails).state), Country = tostring(parse_json(LocationDetails).countryOrRegion)
| project UserPrincipalName, UserId, City, State, Country
I make a KQL Query to search for signin locations of the past 7 days of all the top 9 entities:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let TimePeriodThreshold = timespan(7d);
let TargetUsers = dynamic([
"9f70b6b2ead907b656636d76ba0e504891f1d33097ba8d30cf1f955ab91f00d3@roguecorp.com",
"162e30755bc9a3e7dde8edf2a52dfa9340fbaa344c6758d2ecb5ae7836d3dadd@roguecorp.com",
"a9d973022d30582f4e23c56d3352b4722011bcb45fb5c7445ac6ae0002542086@roguecorp.com",
"06d32e153b231f2f9f2e0a499827f300aafa1b5096815a5837b32f34d6f267df@roguecorp.com",
"47e6322353e0e93adfcb5a7dcb9966df7ab17529a7e33267ebb5e469fa555254@roguecorp.com",
"9c9ecf443bab503c2016bba334a806279b07532911585da6a5b76432bb6877df@roguecorp.com",
"431810045c6e1a565df4a5992d2743312a030b2a6465647eb081ffc8d2bb9a15@roguecorp.com",
"05052212485141aa60d9344755217508aa48c758dff6d0061c43cb6366ac7fb9@roguecorp.com",
"eeb5d3da95689a0b777d86e0de3b2249137f3e56797825349376f183a79bd7c7@roguecorp.com"
]);
SigninLogs
| where TimeGenerated > ago(TimePeriodThreshold)
| where UserPrincipalName in (TargetUsers)
| extend City = tostring(parse_json(LocationDetails).city),
State = tostring(parse_json(LocationDetails).state),
Country = tostring(parse_json(LocationDetails).countryOrRegion)
| project TimeGenerated, UserPrincipalName, UserId, City, State, Country
| order by UserPrincipalName, TimeGenerated desc
User “9c9ecf443bab503c2016bba334a806279b07532911585da6a5b76432bb6877df@roguecorp.com” logged in from Diamond Bar, California at 6/28/2025, 2:39:33.104 AM and then logged in again from Trenton, New Jersey at 6/28/2025, 4:17:38.575 AM.
That is a distance of 2,713 miles.
Containment, Eradication, and Recovery
We can disable the user’s account:
Perhaps the user is using a VPN.
We may inspect the user’s data AzureActivity log to see if any suspicious activity is found. The user’s VM will also be inspected for unusual activity.
The case was elevated to Tier II and management will discuss with the user if the user has done anything that would explain the impossible travel.
Post-Incident Activities
We can create a geo-fencing activity within Azure that prevents logins outside of certain regions.
I updated the Activity Log:
1
2
3
True Positive - Suspicious Activity
We will investigate the user's activities further. The user's management team will discuss with the user about the impossible travel and report the user's response to us. We may disable the user's account if need be.