Query Details

01 ADFS Extranet Lockout

Query

id: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
name: ADFS Extranet Lockout - Sustained Brute Force (Error 396083)
version: 1.0.0
kind: Scheduled
description: |
  Detects users triggering multiple ADFS extranet lockout events (error code 396083)
  within a 24-hour window. Error 396083 is raised when the ADFS extranet lockout policy
  is activated, indicating a sustained brute force or credential stuffing attack that has
  reached the perimeter threshold. Multiple lockouts for the same user suggest an attacker
  is bypassing perimeter controls through distributed IP rotation.
  MITRE ATT&CK: T1110 (Brute Force), T1078 (Valid Accounts)
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - ADFSSignInLogs
queryFrequency: 4h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - InitialAccess
relevantTechniques:
  - T1110
  - T1078
query: |
  ADFSSignInLogs
  | where TimeGenerated > ago(1d)
  | where ResultType == 396083
  | summarize
      LockoutCount = count(),
      UniqueIPs    = dcount(IPAddress),
      IPs          = make_set(IPAddress, 20),
      Countries    = make_set(Location),
      Apps         = make_set(AppDisplayName),
      FirstSeen    = min(TimeGenerated),
      LastSeen     = max(TimeGenerated)
    by UserPrincipalName
  | where LockoutCount >= 3
  | order by LockoutCount desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
customDetails:
  LockoutCount: LockoutCount
  UniqueIPs: UniqueIPs
  Countries: Countries
alertDetailsOverride:
  alertDisplayNameFormat: "ADFS Extranet Lockout - {{UserPrincipalName}} triggered {{LockoutCount}} lockouts"
  alertDescriptionFormat: "User {{UserPrincipalName}} has triggered ADFS extranet lockout (396083) {{LockoutCount}} times from {{UniqueIPs}} unique IPs. Possible brute force attack."
  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 or credential stuffing attacks on Active Directory Federation Services (ADFS) by identifying users who trigger multiple extranet lockout events (specifically error code 396083) within a 24-hour period. Here's a simplified breakdown:

  • Purpose: To identify users who are repeatedly locked out due to failed login attempts, suggesting a possible attack.
  • How it works:
    • It examines ADFS sign-in logs over the past day (24 hours) for instances of error code 396083, which indicates an extranet lockout.
    • It counts the number of lockouts per user and gathers information on the number of unique IP addresses, the countries from which the attempts originated, and the applications involved.
    • It flags users who have been locked out three or more times.
  • Alerting:
    • If a user triggers three or more lockouts, an alert is generated.
    • The alert includes details such as the number of lockouts, unique IP addresses involved, and potential countries of origin.
  • Frequency: The query runs every 4 hours to ensure timely detection.
  • Severity: The alert is marked as high severity due to the potential security risk.
  • Incident Management:
    • Incidents are created for detected cases, with the ability to group related alerts by user account.
    • The system does not reopen closed incidents but looks back 12 hours to group related alerts.

Overall, this query helps security teams quickly identify and respond to potential unauthorized access attempts on ADFS by monitoring for patterns indicative of brute force attacks.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

ADFSSignInLogs

Keywords

ADFSExtranetLockoutUsersIPsCountriesAppsAccounts

Operators

ago==summarizecountdcountmake_setminmaxby>=order by

Actions