Password spraying is an elegant attack that sidesteps account lockout policies by trying a single common password against many accounts, rather than many passwords against one account. It’s one of the most common ways attackers gain initial access to corporate environments — and it works far more often than it should.
Password Spraying vs Brute Force
# Brute Force (gets caught):
# Same account, many passwords:
admin: password1 -> FAIL
admin: password2 -> FAIL (lockout at attempt 5)
admin: password3 -> LOCKOUT TRIGGERED
# Password Spraying (avoids lockout):
# One password, many accounts:
alice: Summer2024! -> FAIL
bob: Summer2024! -> FAIL
charlie: Summer2024! -> SUCCESS!
dave: Summer2024! -> FAIL
# Only 1 attempt per account = never triggers lockout
# Why common passwords work:
# Analysis of leaked corporate credentials shows:
# ~5% of users choose seasonal passwords: Winter2024!, Spring2025!
# ~3% use company name variations: Contoso2024!, Company123!
# ~2% use "password" + year: Password2024, Password2025!
Real-World Example
In 2020, the US CISA and NSA issued an advisory about Russian APT28 (Fancy Bear) conducting password spray attacks against Office 365 and on-premises email systems. In 2021, Iranian threat actors used the same technique against US critical infrastructure. Password spraying is a state-sponsored technique that works against even sophisticated organizations.
Tools Used (For Awareness)
# Password spraying tools attackers use:
# MSOLSpray (Office 365): github.com/dafthack/MSOLSpray
# Ruler (Exchange): github.com/sensepost/ruler
# Spray (SMB, LDAP, WinRM): github.com/Greenwolf/Spray
# Detection: These tools hit authentication endpoints at regular intervals
# Network signature: Many auth requests from single IP with one-per-account cadence
Detection
# Detect password spray attempts in Azure AD / Entra ID logs:
# Azure Sentinel KQL query:
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType != "0" // Failed logins only
| summarize
FailedLogins = count(),
UniqueUsers = dcount(UserPrincipalName),
UniqueIPs = dcount(IPAddress)
by bin(TimeGenerated, 5m), IPAddress
| where UniqueUsers > 10 and FailedLogins < UniqueUsers * 2
// High unique users, low attempts per user = password spray pattern
# Windows Event ID 4625 (Failed Logon) analysis:
# Many unique accounts, similar timestamps, same source IP
# Alert threshold:
# More than 10 unique accounts with failed auth in 10 minutes from same IP
Prevention
# 1. Multi-Factor Authentication (biggest impact)
# Even if password is sprayed successfully, attacker still needs 2nd factor
# 2. Conditional Access — block legacy authentication protocols:
# Legacy auth (SMTP, POP3, IMAP) doesn't support MFA!
# Azure AD Conditional Access Policy:
# Condition: Client apps = "Exchange ActiveSync clients" and "Other clients"
# Grant: Block access
# This forces modern authentication which supports MFA
# 3. Smart Lockout (Azure AD):
# Different from classic lockout - learns user behavior
# az ad sp list --query "[].{name:displayName}" > /dev/null
# Configure in: Entra ID > Authentication methods > Password protection
# 4. Password Protection / Banned Password Lists:
# Azure AD Password Protection bans common + company-specific passwords
# Install agent on on-premises AD to enforce in hybrid environments
# 5. Audit your own organization:
# Run Spray against yourself with authorization!
./spray.py -U users.txt -P Summer2024! --target 192.168.1.10 --proto smb
# Any hits? Those users need immediate password changes + security training
Wrap Up
Password spraying works because people are predictable with passwords. The defense is equally simple: enforce MFA on everything, block legacy authentication, and deploy Azure AD Password Protection to prevent seasonal/company-name passwords. Running a password spray test against your own organization (with permission) is one of the most valuable security exercises you can do.