Correlate Unfamiliar sign-in properties atypical travel alerts
Id | a3df4a32-4805-4c6d-8699-f3c888af2f67 |
Rulename | Correlate Unfamiliar sign-in properties & atypical travel alerts |
Description | The combination of an Unfamiliar sign-in properties alert and an Atypical travel alert about the same user within a +10m or -10m window is considered a high severity incident. |
Severity | High |
Tactics | InitialAccess |
Techniques | T1078 |
Required data connectors | AzureActiveDirectoryIdentityProtection BehaviorAnalytics |
Kind | Scheduled |
Query frequency | 1d |
Query period | 14d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID Protection/Analytic Rules/CorrelateIPC_Unfamiliar-Atypical.yaml |
Version | 1.0.8 |
Arm template | a3df4a32-4805-4c6d-8699-f3c888af2f67.json |
// We can use this configuration TimeDeltaInMinutes if you want to chnage the time window that we try to match the alerts
let TimeDeltaInMinutes = 10;
let Alert_UnfamiliarSignInProps =
SecurityAlert
| where TimeGenerated > ago(1d)
| where ProductName =~ "Azure Active Directory Identity Protection"
| where AlertName =~ "Unfamiliar sign-in properties"
| mv-expand Entity = todynamic(Entities)
| where Entity.Type =~ "account"
| extend AadTenantId = tostring(Entity.AadTenantId)
| extend AadUserId = tostring(Entity.AadUserId)
| join kind=inner (
IdentityInfo
| distinct AccountTenantId, AccountObjectId, AccountUPN, AccountDisplayName
| extend UserName = AccountDisplayName
| extend UserAccount = AccountUPN
| where isnotempty(AccountDisplayName) and isnotempty(UserAccount)
| project AccountTenantId, AccountObjectId, UserAccount, UserName
)
on
$left.AadTenantId == $right.AccountTenantId,
$left.AadUserId == $right.AccountObjectId
| extend CompromisedEntity = iff(CompromisedEntity == "N/A" or isempty(CompromisedEntity), UserAccount, CompromisedEntity)
| extend Alert_UnfamiliarSignInProps_Time = TimeGenerated
| extend Alert_UnfamiliarSignInProps_Name = AlertName
| extend Alert_UnfamiliarSignInProps_Severity = AlertSeverity
| project AadTenantId, AadUserId, AccountTenantId, AccountObjectId, Alert_UnfamiliarSignInProps_Name, Alert_UnfamiliarSignInProps_Severity, Alert_UnfamiliarSignInProps_Time, UserAccount, UserName
;
let Alert_AtypicalTravels =
SecurityAlert
| where TimeGenerated > ago(1d)
| where ProductName =~ "Azure Active Directory Identity Protection"
| where AlertName =~ "Atypical travel"
| mv-expand Entity = todynamic(Entities)
| where Entity.Type =~ "account"
| extend AadTenantId = tostring(Entity.AadTenantId)
| extend AadUserId = tostring(Entity.AadUserId)
| join kind=inner (
IdentityInfo
| distinct AccountTenantId, AccountObjectId, AccountUPN, AccountDisplayName
| extend UserName = AccountDisplayName
| extend UserAccount = AccountUPN
| where isnotempty(AccountDisplayName) and isnotempty(UserAccount)
| project AccountTenantId, AccountObjectId, UserAccount, UserName
)
on
$left.AadTenantId == $right.AccountTenantId,
$left.AadUserId == $right.AccountObjectId
| extend CompromisedEntity = iff(CompromisedEntity == "N/A" or isempty(CompromisedEntity), UserAccount, CompromisedEntity)
| extend Alert_AtypicalTravels_Time = TimeGenerated
| extend Alert_AtypicalTravels_Name = AlertName
| extend Alert_AtypicalTravels_Severity = AlertSeverity
| extend ExtendedProperties_json= parse_json(ExtendedProperties)
| extend CurrentLocation = tostring(ExtendedProperties_json.["Current Location"])
| extend PreviousLocation = tostring(ExtendedProperties_json.["Previous Location"])
| extend CurrentIPAddress = tostring(ExtendedProperties_json.["Current IP Address"])
| extend PreviousIPAddress = tostring(ExtendedProperties_json.["Previous IP Address"])
| project AadTenantId, AadUserId, AccountTenantId, AccountObjectId, Alert_AtypicalTravels_Name, Alert_AtypicalTravels_Severity, Alert_AtypicalTravels_Time, CurrentIPAddress, PreviousIPAddress, CurrentLocation, PreviousLocation, UserAccount, UserName, CompromisedEntity
;
Alert_UnfamiliarSignInProps
| join kind=inner Alert_AtypicalTravels on UserAccount
| where abs(datetime_diff('minute', Alert_UnfamiliarSignInProps_Time, Alert_AtypicalTravels_Time)) <= TimeDeltaInMinutes
| extend TimeDelta = Alert_UnfamiliarSignInProps_Time - Alert_AtypicalTravels_Time
| project UserAccount, AadUserId, Alert_UnfamiliarSignInProps_Name, Alert_UnfamiliarSignInProps_Severity, Alert_UnfamiliarSignInProps_Time, Alert_AtypicalTravels_Name, Alert_AtypicalTravels_Severity, Alert_AtypicalTravels_Time, TimeDelta, CurrentLocation, PreviousLocation, CurrentIPAddress, PreviousIPAddress, UserName
| extend UserEmailName = split(UserAccount,'@')[0], UPNSuffix = split(UserAccount,'@')[1]
name: Correlate Unfamiliar sign-in properties & atypical travel alerts
version: 1.0.8
severity: High
queryFrequency: 1d
triggerOperator: gt
customDetails:
PreviousLocation: PreviousLocation
Alert2_Time: Alert_AtypicalTravels_Time
CurrentLocation: CurrentLocation
TimeDelta: TimeDelta
Alert2_Name: Alert_AtypicalTravels_Name
Alert1_Time: Alert_UnfamiliarSignInProps_Time
CurrentIPAddress: CurrentIPAddress
Alert1_Name: Alert_UnfamiliarSignInProps_Name
Alert2_Severity: Alert_AtypicalTravels_Severity
Alert1_Severity: Alert_UnfamiliarSignInProps_Severity
PreviousIPAddress: PreviousIPAddress
relevantTechniques:
- T1078
status: Available
description: |
'The combination of an Unfamiliar sign-in properties alert and an Atypical travel alert about the same user within a +10m or -10m window is considered a high severity incident.'
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID Protection/Analytic Rules/CorrelateIPC_Unfamiliar-Atypical.yaml
requiredDataConnectors:
- connectorId: AzureActiveDirectoryIdentityProtection
dataTypes:
- SecurityAlert (IPC)
- connectorId: BehaviorAnalytics
dataTypes:
- IdentityInfo
entityMappings:
- fieldMappings:
- identifier: FullName
columnName: UserAccount
- identifier: Name
columnName: UserEmailName
- identifier: UPNSuffix
columnName: UPNSuffix
entityType: Account
- fieldMappings:
- identifier: AadUserId
columnName: AadUserId
entityType: Account
- fieldMappings:
- identifier: Address
columnName: CurrentIPAddress
entityType: IP
- fieldMappings:
- identifier: Address
columnName: PreviousIPAddress
entityType: IP
tactics:
- InitialAccess
queryPeriod: 14d
query: |
// We can use this configuration TimeDeltaInMinutes if you want to chnage the time window that we try to match the alerts
let TimeDeltaInMinutes = 10;
let Alert_UnfamiliarSignInProps =
SecurityAlert
| where TimeGenerated > ago(1d)
| where ProductName =~ "Azure Active Directory Identity Protection"
| where AlertName =~ "Unfamiliar sign-in properties"
| mv-expand Entity = todynamic(Entities)
| where Entity.Type =~ "account"
| extend AadTenantId = tostring(Entity.AadTenantId)
| extend AadUserId = tostring(Entity.AadUserId)
| join kind=inner (
IdentityInfo
| distinct AccountTenantId, AccountObjectId, AccountUPN, AccountDisplayName
| extend UserName = AccountDisplayName
| extend UserAccount = AccountUPN
| where isnotempty(AccountDisplayName) and isnotempty(UserAccount)
| project AccountTenantId, AccountObjectId, UserAccount, UserName
)
on
$left.AadTenantId == $right.AccountTenantId,
$left.AadUserId == $right.AccountObjectId
| extend CompromisedEntity = iff(CompromisedEntity == "N/A" or isempty(CompromisedEntity), UserAccount, CompromisedEntity)
| extend Alert_UnfamiliarSignInProps_Time = TimeGenerated
| extend Alert_UnfamiliarSignInProps_Name = AlertName
| extend Alert_UnfamiliarSignInProps_Severity = AlertSeverity
| project AadTenantId, AadUserId, AccountTenantId, AccountObjectId, Alert_UnfamiliarSignInProps_Name, Alert_UnfamiliarSignInProps_Severity, Alert_UnfamiliarSignInProps_Time, UserAccount, UserName
;
let Alert_AtypicalTravels =
SecurityAlert
| where TimeGenerated > ago(1d)
| where ProductName =~ "Azure Active Directory Identity Protection"
| where AlertName =~ "Atypical travel"
| mv-expand Entity = todynamic(Entities)
| where Entity.Type =~ "account"
| extend AadTenantId = tostring(Entity.AadTenantId)
| extend AadUserId = tostring(Entity.AadUserId)
| join kind=inner (
IdentityInfo
| distinct AccountTenantId, AccountObjectId, AccountUPN, AccountDisplayName
| extend UserName = AccountDisplayName
| extend UserAccount = AccountUPN
| where isnotempty(AccountDisplayName) and isnotempty(UserAccount)
| project AccountTenantId, AccountObjectId, UserAccount, UserName
)
on
$left.AadTenantId == $right.AccountTenantId,
$left.AadUserId == $right.AccountObjectId
| extend CompromisedEntity = iff(CompromisedEntity == "N/A" or isempty(CompromisedEntity), UserAccount, CompromisedEntity)
| extend Alert_AtypicalTravels_Time = TimeGenerated
| extend Alert_AtypicalTravels_Name = AlertName
| extend Alert_AtypicalTravels_Severity = AlertSeverity
| extend ExtendedProperties_json= parse_json(ExtendedProperties)
| extend CurrentLocation = tostring(ExtendedProperties_json.["Current Location"])
| extend PreviousLocation = tostring(ExtendedProperties_json.["Previous Location"])
| extend CurrentIPAddress = tostring(ExtendedProperties_json.["Current IP Address"])
| extend PreviousIPAddress = tostring(ExtendedProperties_json.["Previous IP Address"])
| project AadTenantId, AadUserId, AccountTenantId, AccountObjectId, Alert_AtypicalTravels_Name, Alert_AtypicalTravels_Severity, Alert_AtypicalTravels_Time, CurrentIPAddress, PreviousIPAddress, CurrentLocation, PreviousLocation, UserAccount, UserName, CompromisedEntity
;
Alert_UnfamiliarSignInProps
| join kind=inner Alert_AtypicalTravels on UserAccount
| where abs(datetime_diff('minute', Alert_UnfamiliarSignInProps_Time, Alert_AtypicalTravels_Time)) <= TimeDeltaInMinutes
| extend TimeDelta = Alert_UnfamiliarSignInProps_Time - Alert_AtypicalTravels_Time
| project UserAccount, AadUserId, Alert_UnfamiliarSignInProps_Name, Alert_UnfamiliarSignInProps_Severity, Alert_UnfamiliarSignInProps_Time, Alert_AtypicalTravels_Name, Alert_AtypicalTravels_Severity, Alert_AtypicalTravels_Time, TimeDelta, CurrentLocation, PreviousLocation, CurrentIPAddress, PreviousIPAddress, UserName
| extend UserEmailName = split(UserAccount,'@')[0], UPNSuffix = split(UserAccount,'@')[1]
kind: Scheduled
triggerThreshold: 0
id: a3df4a32-4805-4c6d-8699-f3c888af2f67
{
"$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/a3df4a32-4805-4c6d-8699-f3c888af2f67')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/a3df4a32-4805-4c6d-8699-f3c888af2f67')]",
"properties": {
"alertRuleTemplateName": "a3df4a32-4805-4c6d-8699-f3c888af2f67",
"customDetails": {
"Alert1_Name": "Alert_UnfamiliarSignInProps_Name",
"Alert1_Severity": "Alert_UnfamiliarSignInProps_Severity",
"Alert1_Time": "Alert_UnfamiliarSignInProps_Time",
"Alert2_Name": "Alert_AtypicalTravels_Name",
"Alert2_Severity": "Alert_AtypicalTravels_Severity",
"Alert2_Time": "Alert_AtypicalTravels_Time",
"CurrentIPAddress": "CurrentIPAddress",
"CurrentLocation": "CurrentLocation",
"PreviousIPAddress": "PreviousIPAddress",
"PreviousLocation": "PreviousLocation",
"TimeDelta": "TimeDelta"
},
"description": "'The combination of an Unfamiliar sign-in properties alert and an Atypical travel alert about the same user within a +10m or -10m window is considered a high severity incident.'\n",
"displayName": "Correlate Unfamiliar sign-in properties & atypical travel alerts",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserAccount",
"identifier": "FullName"
},
{
"columnName": "UserEmailName",
"identifier": "Name"
},
{
"columnName": "UPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "AadUserId",
"identifier": "AadUserId"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "CurrentIPAddress",
"identifier": "Address"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "PreviousIPAddress",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID Protection/Analytic Rules/CorrelateIPC_Unfamiliar-Atypical.yaml",
"query": "// We can use this configuration TimeDeltaInMinutes if you want to chnage the time window that we try to match the alerts\nlet TimeDeltaInMinutes = 10;\nlet Alert_UnfamiliarSignInProps = \nSecurityAlert\n| where TimeGenerated > ago(1d)\n| where ProductName =~ \"Azure Active Directory Identity Protection\"\n| where AlertName =~ \"Unfamiliar sign-in properties\"\n| mv-expand Entity = todynamic(Entities)\n| where Entity.Type =~ \"account\"\n| extend AadTenantId = tostring(Entity.AadTenantId)\n| extend AadUserId = tostring(Entity.AadUserId)\n| join kind=inner (\nIdentityInfo\n| distinct AccountTenantId, AccountObjectId, AccountUPN, AccountDisplayName\n| extend UserName = AccountDisplayName\n| extend UserAccount = AccountUPN\n| where isnotempty(AccountDisplayName) and isnotempty(UserAccount)\n| project AccountTenantId, AccountObjectId, UserAccount, UserName\n)\non\n$left.AadTenantId == $right.AccountTenantId,\n$left.AadUserId == $right.AccountObjectId\n| extend CompromisedEntity = iff(CompromisedEntity == \"N/A\" or isempty(CompromisedEntity), UserAccount, CompromisedEntity)\n| extend Alert_UnfamiliarSignInProps_Time = TimeGenerated\n| extend Alert_UnfamiliarSignInProps_Name = AlertName\n| extend Alert_UnfamiliarSignInProps_Severity = AlertSeverity\n| project AadTenantId, AadUserId, AccountTenantId, AccountObjectId, Alert_UnfamiliarSignInProps_Name, Alert_UnfamiliarSignInProps_Severity, Alert_UnfamiliarSignInProps_Time, UserAccount, UserName\n;\nlet Alert_AtypicalTravels = \nSecurityAlert\n| where TimeGenerated > ago(1d)\n| where ProductName =~ \"Azure Active Directory Identity Protection\"\n| where AlertName =~ \"Atypical travel\"\n| mv-expand Entity = todynamic(Entities)\n| where Entity.Type =~ \"account\"\n| extend AadTenantId = tostring(Entity.AadTenantId)\n| extend AadUserId = tostring(Entity.AadUserId)\n| join kind=inner (\nIdentityInfo\n| distinct AccountTenantId, AccountObjectId, AccountUPN, AccountDisplayName\n| extend UserName = AccountDisplayName\n| extend UserAccount = AccountUPN\n| where isnotempty(AccountDisplayName) and isnotempty(UserAccount)\n| project AccountTenantId, AccountObjectId, UserAccount, UserName\n)\non\n$left.AadTenantId == $right.AccountTenantId,\n$left.AadUserId == $right.AccountObjectId\n| extend CompromisedEntity = iff(CompromisedEntity == \"N/A\" or isempty(CompromisedEntity), UserAccount, CompromisedEntity)\n| extend Alert_AtypicalTravels_Time = TimeGenerated\n| extend Alert_AtypicalTravels_Name = AlertName\n| extend Alert_AtypicalTravels_Severity = AlertSeverity\n| extend ExtendedProperties_json= parse_json(ExtendedProperties)\n| extend CurrentLocation = tostring(ExtendedProperties_json.[\"Current Location\"])\n| extend PreviousLocation = tostring(ExtendedProperties_json.[\"Previous Location\"])\n| extend CurrentIPAddress = tostring(ExtendedProperties_json.[\"Current IP Address\"])\n| extend PreviousIPAddress = tostring(ExtendedProperties_json.[\"Previous IP Address\"])\n| project AadTenantId, AadUserId, AccountTenantId, AccountObjectId, Alert_AtypicalTravels_Name, Alert_AtypicalTravels_Severity, Alert_AtypicalTravels_Time, CurrentIPAddress, PreviousIPAddress, CurrentLocation, PreviousLocation, UserAccount, UserName, CompromisedEntity\n;\nAlert_UnfamiliarSignInProps\n| join kind=inner Alert_AtypicalTravels on UserAccount\n| where abs(datetime_diff('minute', Alert_UnfamiliarSignInProps_Time, Alert_AtypicalTravels_Time)) <= TimeDeltaInMinutes\n| extend TimeDelta = Alert_UnfamiliarSignInProps_Time - Alert_AtypicalTravels_Time\n| project UserAccount, AadUserId, Alert_UnfamiliarSignInProps_Name, Alert_UnfamiliarSignInProps_Severity, Alert_UnfamiliarSignInProps_Time, Alert_AtypicalTravels_Name, Alert_AtypicalTravels_Severity, Alert_AtypicalTravels_Time, TimeDelta, CurrentLocation, PreviousLocation, CurrentIPAddress, PreviousIPAddress, UserName\n| extend UserEmailName = split(UserAccount,'@')[0], UPNSuffix = split(UserAccount,'@')[1]\n",
"queryFrequency": "P1D",
"queryPeriod": "P14D",
"severity": "High",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"InitialAccess"
],
"techniques": [
"T1078"
],
"templateVersion": "1.0.8",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}