Query Details

08 Brute Force Success Chain

Query

id: b3c9d1e2-f7a8-0b4c-5d6e-7f8a9b0c1d2e
name: Brute Force Success - Credential Stuffing Succeeded
version: 1.0.0
kind: Scheduled
description: |
  Detects the classic credential stuffing / brute force success pattern: a user account
  experiences multiple non-interactive sign-in failures with brute-force-related error
  codes (wrong password, locked account, etc.) followed by a successful sign-in — indicating
  the attacker eventually guessed or stuffed the correct credentials.
  MITRE ATT&CK: T1110 (Brute Force), T1110.004 (Credential Stuffing)
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
queryFrequency: 1h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - InitialAccess
relevantTechniques:
  - T1110
query: |
  let BruteForceErrors = dynamic(["50126","50055","50056","50064","50053","50034","50057","50128"]);
  let Failures =
      AADNonInteractiveUserSignInLogs
      | where TimeGenerated > ago(3h)
      | extend ErrorCode = tostring(ResultType)
      | where ErrorCode in (BruteForceErrors)
      | summarize
          FailCount = count(),
          LastFail  = max(TimeGenerated),
          FailIPs   = make_set(IPAddress),
          Countries = make_set(Location)
        by UserPrincipalName;
  AADNonInteractiveUserSignInLogs
  | where TimeGenerated > ago(3h)
  | where ResultType == 0
  | summarize
      FirstSuccess = min(TimeGenerated),
      SuccessIP    = tostring(make_set(IPAddress)[0])
    by UserPrincipalName
  | join kind=inner Failures on UserPrincipalName
  | where FailCount > 5
     and  FirstSuccess > LastFail
  | project
      UserPrincipalName,
      FailCount,
      LastFail,
      FirstSuccess,
      TimeBetweenFailAndSuccess = (FirstSuccess - LastFail),
      FailIPs,
      SuccessIP,
      Countries
  | order by FailCount desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SuccessIP
customDetails:
  FailureCount: FailCount
  Countries: Countries
  FailureIPs: FailIPs
alertDetailsOverride:
  alertDisplayNameFormat: "Brute Force Succeeded - {{UserPrincipalName}} had {{FailCount}} failures then logged in successfully"
  alertDescriptionFormat: "Account {{UserPrincipalName}} experienced {{FailCount}} failed sign-in attempts before a successful non-interactive sign-in from {{SuccessIP}}. The pattern matches credential stuffing or brute force success."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AnyAlert
    groupByEntities:
      - Account
    groupByAlertDetails: []
    groupByCustomDetails: []

Explanation

This query is designed to detect successful brute force or credential stuffing attacks on user accounts. Here's a simple breakdown of what it does:

  1. Purpose: It identifies when a user account experiences multiple failed sign-in attempts due to brute-force-related errors (like wrong passwords or locked accounts) followed by a successful sign-in. This pattern suggests that an attacker may have guessed or used the correct credentials.

  2. Data Source: The query uses logs from Azure Active Directory, specifically focusing on non-interactive user sign-in logs.

  3. Time Frame: It examines sign-in attempts within the last 3 hours and runs every hour.

  4. Error Codes: It looks for specific error codes that are commonly associated with brute force attacks.

  5. Process:

    • It first identifies accounts with multiple failed sign-in attempts (more than 5) using the specified error codes.
    • Then, it checks if there was a successful sign-in for the same account after the last failed attempt.
    • It calculates the time between the last failed attempt and the first successful sign-in.
  6. Output: The query outputs details such as the user account name, the number of failed attempts, the time of the last failure, the time of the first success, the IP addresses involved, and the countries from which the failed attempts originated.

  7. Alerting: If such a pattern is detected, it triggers an alert with a high severity level. The alert includes details about the user account and the nature of the sign-in attempts.

  8. Incident Management: It can create an incident in the system for further investigation, grouping alerts by account to manage related incidents effectively.

Overall, this query helps security teams quickly identify and respond to potential credential stuffing or brute force attacks on their systems.

Details

David Alonso profile picture

David Alonso

Released: May 29, 2026

Tables

AADNonInteractiveUserSignInLogs

Keywords

AzureActiveDirectoryAADNonInteractiveUserSignInLogsUserPrincipalNameIPAddressLocationAccountIP

Operators

letdynamicinagoextendtostringsummarizecountmaxmake_setbywhere==joinkind=inneron>andproject-order bydesc

Actions