Analytic rule catalog
Password spray attack against Microsoft Entra ID Seamless SSO
Back
| Id | fb7ca1c9-e14c-40a3-856e-28f3c14ea1ba |
| Rulename | Password spray attack against Microsoft Entra ID Seamless SSO |
| Description | This query detects when there is a spike in Microsoft Entra ID Seamless SSO errors. They may not be caused by a Password Spray attack, but the cause of the errors might need to be investigated. Microsoft Entra ID only logs the requests that matched existing accounts, thus there might have been unlogged requests for non-existing accounts. |
| Severity | Medium |
| Tactics | CredentialAccess |
| Techniques | T1110 |
| Required data connectors | AzureActiveDirectory |
| Kind | Scheduled |
| Query frequency | 1h |
| Query period | 1h |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/Analytic%20Rules/SeamlessSSOPasswordSpray.yaml |
| Version | 1.0.4 |
| Arm template | fb7ca1c9-e14c-40a3-856e-28f3c14ea1ba.json |
let account_threshold = 5;
AADNonInteractiveUserSignInLogs
//| where ResultType == "81016"
| where ResultType startswith "81"
| summarize DistinctAccounts = dcount(UserPrincipalName), DistinctAddresses = make_set(IPAddress,100) by ResultType
| where DistinctAccounts > account_threshold
| mv-expand IPAddress = DistinctAddresses
| extend IPAddress = tostring(IPAddress)
| join kind=leftouter (union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs) on IPAddress
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
UserPrincipalName = make_set(UserPrincipalName,100),
UserAgent = make_set(UserAgent,100),
ResultDescription = take_any(ResultDescription),
ResultSignature = take_any(ResultSignature)
by IPAddress, Type, ResultType
| project Type, StartTime, EndTime, IPAddress, ResultType, ResultDescription, ResultSignature, UserPrincipalName, UserAgent = iff(array_length(UserAgent) == 1, UserAgent[0], UserAgent)
| extend Name = tostring(split(UserPrincipalName[0],'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName[0],'@',1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
triggerThreshold: 0
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/Analytic%20Rules/SeamlessSSOPasswordSpray.yaml
relevantTechniques:
- T1110
queryFrequency: 1h
version: 1.0.4
status: Available
query: |
let account_threshold = 5;
AADNonInteractiveUserSignInLogs
//| where ResultType == "81016"
| where ResultType startswith "81"
| summarize DistinctAccounts = dcount(UserPrincipalName), DistinctAddresses = make_set(IPAddress,100) by ResultType
| where DistinctAccounts > account_threshold
| mv-expand IPAddress = DistinctAddresses
| extend IPAddress = tostring(IPAddress)
| join kind=leftouter (union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs) on IPAddress
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
UserPrincipalName = make_set(UserPrincipalName,100),
UserAgent = make_set(UserAgent,100),
ResultDescription = take_any(ResultDescription),
ResultSignature = take_any(ResultSignature)
by IPAddress, Type, ResultType
| project Type, StartTime, EndTime, IPAddress, ResultType, ResultDescription, ResultSignature, UserPrincipalName, UserAgent = iff(array_length(UserAgent) == 1, UserAgent[0], UserAgent)
| extend Name = tostring(split(UserPrincipalName[0],'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName[0],'@',1)[0])
triggerOperator: gt
description: |
'This query detects when there is a spike in Microsoft Entra ID Seamless SSO errors. They may not be caused by a Password Spray attack, but the cause of the errors might need to be investigated.
Microsoft Entra ID only logs the requests that matched existing accounts, thus there might have been unlogged requests for non-existing accounts.'
id: fb7ca1c9-e14c-40a3-856e-28f3c14ea1ba
severity: Medium
queryPeriod: 1h
tactics:
- CredentialAccess
requiredDataConnectors:
- dataTypes:
- AADNonInteractiveUserSignInLogs
connectorId: AzureActiveDirectory
name: Password spray attack against Microsoft Entra ID Seamless SSO
{
"$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/fb7ca1c9-e14c-40a3-856e-28f3c14ea1ba')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/fb7ca1c9-e14c-40a3-856e-28f3c14ea1ba')]",
"properties": {
"alertRuleTemplateName": "fb7ca1c9-e14c-40a3-856e-28f3c14ea1ba",
"customDetails": null,
"description": "'This query detects when there is a spike in Microsoft Entra ID Seamless SSO errors. They may not be caused by a Password Spray attack, but the cause of the errors might need to be investigated.\nMicrosoft Entra ID only logs the requests that matched existing accounts, thus there might have been unlogged requests for non-existing accounts.'\n",
"displayName": "Password spray attack against Microsoft Entra ID Seamless SSO",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "Name",
"identifier": "Name"
},
{
"columnName": "UPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "IPAddress",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft%20Entra%20ID/Analytic%20Rules/SeamlessSSOPasswordSpray.yaml",
"query": "let account_threshold = 5;\nAADNonInteractiveUserSignInLogs\n//| where ResultType == \"81016\"\n| where ResultType startswith \"81\"\n| summarize DistinctAccounts = dcount(UserPrincipalName), DistinctAddresses = make_set(IPAddress,100) by ResultType\n| where DistinctAccounts > account_threshold\n| mv-expand IPAddress = DistinctAddresses\n| extend IPAddress = tostring(IPAddress)\n| join kind=leftouter (union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs) on IPAddress\n| summarize\n StartTime = min(TimeGenerated),\n EndTime = max(TimeGenerated),\n UserPrincipalName = make_set(UserPrincipalName,100),\n UserAgent = make_set(UserAgent,100),\n ResultDescription = take_any(ResultDescription),\n ResultSignature = take_any(ResultSignature)\n by IPAddress, Type, ResultType\n| project Type, StartTime, EndTime, IPAddress, ResultType, ResultDescription, ResultSignature, UserPrincipalName, UserAgent = iff(array_length(UserAgent) == 1, UserAgent[0], UserAgent)\n| extend Name = tostring(split(UserPrincipalName[0],'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName[0],'@',1)[0])\n",
"queryFrequency": "PT1H",
"queryPeriod": "PT1H",
"severity": "Medium",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"CredentialAccess"
],
"techniques": [
"T1110"
],
"templateVersion": "1.0.4",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}