Impossible travel compares successive successful sign-ins for an account and flags a pair whose implied speed no traveller could achieve. It is one of the oldest cloud detections and one of the noisiest, because VPNs, mobile carriers and proxies break the assumption that an IP address maps to a physical place. Done well, it combines distance, speed and baseline context rather than speed alone.
Signals at a Glance
| Signal | Where | ATT&CK |
|---|---|---|
| Implied speed between consecutive sign-ins above a plausible threshold | SigninLogs | T1078.004 |
| First sign-in from a country or ASN never seen for the user | SigninLogs | T1078.004 |
| Success after password spray or MFA failures from the new location | SigninLogs | T1110.003 |
| Legacy protocol or unusual client app from the new location | SigninLogs | T1078.004 |
| Entra ID Protection atypical travel and unfamiliar sign-in properties | Risk detections | T1078.004 |
Computing Geo-velocity
SigninLogs carries latitude and longitude in LocationDetails.geoCoordinates. Order each user’s successful sign-ins, measure the distance to the previous one and divide by elapsed time:
// Microsoft Sentinel (KQL) - impossible travel candidates
SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType == 0
| extend lat = todouble(LocationDetails.geoCoordinates.latitude),
lon = todouble(LocationDetails.geoCoordinates.longitude),
country = tostring(LocationDetails.countryOrRegion)
| where isnotnull(lat) and isnotnull(lon)
| project TimeGenerated, UserPrincipalName, IPAddress, country, lat, lon, AutonomousSystemNumber, AppDisplayName
| order by UserPrincipalName asc, TimeGenerated asc
| serialize
| extend prevUser = prev(UserPrincipalName), prevTime = prev(TimeGenerated),
prevLat = prev(lat), prevLon = prev(lon), prevCountry = prev(country), prevIP = prev(IPAddress)
| where prevUser == UserPrincipalName and country != prevCountry
| extend km = geo_distance_2points(lon, lat, prevLon, prevLat) / 1000.0,
hours = max_of(datetime_diff('minute', TimeGenerated, prevTime) / 60.0, 0.01)
| extend kmh = km / hours
| where km > 500 and kmh > 900
| project TimeGenerated, UserPrincipalName, prevCountry, country, prevIP, IPAddress, km, kmh
// tune/validate: 900 km/h approximates airliner speed; exclude known VPN egress ranges and corporate proxy ASNs
- Compare only sign-ins that produced a real authentication, and exclude token refreshes from non-interactive logs to avoid geo-noise from cloud services.
- Maintain an allowlist of corporate VPN and secure web gateway egress; those ranges generate the majority of false positives.
- Treat the Entra ID Protection atypical travel detection as an input to your own query, not a replacement, since its model and thresholds are not tunable.
Adding Baseline Context
Speed alone is a weak signal. Stronger detections ask whether the new location is also new for this user and whether the sign-in looks risky. This version flags a first-seen country that is paired with other anomalies:
// KQL - first-seen country for the user with supporting risk indicators
let baseline = SigninLogs
| where TimeGenerated between (ago(30d) .. ago(1d)) and ResultType == 0
| summarize KnownCountries = make_set(tostring(LocationDetails.countryOrRegion)) by UserPrincipalName;
SigninLogs
| where TimeGenerated > ago(1d) and ResultType == 0
| extend country = tostring(LocationDetails.countryOrRegion)
| join kind=inner baseline on UserPrincipalName
| where not(set_has_element(KnownCountries, country))
| where ClientAppUsed in ("Exchange ActiveSync", "IMAP4", "POP3", "SMTP", "Other clients")
or RiskLevelDuringSignIn in ("medium", "high")
or AuthenticationRequirement == "singleFactorAuthentication"
| project TimeGenerated, UserPrincipalName, country, IPAddress, ClientAppUsed, RiskLevelDuringSignIn, AppDisplayName
// tune/validate: users who travel will appear; suppress via a travel-calendar or HR feed if you have one
- Triage by checking device compliance, MFA claim source and the apps touched in the following hour.
- Escalate immediately when the new-location sign-in is followed by mailbox rule changes or OAuth consent.
Use Microsoft Entra ID Protection risk detection documentation, Microsoft Sentinel anomalous sign-in content, the Elastic rule for sign-ins from impossible locations and Splunk ESCU geo-velocity analytics. Baseline your own environment: the quality of this detection depends almost entirely on how well you model VPN and proxy egress.
Build Detections That Fire
Mjolnir Security helps blue teams turn threat intelligence into high-fidelity detections across endpoint, network, identity and cloud telemetry.
- Detection Engineering We build and tune detections mapped to ATT&CK from your own telemetry.
- Threat Hunting as a Service Hypothesis-driven hunts for the behaviours on this page.
- 24/7 Incident Response Containment when a detection fires. Call +1 833 403 5875.
Stay ahead of emerging threats. Get notified when we publish new intelligence reports and advisories.
Subscribe to Alerts