Query Details

05 ADFS Brute Force Success Chain

Query

id: e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8a9b
name: ADFS Brute Force to Successful Sign-In Chain
version: 1.0.0
kind: Scheduled
description: |
  Detects the "attacker guessed the password" pattern: the same user account accumulates
  more than 5 brute-force credential failures in ADFS, followed by a successful sign-in
  where the success occurs AFTER the last failure. This sequence is a strong indicator of
  a successful credential compromise via brute force against the ADFS federation endpoint.
  The SuccessIP vs FailIPs comparison helps identify whether the attacker IP changed after
  the compromise.
  MITRE ATT&CK: T1110 (Brute Force), T1078 (Valid Accounts)
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - ADFSSignInLogs
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - InitialAccess
relevantTechniques:
  - T1110
  - T1078
query: |
  let BruteForceErrors = dynamic(["50126", "50055", "50056", "50064",
                                    "50053", "50034", "50057", "396083"]);
  let Failures =
      ADFSSignInLogs
      | where TimeGenerated > ago(1d)
      | extend ErrorCode = tostring(ResultType)
      | where ErrorCode in (BruteForceErrors)
      | summarize
          FailCount = count(),
          LastFail  = max(TimeGenerated),
          FailIPs   = make_set(IPAddress)
        by UserPrincipalName;
  ADFSSignInLogs
  | where TimeGenerated > ago(1d)
  | 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,
      TimeDiff  = FirstSuccess - LastFail,
      FailIPs,
      SuccessIP
  | order by FailCount desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SuccessIP
customDetails:
  FailCount: FailCount
  SuccessIP: SuccessIP
alertDetailsOverride:
  alertDisplayNameFormat: "ADFS Brute Force Success - {{UserPrincipalName}} compromised after {{FailCount}} failures"
  alertDescriptionFormat: "User {{UserPrincipalName}} had {{FailCount}} ADFS failures followed by a successful login from {{SuccessIP}}. Account may be compromised."
  alertSeverityColumnName: ""
  alertTacticsColumnName: ""
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT12H
    matchingMethod: AnyAlert
    groupByEntities:
      - Account
    groupByAlertDetails: []
    groupByCustomDetails: []

Explanation

This query is designed to detect potential brute force attacks on Active Directory Federation Services (ADFS). It looks for patterns where a user account experiences more than five failed login attempts due to specific error codes associated with brute force attempts, followed by a successful login. The successful login must occur after the last failed attempt, indicating that the attacker may have guessed the correct password.

Here's a simple breakdown of what the query does:

  1. Identify Failed Attempts: It checks the ADFS sign-in logs for the past day to find failed login attempts with specific error codes that suggest brute force attacks. It counts these failures and notes the last failure time and the IP addresses used.

  2. Identify Successful Logins: It then looks for successful logins (where the result type is 0) within the same timeframe and records the time of the first successful login and the IP address used.

  3. Match Failures to Success: The query matches users who had more than five failed attempts with a subsequent successful login. It ensures the successful login happened after the last failed attempt.

  4. Analyze and Alert: If such a pattern is found, it generates an alert indicating a potential account compromise. The alert includes details like the number of failed attempts, the time difference between the last failure and the first success, and the IP addresses involved.

  5. Incident Management: The query is set to create an incident if such an alert is triggered, grouping alerts by account to manage potential compromises effectively.

Overall, this query helps security teams identify and respond to potential brute force attacks that result in successful unauthorized access to user accounts.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

ADFSSignInLogs

Keywords

ADFSADFSSignInLogsUserAccountIPAddressTimeGeneratedErrorCodeResultTypeUserPrincipalNameFailCountLastFailFailIPsSuccessIPFirstSuccessTimeDiffFullNameAddress

Operators

letdynamicextendtostringwhereinsummarizecountmaxmake_setbyjoinkind=inneronandprojectorder bydescago==>>

Actions