Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Distributed Password cracking attempts in AzureAD

Back
Idbfb1c90f-8006-4325-98be-c7fffbc254d6
RulenameDistributed Password cracking attempts in AzureAD
DescriptionIdentifies distributed password cracking attempts from the Azure Active Directory SigninLogs.

The query looks for unusually high number of failed password attempts coming from multiple locations for a user account.

References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes

50053 Account is locked because the user tried to sign in too many times with an incorrect user ID or password.

50055 Invalid password, entered expired password.

50056 Invalid or null password - Password does not exist in store for this user.

50126 Invalid username or password, or invalid on-premises username or password.
SeverityMedium
TacticsCredentialAccess
TechniquesT1110
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/DistribPassCrackAttempt.yaml
Version1.0.2
Arm templatebfb1c90f-8006-4325-98be-c7fffbc254d6.json
Deploy To Azure
let s_threshold = 30;
let l_threshold = 3;
let aadFunc = (tableName:string){
table(tableName)
| where OperationName =~ "Sign-in activity"
// Error codes that we want to look at as they are related to the use of incorrect password.
| where ResultType in ("50126", "50053" , "50055", "50056")
| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser
| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)
| extend LocationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city))
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), LocationCount=dcount(LocationString), Location = make_set(LocationString,100),
IPAddress = make_set(IPAddress,100), IPAddressCount = dcount(IPAddress), AppDisplayName = make_set(AppDisplayName,100), ResultDescription = make_set(ResultDescription,50),
Browser = make_set(Browser,20), OS = make_set(OS,20), SigninCount = count() by UserPrincipalName, Type
// Setting a generic threshold - Can be different for different environment
| where SigninCount > s_threshold and LocationCount >= l_threshold
| extend Location = tostring(Location), IPAddress = tostring(IPAddress), AppDisplayName = tostring(AppDisplayName), ResultDescription = tostring(ResultDescription), Browser = tostring(Browser), OS = tostring(OS)
| extend timestamp = StartTime, Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
queryFrequency: 1d
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: Name
    identifier: Name
  - columnName: UPNSuffix
    identifier: UPNSuffix
- entityType: IP
  fieldMappings:
  - columnName: IPAddress
    identifier: Address
severity: Medium
triggerThreshold: 0
relevantTechniques:
- T1110
query: |
  let s_threshold = 30;
  let l_threshold = 3;
  let aadFunc = (tableName:string){
  table(tableName)
  | where OperationName =~ "Sign-in activity"
  // Error codes that we want to look at as they are related to the use of incorrect password.
  | where ResultType in ("50126", "50053" , "50055", "50056")
  | extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)
  | extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser
  | extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)
  | extend LocationString = strcat(tostring(LocationDetails.countryOrRegion), "/", tostring(LocationDetails.state), "/", tostring(LocationDetails.city))
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), LocationCount=dcount(LocationString), Location = make_set(LocationString,100),
  IPAddress = make_set(IPAddress,100), IPAddressCount = dcount(IPAddress), AppDisplayName = make_set(AppDisplayName,100), ResultDescription = make_set(ResultDescription,50),
  Browser = make_set(Browser,20), OS = make_set(OS,20), SigninCount = count() by UserPrincipalName, Type
  // Setting a generic threshold - Can be different for different environment
  | where SigninCount > s_threshold and LocationCount >= l_threshold
  | extend Location = tostring(Location), IPAddress = tostring(IPAddress), AppDisplayName = tostring(AppDisplayName), ResultDescription = tostring(ResultDescription), Browser = tostring(Browser), OS = tostring(OS)
  | extend timestamp = StartTime, Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])};
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  union isfuzzy=true aadSignin, aadNonInt  
id: bfb1c90f-8006-4325-98be-c7fffbc254d6
triggerOperator: gt
version: 1.0.2
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - SigninLogs
- connectorId: AzureActiveDirectory
  dataTypes:
  - AADNonInteractiveUserSignInLogs
description: |
  'Identifies distributed password cracking attempts from the Azure Active Directory SigninLogs.
  The query looks for unusually high number of failed password attempts coming from multiple locations for a user account.
  References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes
  50053   Account is locked because the user tried to sign in too many times with an incorrect user ID or password.
  50055   Invalid password, entered expired password.
  50056   Invalid or null password - Password does not exist in store for this user.
  50126   Invalid username or password, or invalid on-premises username or password.'  
queryPeriod: 1d
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/DistribPassCrackAttempt.yaml
status: Available
name: Distributed Password cracking attempts in AzureAD
tactics:
- CredentialAccess
kind: Scheduled
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/bfb1c90f-8006-4325-98be-c7fffbc254d6')]",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/bfb1c90f-8006-4325-98be-c7fffbc254d6')]",
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
      "kind": "Scheduled",
      "apiVersion": "2022-11-01",
      "properties": {
        "displayName": "Distributed Password cracking attempts in AzureAD",
        "description": "'Identifies distributed password cracking attempts from the Azure Active Directory SigninLogs.\nThe query looks for unusually high number of failed password attempts coming from multiple locations for a user account.\nReferences: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes\n50053   Account is locked because the user tried to sign in too many times with an incorrect user ID or password.\n50055   Invalid password, entered expired password.\n50056   Invalid or null password - Password does not exist in store for this user.\n50126   Invalid username or password, or invalid on-premises username or password.'\n",
        "severity": "Medium",
        "enabled": true,
        "query": "let s_threshold = 30;\nlet l_threshold = 3;\nlet aadFunc = (tableName:string){\ntable(tableName)\n| where OperationName =~ \"Sign-in activity\"\n// Error codes that we want to look at as they are related to the use of incorrect password.\n| where ResultType in (\"50126\", \"50053\" , \"50055\", \"50056\")\n| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(DeviceDetail), LocationDetails = todynamic(LocationDetails)\n| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser\n| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)\n| extend LocationString = strcat(tostring(LocationDetails.countryOrRegion), \"/\", tostring(LocationDetails.state), \"/\", tostring(LocationDetails.city))\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), LocationCount=dcount(LocationString), Location = make_set(LocationString,100),\nIPAddress = make_set(IPAddress,100), IPAddressCount = dcount(IPAddress), AppDisplayName = make_set(AppDisplayName,100), ResultDescription = make_set(ResultDescription,50),\nBrowser = make_set(Browser,20), OS = make_set(OS,20), SigninCount = count() by UserPrincipalName, Type\n// Setting a generic threshold - Can be different for different environment\n| where SigninCount > s_threshold and LocationCount >= l_threshold\n| extend Location = tostring(Location), IPAddress = tostring(IPAddress), AppDisplayName = tostring(AppDisplayName), ResultDescription = tostring(ResultDescription), Browser = tostring(Browser), OS = tostring(OS)\n| extend timestamp = StartTime, Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])};\nlet aadSignin = aadFunc(\"SigninLogs\");\nlet aadNonInt = aadFunc(\"AADNonInteractiveUserSignInLogs\");\nunion isfuzzy=true aadSignin, aadNonInt\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0,
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess"
        ],
        "techniques": [
          "T1110"
        ],
        "alertRuleTemplateName": "bfb1c90f-8006-4325-98be-c7fffbc254d6",
        "customDetails": null,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "identifier": "Name",
                "columnName": "Name"
              },
              {
                "identifier": "UPNSuffix",
                "columnName": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "identifier": "Address",
                "columnName": "IPAddress"
              }
            ]
          }
        ],
        "status": "Available",
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/DistribPassCrackAttempt.yaml",
        "templateVersion": "1.0.2"
      }
    }
  ]
}