By Paul Keely and Garv Sachdeva
Impossible Travel alerts and there lack of usefulness is something we hear about all the time with customers. The fact is that people connecting to our resources from new/unfamiliar locations or with impossible travel should be something we are pretty interest in, so why are you getting so may false positives, or non-actionable results?
Lets look at this simple example to explain the Impossible Travel (IT) predicament.
In this example the end user’s phone or laptop is connecting to an ISP in the US, with a public IP address somewhere in DC. As soon as they join the Wi-Fi in the business lounge in the airport they are now sitting in London. In Azure AD (AAD) the IP Address is registered, and if you have CASB they ISP is also being logged. When AAD logs the IP it also looks at the latitude and longitude, and if we have a Geographically Distant new IP in a short time period we get an IT alert.
All IT alerts are not the same and you need to understand how they can be structured, where to look in Sentinel logs to build your queries and what is the difference between an everyday IT alert and a potentially serious IT alert. Lets dig into some more examples below.
We have 3 scenarios above. The key point with IT alerts is that we always recommend that you join your devices to AAD and ideally have them managed by Intune. If you are using a 3rd party MDM solution good for you, we just need the logs. When you have your devices somewhere in the AAD/EMS ecosystem we have a device ID, this is critical in working out if the device is know or not. If the device is unknown this is a worry for us. If the device is known but non-compliant, this is also a worry.
In the first scenario, we have 1 user (that’s the same for them all, not sure why I even have this in) but we have 2 public IP addresses but 1 known device. So if we know the device, and then if its managed by Intune we can tell if it is compliant, and as part of that compliance then we see its also all clear with WD-ATP this is a pretty low severity alert. You want to follow up, but maybe not that critical.
In the second scenario, we have a user logging in from two cities both on managed or known devices but there are 2 devices. The most likely cause for this are either password sharing or logging into O365 on two separate terminals. If you have faith in the compliance of the devices again this is a low security situation, if its a password share you need to fix that, with a big grumpy InfoSec lecture that sounds like your their parent, and not there to help.
In the last scenario we are kinda in trouble. The second logon is from an unknown device, and all bets are off. In this scenario you need to:
- Try make contact with the end user (good luck with that)
- Block their O365 account
- Analyze what the logon from the unmanaged account did, while they were logged on
(we will cover all of these in future blogs that build from this one).
We are going to drill into the logs a little so that you can understand how we are building our IT alerts. We assume that you have your devices either joined to AAD or managed by Intune, also we have some pulled sign-in logs from the Graph API and we get the machine ID’s. When you install Sentinel and connect the AAD connector it builds a dataset called SigninLogs. I am going to go to AAD (just from the Azure portal) and find my own device ID.
With the device ID i just go to Log Analytics and just search for the ID. The device ID for me is found in a number of logs.
The SigninLogs are in LogManagement\SignInLogs, and so in number 1 you see we are hitting the SigninLogs, and 2 shows you this is a Sign-In Activity
In point 3 you can see its me, 4 shows the country i am in right now (France), 5 shows my App ID and then 6 shows what CA policies were assessed (but none were applied)
7 is my risk score on signin and 8 shows me that my MFA token was satisfied by token. Now we have a good idea of the date fields in the dataset we also go and pull a few more data sets to write our query.
In point 1 we are going to the Intune logs to get the AAD device ID, in 2 we are making a set with that field, we are then going to a new data set (Security Alert) to find the IT alert. In 4 we are removing any alert that does not have an IP address, and then in point 5 we go to the signin logs that we already covered. Lastly in point 6 we go back to the Intune logs to look for point 7, is the device compliant. In our org we have pretty draconian Intune policies that look for a really high security bar on our devices.
The goal of this query is to reduce the number of alerts that surface as alerts in Sentinel. What we are saying with this query is
- if you logon in with a managed device
- and its compliant in Intune
- and its compliant in WD-ATP
- then we will not create an alert
- If the logon is from an unknow device
- your device is not compliant
- then we are far more likely to raise an alert (we actually go through more steps and I will go through them in another blog)
Here is what the query looks like when it finds a match.
Here is the query I used (it uses a custom data set around Intune).
let deviceids = toscalar(O365IntuneRegisteredDevices_CL| extend azureADDeviceId = parse_json(JsonData_s).[‘AzureADDeviceId’]| summarize arg_max(TimeGenerated, *) by tostring(azureADDeviceId)| summarize makeset(azureADDeviceId)| project set_azureADDeviceId);let IntuneData = O365IntuneRegisteredDevices_CL| summarize arg_max(TimeGenerated, *) by tostring(parse_json(JsonData_s).[‘AzureADDeviceId’])| project UserPrincipalName_s , azureADDeviceId = tostring(parse_json(JsonData_s).[‘AzureADDeviceId’]) , ComplianceGracePeriod = parse_json(JsonData_s).[‘ComplianceGracePeriodExpirationDateTime’] , ComplianceState = parse_json(JsonData_s). [‘ComplianceState’], Registered = parse_json(JsonData_s).DeviceRegistrationState , DeviceName = parse_json(JsonData_s).DeviceName, SerialNo = parse_json(JsonData_s).SerialNumber, SubscriberCarrier = parse_json(JsonData_s).SubscriberCarrier, UserId = parse_json(JsonData_s).UserId, WifiMac = parse_json(JsonData_s).WiFiMacAddress;
//Trace all impossible travel alerts, with their IPs and Locations.SecurityAlert| where AlertType == “ImpossibleTravel”| extend Address = pack_array(tostring(parse_json(todynamic(Entities)).Address), tostring(parse_json(todynamic(Entities)).Address))| extend Location = pack_array(tostring(parse_json(todynamic(Entities)).Location), tostring(parse_json(todynamic(Entities)).Location))| mvexpand Address, Location| where Address != “”| extend Address = tostring(Address)| project TimeGenerated , Address , tostring(Location) , AlertName , AlertSeverity , AlertType , ExtendedProperties , Type| join kind= inner( // Join on basis of IPs with Signin logs to get the device id values. SigninLogs | extend deviceId = tostring(parse_json(DeviceDetail).deviceId) | project TimeGenerated , IPAddress , tostring(deviceId) ,tostring(LocationDetails), Identity , Id, tostring(Status) , UserPrincipalName)on $left.Address == $right.IPAddress//| where deviceId != “”// Final Result would be alerts from all unknown devices.//Join with intune data to check for compliance| join kind= leftouter( IntuneData)on $left.deviceId == $right.azureADDeviceId| where ComplianceState != “compliant”