Detect Wide Low Volume Password Sprays


Wide, low-volume spray: from a single IP, exactly one failed attempt per user in a day, but against many different users.

KQL Library  /  Analytics Rules

 Analytics Rules Password Spray detect-wide-low-volume-password-sprays.kql

Wide, low-volume spray: from a single IP, exactly one failed attempt per user in a day, but against many different users.

 Download .kql
// Author: Ian D. Hanley (DevSecOpsDad) | linkedin.com/in/ianhanley | devsecopsdad.com | devsecopsdadattack.com
// Sentinel (SigninLogs) — detect “wide, low-volume” password sprays
// Heuristic: from a single IP, exactly one failed attempt per user in a day, but against many different users.

SigninLogs
| where ResultType != 0                             // Keep only failed sign-ins (ResultType==0 is success)
| summarize Attempts=count()
    by bin(TimeGenerated, 1d), UserPrincipalName, IPAddress, UserAgent   // Count failed attempts per day 
| where Attempts == 1                               // Keep patterns with exactly one failure per targeted user from that IP per day
                                                    // (characteristic of “one-try-per-user” low-and-slow sprays)
| summarize
    UsersTargeted = dcount(UserPrincipalName),      // How many distinct users were hit by this IP that day
    UAs = make_set(UserAgent, 20)                   // Keep up to 20 distinct UAs seen for context
  by bin(TimeGenerated, 1d), IPAddress              // Roll up to (day × IP), aggregating distinct users and UAs
| where UsersTargeted >= 10                         // Threshold: treat as suspicious only if the IP touched many users that day (tune to your baseline)
| order by TimeGenerated desc, UsersTargeted desc   // Show the most recent and broadest spray patterns first