Anomalous sign-in location by user account and authenticating application
Id | 7cb8f77d-c52f-4e46-b82f-3cf2e106224a |
Rulename | Anomalous sign-in location by user account and authenticating application |
Description | This query over Microsoft Entra ID sign-in considers all user sign-ins for each Microsoft Entra ID application and picks out the most anomalous change in location profile for a user within an individual application. |
Severity | Medium |
Tactics | InitialAccess |
Techniques | T1078 |
Required data connectors | AzureActiveDirectory |
Kind | Scheduled |
Query frequency | 1d |
Query period | 7d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/AnomalousUserAppSigninLocationIncrease-detection.yaml |
Version | 2.0.4 |
Arm template | 7cb8f77d-c52f-4e46-b82f-3cf2e106224a.json |
// Adjust this figure to adjust how sensitive this detection is
let sensitivity = 2.5;
let AuthEvents = materialize(
union isfuzzy=True SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| extend LocationDetails = LocationDetails_dynamic
| extend Location = strcat(LocationDetails.countryOrRegion, "-", LocationDetails.state,"-", LocationDetails.city)
| where Location != "--");
AuthEvents
| summarize dcount(Location) by AppDisplayName, AppId, UserPrincipalName, UserId, bin(startofday(TimeGenerated), 1d)
| where dcount_Location > 2
| make-series CountOfLocations = sum(dcount_Location) on TimeGenerated step 1d by AppId, UserId
| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(CountOfLocations, sensitivity, -1, 'linefit')
| mv-expand CountOfLocations to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double), Score to typeof(double), Baseline to typeof(long)
| where Anomalies > 0 and Baseline > 0
| join kind=inner( AuthEvents | extend TimeStamp = startofday(TimeGenerated)) on UserId, AppId
| extend SignInDetails = bag_pack("TimeGenerated", TimeGenerated1, "Location", Location, "Source", IPAddress, "Device", DeviceDetail_dynamic)
| summarize SignInDetailsSet=make_set(SignInDetails, 1000) by UserId, UserPrincipalName, CountOfLocations, TimeGenerated, AppId, AppDisplayName
| extend Name = split(UserPrincipalName, "@")[0], UPNSuffix = split(UserPrincipalName, "@")[1]
queryFrequency: 1d
description: |
'This query over Microsoft Entra ID sign-in considers all user sign-ins for each Microsoft Entra ID application and picks out the most anomalous change in location profile for a user within an individual application.
customDetails:
Application: AppDisplayName
status: Available
version: 2.0.4
relevantTechniques:
- T1078
kind: Scheduled
name: Anomalous sign-in location by user account and authenticating application
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: SingleAlert
query: |
// Adjust this figure to adjust how sensitive this detection is
let sensitivity = 2.5;
let AuthEvents = materialize(
union isfuzzy=True SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| extend LocationDetails = LocationDetails_dynamic
| extend Location = strcat(LocationDetails.countryOrRegion, "-", LocationDetails.state,"-", LocationDetails.city)
| where Location != "--");
AuthEvents
| summarize dcount(Location) by AppDisplayName, AppId, UserPrincipalName, UserId, bin(startofday(TimeGenerated), 1d)
| where dcount_Location > 2
| make-series CountOfLocations = sum(dcount_Location) on TimeGenerated step 1d by AppId, UserId
| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(CountOfLocations, sensitivity, -1, 'linefit')
| mv-expand CountOfLocations to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double), Score to typeof(double), Baseline to typeof(long)
| where Anomalies > 0 and Baseline > 0
| join kind=inner( AuthEvents | extend TimeStamp = startofday(TimeGenerated)) on UserId, AppId
| extend SignInDetails = bag_pack("TimeGenerated", TimeGenerated1, "Location", Location, "Source", IPAddress, "Device", DeviceDetail_dynamic)
| summarize SignInDetailsSet=make_set(SignInDetails, 1000) by UserId, UserPrincipalName, CountOfLocations, TimeGenerated, AppId, AppDisplayName
| extend Name = split(UserPrincipalName, "@")[0], UPNSuffix = split(UserPrincipalName, "@")[1]
alertDetailsOverride:
alertDisplayNameFormat: Anomalous sign-in location by {{UserPrincipalName}} to {{AppDisplayName}}
alertDescriptionFormat: |
This query over Microsoft Entra ID sign-in considers all user sign-ins for each Microsoft Entra ID application and picks out the most anomalous change in location profile for a user within an
individual application. This has detected {{UserPrincipalName}} signing into {{AppDisplayName}} from {{CountOfLocations}}
different locations.
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/AnomalousUserAppSigninLocationIncrease-detection.yaml
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
tactics:
- InitialAccess
severity: Medium
id: 7cb8f77d-c52f-4e46-b82f-3cf2e106224a
queryPeriod: 7d
entityMappings:
- fieldMappings:
- columnName: UserPrincipalName
identifier: FullName
- columnName: Name
identifier: Name
- columnName: UPNSuffix
identifier: UPNSuffix
entityType: Account
- fieldMappings:
- columnName: UserId
identifier: AadUserId
entityType: Account
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/7cb8f77d-c52f-4e46-b82f-3cf2e106224a')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/7cb8f77d-c52f-4e46-b82f-3cf2e106224a')]",
"properties": {
"alertDetailsOverride": {
"alertDescriptionFormat": "This query over Microsoft Entra ID sign-in considers all user sign-ins for each Microsoft Entra ID application and picks out the most anomalous change in location profile for a user within an\nindividual application. This has detected {{UserPrincipalName}} signing into {{AppDisplayName}} from {{CountOfLocations}} \ndifferent locations.\n",
"alertDisplayNameFormat": "Anomalous sign-in location by {{UserPrincipalName}} to {{AppDisplayName}}"
},
"alertRuleTemplateName": "7cb8f77d-c52f-4e46-b82f-3cf2e106224a",
"customDetails": {
"Application": "AppDisplayName"
},
"description": "'This query over Microsoft Entra ID sign-in considers all user sign-ins for each Microsoft Entra ID application and picks out the most anomalous change in location profile for a user within an individual application.\n",
"displayName": "Anomalous sign-in location by user account and authenticating application",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "Name",
"identifier": "Name"
},
{
"columnName": "UPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserId",
"identifier": "AadUserId"
}
]
}
],
"eventGroupingSettings": {
"aggregationKind": "SingleAlert"
},
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/AnomalousUserAppSigninLocationIncrease-detection.yaml",
"query": "// Adjust this figure to adjust how sensitive this detection is\nlet sensitivity = 2.5;\nlet AuthEvents = materialize(\nunion isfuzzy=True SigninLogs, AADNonInteractiveUserSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == 0\n| extend LocationDetails = LocationDetails_dynamic\n| extend Location = strcat(LocationDetails.countryOrRegion, \"-\", LocationDetails.state,\"-\", LocationDetails.city)\n| where Location != \"--\");\nAuthEvents\n| summarize dcount(Location) by AppDisplayName, AppId, UserPrincipalName, UserId, bin(startofday(TimeGenerated), 1d)\n| where dcount_Location > 2\n| make-series CountOfLocations = sum(dcount_Location) on TimeGenerated step 1d by AppId, UserId\n| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(CountOfLocations, sensitivity, -1, 'linefit')\n| mv-expand CountOfLocations to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double), Score to typeof(double), Baseline to typeof(long)\n| where Anomalies > 0 and Baseline > 0\n| join kind=inner( AuthEvents | extend TimeStamp = startofday(TimeGenerated)) on UserId, AppId\n| extend SignInDetails = bag_pack(\"TimeGenerated\", TimeGenerated1, \"Location\", Location, \"Source\", IPAddress, \"Device\", DeviceDetail_dynamic)\n| summarize SignInDetailsSet=make_set(SignInDetails, 1000) by UserId, UserPrincipalName, CountOfLocations, TimeGenerated, AppId, AppDisplayName\n| extend Name = split(UserPrincipalName, \"@\")[0], UPNSuffix = split(UserPrincipalName, \"@\")[1]\n",
"queryFrequency": "P1D",
"queryPeriod": "P7D",
"severity": "Medium",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"InitialAccess"
],
"techniques": [
"T1078"
],
"templateVersion": "2.0.4",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}