Query Details

ADFS Brute Force to Successful Sign-In Chain

05 ADFS Brute Force Success Chain

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

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

ADFSADFSSignInLogsUserAccountIPAddressTimeGeneratedErrorCodeResultTypeUserPrincipalNameFailCountLastFailFailIPsSuccessIPFirstSuccessTimeDiffFullName

Operators

letdynamicextendtostringwhereinsummarizecountmaxmake_setbyjoinkind=inneronandprojectorder bydescago==>

Severity

High

Tactics

CredentialAccessInitialAccess

MITRE Techniques

Frequency: 1h

Period: 1d

Actions

GitHub