Query Details

Enabled Account Password Spray Detection

Query

//This query detects potential password spray attacks by comparing failed logon attempts against historical averages
//Focuses on enabled accounts with significant deviations from normal failed logon rates
let lookback = 14d;
let AverageFailedLogonPerUser = IdentityLogonEvents
| where TimeGenerated > ago(lookback)
| where isnotempty(AccountUpn)
| where ActionType == "LogonFailed"
| project TimeGenerated, AccountUpn = tolower(AccountUpn), ActionType
| summarize FailedLogonsPerMinute = count() by bin(TimeGenerated, 1m), AccountUpn
| summarize FailedLogonAverage = avg(FailedLogonsPerMinute) by AccountUpn
| extend FailedLogonAverageRounded = round(FailedLogonAverage);
let EnabledAccounts = IdentityInfo
| where TimeGenerated >= ago(lookback) 
| where IsAccountEnabled
| where isnotempty(AccountUPN)
| project AccountUPN = tolower(AccountUPN)
| distinct AccountUPN;
SigninLogs
| where TimeGenerated >= ago(1h)
| where IsInteractive
| where isnotempty(ResultDescription)
| summarize UnknownIPs = make_set(IPAddress) by bin(TimeGenerated, 1m), UserPrincipalName, AppDisplayName
| where UserPrincipalName has_any (EnabledAccounts)
| extend NumberOfFailedAttemptsPerMinute = array_length(UnknownIPs), UserPrincipalName = tolower(UserPrincipalName)
| join kind=inner AverageFailedLogonPerUser on $left.UserPrincipalName == $right.AccountUpn
| where NumberOfFailedAttemptsPerMinute > FailedLogonAverageRounded
| project-away AccountUpn 

Explanation

This query is designed to identify potential password spray attacks by analyzing failed logon attempts and comparing them to historical averages. Here's a simplified breakdown of what the query does:

  1. Historical Analysis: It looks back over the past 14 days to calculate the average number of failed logon attempts per minute for each user account. This is done by:

    • Filtering logon events to only include failed attempts.
    • Grouping these attempts by user and time to calculate the average failed logons per minute for each user.
  2. Enabled Accounts: It identifies user accounts that are currently enabled by:

    • Filtering account information to include only enabled accounts.
    • Ensuring that these accounts have a valid user principal name (UPN).
  3. Recent Logon Attempts: It examines logon attempts from the past hour to find:

    • Interactive logons with failed attempts.
    • The number of unique IP addresses associated with these failed attempts for each user.
  4. Comparison and Detection: It compares the recent failed logon attempts per minute for each user against their historical average:

    • Joins the recent logon data with historical averages for enabled accounts.
    • Flags any user whose recent failed attempts exceed their historical average.

The result is a list of user accounts that have experienced a significant increase in failed logon attempts, which could indicate a potential password spray attack.

Details

@H1dd3n00b profile picture

@H1dd3n00b

Released: November 10, 2024

Tables

IdentityLogonEventsIdentityInfoSigninLogs

Keywords

IdentityLogonEventsIdentityInfoSigninLogsAccountUpnUserPrincipalNameIPAddressAppDisplayName

Operators

letagoisnotemptytolowerprojectsummarizecountbinavgextendroundwheredistinctmake_sethas_anyarray_lengthjoinon==>project-away

Actions