Password Spraying: Low-and-Slow Attacks Against a Domain
Password spraying trades speed for stealth: one common password against the whole domain, timed to stay under the lockout threshold. How to build the list, pick passwords, spray SMB, LDAP and Kerberos with netexec and kerbrute, and shut it down.
Password spraying is the patient cousin of the brute force attack: instead of hammering one account with a long wordlist, you take a single likely password and try it quietly against every account in the domain. One guess per user, then you wait. That inversion is the whole trick, because it keeps every account below its lockout threshold while still giving you dozens of chances to hit someone who picked a weak password. In a domain of a few hundred users, someone almost always did.
This guide walks the full operation the way it runs on an engagement: build the user list, work out the username format, pick passwords that real people actually use, respect the lockout policy, then spray the right surfaces with kerbrute and netexec and see what a single valid credential opens up. It ends on the defender side, with the detection signatures that give a spray away and the controls that make it pointless.
Everything here is for a lab you own or a domain you are authorized to test, with the example domain lab.0xpriv.dev living on 10.10.10.0/24. Spraying accounts you do not have written permission to assess is unauthorized access and a crime. Build a small Active Directory lab and practice there.
01What password spraying is
Password spraying is an authentication attack that tries one password across many accounts rather than many passwords against one account. You pick a password that a chunk of the user base is statistically likely to have chosen, send a single attempt for every account you know about, and record the hits. Because each individual account only ever sees one failed attempt per round, the attack stays quiet in the one place defenders usually watch: the per-account bad-password counter.
The technique maps to MITRE ATT&CK as T1110.003, a sub-technique of brute forcing. It shows up constantly in real intrusions because it needs almost nothing to start: a list of usernames and one network path to an authentication service. There is no exploit, no payload, no memory corruption. You are simply logging in, over and over, as the users who were going to let you in anyway.
02How it differs from brute force
The difference from brute force is a ninety-degree turn in how you walk the matrix of users and passwords. A classic brute force attack fixes one username and pushes a thousand passwords at it. That trips the account lockout policy in seconds and lights up the logs for that single account. Spraying fixes one password and walks sideways across every username, so no account crosses its threshold and the noise spreads thin across hundreds of victims instead of piling onto one.
That is why spraying is a low-and-slow attack. You are not trying to be fast. You are trying to stay under a number, the lockout threshold, and a clock, the observation window, while fishing the whole pond with a single bait.
03Building the user list first
Building a user list is the first job, because a spray is only as good as the names you aim it at. There are two ways to get names, and on most engagements you use both. The external way is open-source intelligence: harvest employee names from LinkedIn, the company website, conference talks, GitHub commits and breach data, then transform them into the organization's username format. The internal way is Active Directory enumeration, which you can do the moment you have any network foothold, even without credentials.
From inside the network, an unauthenticated or low-privilege query against the domain controller often returns the full account list. Kerbrute can confirm which names are real before you ever send a password, and it does so through Kerberos pre-authentication, which is quieter than a logon attempt.
# 0xpriv.dev/red-team # validate guessed usernames via Kerberos AS-REQ, before any password is tried kerbrute userenum -d lab.0xpriv.dev --dc 10.10.10.5 names.txt # with any valid domain creds, pull the whole account list with netexec nxc smb 10.10.10.5 -u j.doe -p 'Autumn2024!' --users
Username enumeration matters because every invalid name you spray is wasted noise and a wasted request. Confirm the names first, then spray only the live ones. Kerbrute's userenum tells valid from invalid by the Kerberos error it gets back: a known user that requires pre-authentication answers differently from a principal that does not exist.
04Working out the username format
The username format is the bridge between a human name and a thing you can log in as, so pin it down before you build the final list. Organizations are boringly consistent here. Once you confirm that Jane Doe is j.doe, you can expand your entire OSINT name list the same way. Common patterns are easy to generate and test in bulk.
| Format | Jane Doe becomes |
|---|---|
| first initial + last | jdoe |
| first.last | jane.doe |
| first initial . last | j.doe |
| last + first initial | doej |
| first + last initial | janed |
Generate each candidate format for a handful of known employees, run them through kerbrute userenum, and whichever pattern comes back valid is the house style. Apply that one pattern to your full OSINT list and you have a clean target set with almost no invalid names in it.
05Choosing candidate passwords
Choosing candidate passwords is where experience beats a giant wordlist, because a spray lives or dies on a short list of very human guesses. You are not trying every password. You are trying the two or three that a complexity policy quietly pushes everyone toward. The winners are predictable: the current season joined to the year, the company name with a number and a symbol, and the eternal Password1 family that satisfies the default complexity rule with the least possible thought.
| Pattern | Examples |
|---|---|
| Season + year | Autumn2024!, Winter2025!, Spring2025! |
| Company + number | Acme2025!, Acme123! |
| Classic complexity-minimum | Password1, Password1!, Welcome1 |
| Month + year | October2025! |
Pick the single best guess for the current date and the target, for example the current season and year with a trailing symbol, and spray that one password across the whole list. Then wait, and only after the observation window has reset do you spray the next candidate. One password per round is the discipline that keeps every account safe from lockout.
If the domain requires a minimum length of twelve, Password1 cannot exist on it, so do not waste a round on it. Netexec can read the policy for you with any valid credential, which turns password choice from a guess into an informed bet.
06Respecting the lockout policy and observation window
The lockout policy is the single number that governs the whole operation, so read it before you send a single attempt. Three settings matter. The lockout threshold is how many bad passwords an account tolerates before it locks. The observation window, or reset counter, is how long the account remembers those bad attempts before the counter rolls back to zero. The lockout duration is how long a locked account stays locked. Lock a real user out and you have both failed the engagement goal and handed the defender a flashing alarm.
# read the domain password and lockout policy with any valid credential nxc smb 10.10.10.5 -u j.doe -p 'Autumn2024!' --pass-pol
$ nxc smb 10.10.10.5 -u j.doe -p 'Autumn2024!' --pass-pol SMB 10.10.10.5 445 DC01 [+] Dumping password info for domain: LAB SMB 10.10.10.5 445 DC01 Minimum password length: 7 SMB 10.10.10.5 445 DC01 Account Lockout Threshold: 5 SMB 10.10.10.5 445 DC01 Reset Account Lockout Counter after: 30 minutes SMB 10.10.10.5 445 DC01 Locked Account Duration: 30 minutes
With a threshold of five and a reset window of thirty minutes, one attempt per account per round leaves four attempts of headroom, and the counter clears every thirty minutes anyway. If you cannot read the policy, assume the worst: a threshold of three and a short window. Send one attempt, wait longer than the observation window, then send the next. Slow, but no account ever locks.
07Spraying surfaces and the tools for each
The spraying surface is whichever service will answer an authentication attempt, and a domain exposes several. Internally you spray SMB, LDAP and Kerberos pre-authentication against the domain controller. Externally you spray whatever faces the internet, which is usually Outlook Web Access, Microsoft 365, a VPN portal or a remote desktop gateway. The protocol changes but the logic is identical: one password, every user, one round.
| Surface | Tool and invocation | Note |
|---|---|---|
| SMB (internal) | nxc smb | Reliable, reads policy, counts as a logon |
| LDAP (internal) | nxc ldap | Simple bind, same credential check |
| Kerberos pre-auth | kerbrute passwordspray | Fast, speaks straight to the KDC |
| OWA / Microsoft 365 | MailSniper, TREVORspray, o365spray | External, watch cloud smart lockout |
# SMB spray: one password across the whole validated user list nxc smb 10.10.10.5 -u users.txt -p 'Autumn2024!' --continue-on-success # Kerberos spray straight at the KDC, no SMB session needed kerbrute passwordspray -d lab.0xpriv.dev --dc 10.10.10.5 users.txt 'Autumn2024!' # LDAP simple bind as an alternative internal surface nxc ldap 10.10.10.5 -u users.txt -p 'Autumn2024!' --continue-on-success
The --continue-on-success flag matters for a spray: without it netexec stops at the first hit, and you want the full list of valid credentials from one round, not just the first lucky one. Kerberos spraying through kerbrute is the quietest internal option because it talks to the key distribution center directly and does not open an SMB session, though it still produces Kerberos events on the controller.
$ nxc smb 10.10.10.5 -u users.txt -p 'Autumn2024!' --continue-on-success SMB 10.10.10.5 445 DC01 [-] LAB\a.admin:Autumn2024! STATUS_LOGON_FAILURE SMB 10.10.10.5 445 DC01 [-] LAB\s.khan:Autumn2024! STATUS_LOGON_FAILURE SMB 10.10.10.5 445 DC01 [+] LAB\j.doe:Autumn2024! SMB 10.10.10.5 445 DC01 [-] LAB\m.lee:Autumn2024! STATUS_LOGON_FAILURE
08Timing to stay under lockout thresholds
Timing is the control that separates a professional spray from a self-inflicted outage, so plan it against the policy you read earlier. The rule is one attempt per account per observation window, with a safety margin on top. If the reset window is thirty minutes, spray once, then sleep thirty-one minutes before the next password. Across a working day that is roughly a dozen distinct passwords tried against every account, and no account ever sees more than one bad attempt inside any single window.
$ kerbrute passwordspray -d lab.0xpriv.dev --dc 10.10.10.5 users.txt 'Autumn2024!' Done! Tested 92 logins (0 successes) in 4.1 seconds $ sleep 1860 # wait out the reset window before the next password $ kerbrute passwordspray -d lab.0xpriv.dev --dc 10.10.10.5 users.txt 'Winter2025!' Done! Tested 92 logins (1 success) in 4.0 seconds
Microsoft 365 smart lockout tracks source, behavior and familiar locations rather than a flat counter, and it can lock an account from a single suspicious attempt or let a familiar device through. Against cloud surfaces, go slower still, spread attempts across time, and never assume the on-prem math applies.
09What one valid credential unlocks next
A single valid credential is the real prize, because in a domain one low-privilege account is a door into everything else. The password itself is almost beside the point. What that account grants you is read access to the directory, a session you can run enumeration from, and a starting node for mapping a path to Domain Admin.
Map the domain
Run BloodHound collection as the new user to graph every group, ACL and session, then look for the shortest path to high privilege.
Enumerate access
List readable shares and services with the credential, since config files and scripts on open shares often hold more passwords.
Kerberoast
Any domain user can request service tickets for accounts with a service principal name and crack them offline, turning one weak password into a service account.
Reuse and pivot
Try the same password on other users and on local accounts. Password reuse across a domain is the norm, not the exception.
# from one valid credential, graph the domain and list what it can reach nxc smb 10.10.10.5 -u j.doe -p 'Winter2025!' --shares nxc ldap 10.10.10.5 -u j.doe -p 'Winter2025!' --bloodhound --collection All # request roastable service tickets as any authenticated user nxc ldap 10.10.10.5 -u j.doe -p 'Winter2025!' --kerberoasting roast.txt
10A worked lab example on 10.10.10.0/24
A worked example ties the whole chain together on the lab domain lab.0xpriv.dev, with the controller DC01 at 10.10.10.5. Start with nothing but a few employee names scraped from the company site. The goal is a single valid credential, reached without locking out one account.
Confirm the format
Run kerbrute userenum -d lab.0xpriv.dev --dc 10.10.10.5 names.txt with several formats for known staff. The j.doe style comes back valid, so that is the house pattern.
Build the list
Apply the confirmed format to the full OSINT name set, giving a clean users.txt of 92 live accounts.
Read the policy
A low-privilege account is not available yet, so assume a conservative threshold of three and a thirty-minute window, and pace accordingly.
Spray round one
Autumn2024! against all 92 users returns no hits. Wait out the window.
Spray round two
Winter2025! lands on j.doe. One valid credential, zero lockouts, and the start of a path into the domain.
From that single credential you re-read the real password policy, run BloodHound as j.doe, and look for the reuse and the misconfigured ACL that carry you toward Domain Admin. The spray was never the end. It was the cheapest possible way in.
11Detecting password spraying
Detecting a spray is about spotting a horizontal pattern, because the attack hides from per-account thresholds but is loud across the directory as a whole. The signature is many accounts each seeing a single failed logon from one source in a tight window, which is the inverse of the vertical burst a brute force attack produces against one account. Watch the behavior, not a payload.
| Event ID | What it means in a spray |
|---|---|
| 4625 | Failed logon; a spray shows one per account across many accounts |
| 4771 | Kerberos pre-auth failed, failure code 0x18 means a bad password |
| 4768 | Kerberos TGT requested; a flood of these from one host is suspect |
| 4776 | NTLM credential validation on the domain controller |
Build the detection on correlation, not count. Group authentication failures by source address inside a sliding window and alert when the number of distinct accounts failing against that source crosses a low bar, for example ten different accounts in fifteen minutes with one failure each. On cloud identity, add impossible-travel logic: the same account or source authenticating from two distant locations in a span too short to fly between them is a strong spray-or-replay signal. Both patterns stand out far better than any single log line does on its own.
12Shutting it down as a defender
Shutting a spray down means removing the thing it depends on, which is a predictable password reachable without a second factor. No single control is enough, so stack them. The following box is the set that, together, turns a spray from a reliable technique into wasted effort.
13FAQ
How is password spraying different from brute force?FAQ
Brute force tries many passwords against one account and trips its lockout almost immediately. Password spraying tries one password against many accounts, so each account only ever sees a single failed attempt per round and stays under the lockout threshold. Same goal, opposite direction across the user and password matrix.
How do I avoid locking accounts out while spraying?FAQ
Read the domain lockout policy first, then send only one attempt per account per observation window with a safety margin. If the reset window is thirty minutes, spray once and wait thirty-one minutes before the next password. When you cannot read the policy, assume a low threshold and a short window and go slower.
Which surface should I spray on an internal engagement?FAQ
SMB and LDAP against the domain controller with netexec are reliable and let you read the password policy. Kerberos pre-authentication with kerbrute is the quietest option since it talks to the KDC without opening an SMB session, though it still writes Kerberos events. Externally, target OWA, Microsoft 365 or a VPN portal.
What do defenders see when a spray runs?FAQ
A cluster of failed logons, event 4625 or Kerberos 4771 with failure code 0x18, spread across many distinct accounts from one source in a short window, each account failing only once. That horizontal pattern is the giveaway, so detection should correlate failures by source rather than count them per account.
Does MFA stop password spraying completely?FAQ
MFA stops a sprayed password from becoming a session, which neutralizes the attack on any surface it protects. The gap is legacy authentication: basic-auth protocols that bypass MFA. Disable legacy auth alongside MFA, add a banned-password list, and the technique has nothing left to stand on.
references
Founder of 0xpriv, covering offensive security, web application vulnerabilities, and practical security research through PHP, Python, and hands-on technical analysis.