Seach

The Impossible Travel Alert: Friend or Foe?

Using raw Azure AD SigningLogs table in Azure Sentinel versus Microsoft Cloud App Security detection policies

By Adrian Grigorof

The impossible travel has been on the list of SIEM detection for a long time, being even listed on Wikipedia’s SIEM page.

To quote the article: “When a user logs in to a system, generally speaking, it creates a timestamp of the event. Alongside the time, the system may often record other useful information such as the device used, GPS address, IP address, incorrect login attempts, etc. The more data is collected the more use can be gathered from it. For impossible travel, the system looks at the current and last login date/time and the difference between the recorded distances. If it deems it’s not possible for this to happen, for example traveling hundreds of miles within a minute, then it will set off a warning. Fortunately, many employees and users are now using VPN services, therefore this should be taken into consideration when setting up such a rule.”

In the Azure Sentinel world, the “impossible travel” alerts are one of the detections received from Microsoft Cloud App Security – its native Sentinel data connector allowing the integration of incidents with just a couple of clicks of the mouse:

The impossible travel is just one of MCAS detections (based on “policies” defined in the MCAS portal). As of May 2021, MCAS has 91 policies:

Impossible Travel policy is part of the Threat Detection category and has the following characteristics:

  • Uses seven days of user activity to build a baseline before identifying anomalies. The policy scope can be configured to only be applicable to specific users and groups.
  • Description: “This policy profiles your environment and triggers alerts when activities are detected from the same user in different locations within a time period that is shorter than the expected travel time between the two locations. This could indicate that a different user is using the same credentials. Detecting this anomalous behavior necessitates an initial learning period of seven days during which it learns a new user’s activity pattern.”
  • Sensitivity (Low to High) is based on how far the deviation from the pattern is for the monitored users. Multiple levels of sensitivity can be set based on specific users or groups.
  • The policy will monitor specific types of activity, related to sign-ins but what kind of sign-ins can be customized to include all sign-ins, just successful sign-ins or successful sign-ins and non-legacy failed sign-ins.
  • Once an incident is triggered, the policy can perform a range of automation tasks such as sending email or SMS notifications, or launch a Power Automate tasks to perform more advanced tasks. Some identity governance automation (such as signing out our disabling a user) can be specified for all apps, Office 365 or Google Workspace.

See also: https://docs.microsoft.com/en-us/cloud-app-security/anomaly-detection-policy

The details page for “impossible travel” incidents will include all the anomalies identified by MCAS:

MCAS comes bundled with a number of Microsoft licensing bundles so there is a cost to deploy it manage it. But, since it relies on access to raw logs, can one build some of its detections using just the logs collected in Azure Sentinel?
Let’s how we can approach this request, building the “impossible travel” detection using raw Azure AD logs in Sentinel.

Use-case description:

  • Identify users connecting from different IP addresses. Based on the IP address geo-location, calculate the distance between the two locations.
  • Using the timestamps for the connections from the two locations, estimate if it was possible to travel between the two locations within the calculated time difference.
  • Discard any failed login attempts.

Approach:

  • Identify users connecting from two or more locations.
  • Use the geo_distance_2points() KQL function to calculate the distance.
  • Calculate the virtual “speed” based on distance/time formula.
  • Filter for successful logins.
  • Filter for speed over a specific threshold (i.e. 1000 km/hour – a bit over the 900 km/hour average speed of a jet liner)

Logs required:

  • Azure Active Directory SiginLogs collected via the Azure Sentinel Azure AD data connector.

KQL script:
let maxSpeed = 1000;
SigninLogs
| where ResultType == “0”
| extend latitude_ = todouble(parse_json(tostring(LocationDetails.geoCoordinates)).latitude)
| extend longitude_ = todouble(parse_json(tostring(LocationDetails.geoCoordinates)).longitude)
| extend countryOrRegion = tostring(LocationDetails.countryOrRegion)
| extend state = tostring(LocationDetails.state)
| extend location = strcat(state,’ – ‘, countryOrRegion)
| where location <> ‘ – ‘
| extend browser = tostring(DeviceDetail.browser)
| summarize Count=count(),IP=any(IPAddress),Last=max(TimeGenerated) by UserDisplayName, latitude_, longitude_, Locations=tostring(location), browser,AppDisplayName, UserPrincipalName, Location
| extend coordinates = pack_array(latitude_,longitude_)
| summarize coordinates=any(coordinates),StateCountries=makeset(Locations),Last=max(Last),IP=any(IP),Apps=makeset(AppDisplayName),Browsers=makeset(browser), Locations=makeset(Locations) by UserDisplayName, UserPrincipalName, Location
| summarize Coordinates=makeset(coordinates),NumberOfCountries=dcount(Location) ,StateCountries=makeset(Locations),Timestamps=makeset(Last),IPs=makeset(IP),Apps=makeset(Apps),Browsers=makeset(Browsers) by UserDisplayName, UserPrincipalName
| where NumberOfCountries > 1
| extend distance = round(geo_distance_2points(todouble(Coordinates[1]),todouble(Coordinates[0]),todouble(Coordinates[3]),todouble(Coordinates[2]))/1000,0)
| extend hours = abs(datetime_diff(‘hour’, todatetime(Timestamps[1]),todatetime(Timestamps[0])))
| where hours > 0
| extend speedKmPerHour = round(distance/hours,0)
| where speedKmPerHour > maxSpeed
| project-reorder Timestamps, UserDisplayName, UserPrincipalName, IPs, speedKmPerHour, distance, hours, StateCountries, Apps, Browsers, StateCountries
| sort by UserPrincipalName asc
| extend IPs = replace(‘[\[|\|\\|”\]]’,”,tostring(IPs))

