Microsoft 365 Password Spray Attack Detection
Password spraying against Microsoft 365 / Entra ID remains one of the most effective initial access techniques against SMBs. Attackers use lists of valid corporate usernames (harvested from LinkedIn, HaveIBeenPwned, or prior breaches) and try a small number of common passwords (season+year, company name variations, Welcome1!) across all accounts — staying below per-account lockout thresholds. Microsoft documented Midnight Blizzard (Cozy Bear) using this to gain initial access to Microsoft corporate accounts in 2024. Storm-1152 (bulk account creation / credential fraud group) services this on behalf of other threat actors. NCSC UK has repeatedly warned about Iranian and Russian threat actors using password spraying against UK SMBs in critical sectors. The attack targets legacy authentication protocols (IMAP, SMTP, MAPI) and BasicAuth endpoints that bypass MFA — even if the organisation has MFA deployed for interactive sign-ins.
// THREAT: Microsoft 365 Password Spray Detection
// Detects systematic password spraying against M365 / Entra ID
// using sign-in failure patterns across multiple users from shared infrastructure
// Alert 1: Single IP targeting many accounts (spray pattern)
let SprayThreshold_UserCount = 10;
let SprayThreshold_FailureCount = 20;
let SprayWindow = 30min;
AADSignInLogs
| where TimeGenerated > ago(24h)
| where Status.errorCode in (50126, 50053, 50055, 50056, 50064, 50074, 50076, 50079)
// 50126=invalid credentials, 50053=locked, 50055=expired password, 50056=no password
// 50064=credential validation failure, 50074=strong auth required
| summarize
FailureCount=count(),
UniqueUsers=dcount(UserPrincipalName),
TargetUsers=make_set(UserPrincipalName),
ErrorCodes=make_set(Status.errorCode),
Apps=make_set(AppDisplayName)
by IPAddress, bin(TimeGenerated, SprayWindow)
| where UniqueUsers >= SprayThreshold_UserCount and FailureCount >= SprayThreshold_FailureCount
| extend ThreatType = "PasswordSpray_MultipleUsers_SingleIP"
| extend Severity = "High";
// Alert 2: Successful sign-in following spray activity from same IP
let SprayIPs = AADSignInLogs
| where TimeGenerated > ago(24h)
| where Status.errorCode in (50126, 50053, 50055)
| summarize Failures=count(), Users=dcount(UserPrincipalName)
by IPAddress, bin(TimeGenerated, 30m)
| where Failures >= 20 and Users >= 10
| distinct IPAddress;
AADSignInLogs
| where TimeGenerated > ago(24h)
| where Status.errorCode == 0
| where IPAddress in (SprayIPs)
| project TimeGenerated, UserPrincipalName, IPAddress, Location,
AppDisplayName, AuthenticationRequirement, Status
| extend ThreatType = "PasswordSpray_SuccessfulBreachAfterSpray"
| extend Severity = "Critical" Data Sources
Required Tables
False Positives
- Misconfigured applications using a service account that have incorrect credentials and fail authentication across multiple tenants
- Corporate password rotation events where many users have passwords expired simultaneously and attempt sign-in with old credentials
- Load-balanced authentication infrastructure where many employees share an outbound NAT IP (adjust thresholds upward for organisations with NAT)
- Vulnerability scanner credentials testing from a shared assessment IP during authorised penetration tests
References (5)
- https://www.microsoft.com/en-us/security/blog/2024/01/25/midnight-blizzard-guidance-for-responders-on-nation-state-attack/
- https://www.ncsc.gov.uk/guidance/spray-attacks
- https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-347a
- https://attack.mitre.org/techniques/T1110/003/
- https://github.com/dafthack/MSOLSpray
Unlock Pro Content
Get the full detection package for THREAT-M365-PasswordSpray including response playbook, investigation guide, and atomic red team tests.