The script is available at: https://github.com/ManagedSentinel/AzureSentinelKQLScripts/blob/master/AzureADImpossibleTravel.kql
Running this KQL in Azure Sentinel for a full day (May 5, 2012), returns 210 results – quite a lot:

We will look at the details for user Adrian Grigorof. The KQL results indicate two connections, one from Austria, the other one from California within one hour, implying a “trip” at over 10,000 km/hour. IP addresses 213.157.159.3 in Austria and x.x.x.245 in U.S. We can use abuseipdb.com to get some details about the IP addresses.

The Austrian connection appears to be from an ISP in Villach, a city in southern Austria.

The second IP being our corporate VPN IP address – notice the “Corporate” usage type on Abuse IP DB:

The activity for the last seven days does shows only connections from two IPs:

Charting the summary by one-hour connections count provides a better visual indicator of Azure AD sign-ins pattern.

The summary by location and IP address show just 2 distinct locations:

The user activity did not trigger an MCAS alert as the pattern was deemed to be within the accepted baseline. So how does the activity of a user that does trigger MCAS alerts look like? Let’s review the data from a user captured in an MCAS impossible travel alert.

Once integrated in Azure Sentinel, the MCAS alerts will be recorded in the SecurityAlert table under the MCAS provider name. Optionally, incidents can be created when an MCAS alert is recorded.

Let’s find the MCAS Impossible Travel alerts recorded by MCAS in Azure Sentinel for May 5, 2021.

SecurityAlert

| where ProviderName == “MCAS”

| where AlertName == “Impossible travel activity”

| project TimeGenerated, DisplayName, Description 

| sort by TimeGenerated desc

The KQL script returns 5 results:

So what is special about these five users and how is their behavior different from the “regular” impossible traveller? 

Let’s verify the activity for the Kapoor, Raj user in Azure AD Sign-in logs. Filtering the SigningLog table in Azure Sentinel for Raj Kapoor shows 11 different IP addresses used in the last seven days:

A summary of the locations shows quite a few IP addresses, with the IPs captured in the MCAS alert as outliers:

In the “impossible travel” KQL we are picking up one IP per country and calculate the virtual “speed” for the user:

The user tagged in MCAS under the impossible travel alert was captured in the KQL running against the raw Azure AD SigninLogs table but so were an additional 209 users. Five versus 210 represents a ratio of 1:42 and that’s a great reduction of potential false positives performed by MCAS for just one-day’s worth of data.

The KQL provided above and used to process the raw logs can be improved to match some of the logic described in the MCAS policy:

  • Comparison with the activity from previous seven days (build a list of IPs and locations and use a Kusto “anti” join to exclude them).
  • Enrichment of IP addresses using a threat intel feed or an IP “reputation” provider.
  • Enforcement of scope to apply the detection to include or exclude specific users, applications or IP addresses.

Conclusions
In principle, the MCAS detection policy can be recreated up to a point in Azure Sentinel but is it worth it? This is just one of the 91 current policies enforced by MCAS and Microsoft has an army of engineers working on maintaining these detections.

This brings us to our recommendations around building detections based on raw logs when a fully specialized security control already exists for it: when possible, use dedicated detection tools to focus on their specific area and use Azure Sentinel as the aggregator of signals and SOAR platform. This provides both a single-pane of glass and a single location to build enhanced playbooks to act on the detected events.

When it comes to automation for impossible travel events, the potential actions depend on the organization’s IT security policies, its risk appetite, the nature of their applications, and the distribution of their staff around the globe, its VPN usage, MFA implementation, access control, and BYOD policies. 

Potential actions include:

  • Logon session reset triggered by a number of infringements
  • Account disabling or password reset
  • Manager notifications
  • User notifications
  • Correlation with other incidents from the same user or IP address, anomalies in other activities such as file transfers, hosts accessed, etc.

While traditionally time consuming and difficult to investigate, the impossible travel alerts should at the very least be collected as additional context for use in other types of detections. Azure Sentinel automation rules and playbooks provide the capability to build a wide range of automation, with the main challenge being defining the actual SOAR requirement, rather than implementing it. 

At BlueVoyant, part of our Azure Sentinel implementations we have deployed a range of complex SOAR playbooks to perform all the activities mentioned above. When applicable we always advise our customers on improving the overall visibility into the threat landscape by choosing the optimal combination of tools based on their strengths. 

Adrian Grigorof is the CTO of Managed Sentinel.

Related reading