🛡️ Methodology Checklist
- Enumerate password policy BEFORE spraying:
net accounts /domain - Check lockout threshold and observation window
- Get Fine-Grained Password Policies (PSOs):
Get-DomainFGPasswordPolicy - Build userlist from: NULL session enum, Kerbrute, LinkedIn
- Deduplicate and validate list: remove disabled accounts
- Calculate safe spray frequency: 1 attempt per (lockout_window - 5 min)
- Document policy before and spray timing for report
🎯 Operational Context
Use when: Before any password spraying — extract the domain password policy to calculate safe spray intervals and lockout thresholds.
Think Dumber First: One lockout is a failed pentest. Run nxc smb [DC] --pass-pol before any spray. Extract user list from LDAP, not brute force. Never spray more than 1 password per 30 minutes without confirming the observation window.
Skip when: Fine-grained password policies (PSOs) may apply to sensitive accounts — enumerate per-account policy too.
⚡ Tactical Cheatsheet
| Command | Tactical Outcome |
|---|---|
rpcclient -U "" -N [DC_IP] → getdompwinfo | Pull password policy via RPC null session |
nxc smb [DC_IP] -u [USER] -p [PASS] --pass-pol | Pull password policy authenticated via SMB |
enum4linux-ng -P [DC_IP] -oA output | Automated password policy extraction to JSON/YAML |
ldapsearch -H ldap://[DC_IP] -x -b "DC=[DOMAIN],DC=[TLD]" -s sub "*" | grep -m 1 -B 10 pwdHistoryLength | LDAP anon bind — pull policy attributes |
net accounts | Pull password policy from domain-joined Windows host |
Get-DomainPolicy | PowerView — full policy dump |
enum4linux -U [DC_IP] | grep "user:" | cut -f2 -d"[" | cut -f1 -d"]" | Extract user list via SMB null session |
rpcclient -U "" -N [DC_IP] → enumdomusers | List all domain users via RPC null session |
nxc smb [DC_IP] -u [USER] -p [PASS] --users | Enumerate users with badpwdcount and badpwdtime |
ldapsearch -h [DC_IP] -x -b "DC=[DOMAIN],DC=[TLD]" -s sub "(&(objectclass=user))" | grep sAMAccountName: | cut -f2 -d" " | LDAP anon bind — extract all user sAMAccountNames |
./windapsearch.py --dc-ip [DC_IP] -u "" -U | Windapsearch — anonymous LDAP user extraction |
kerbrute userenum -d [DOMAIN] --dc [DC_IP] [USER_LIST] | Validate usernames via Kerberos pre-auth (no lockout) |
🔬 Deep Dive & Workflow
Why Policy Enumeration Comes First
Spraying without knowing the lockout threshold is an engagement-ending mistake. A single over-aggressive run can lock out hundreds of accounts, trigger SOC alerts, and halt business operations. Always retrieve:
| Metric | Why It Matters |
|---|---|
| Lockout Threshold | Max attempts — stay 1-2 below this number |
| Lockout Duration | Time to wait between spray rounds |
| Observation Window | Window in which failed attempts are counted |
| Min Password Length | Minimum password to attempt |
| Complexity Requirement | Whether uppercase/symbol required |
Default AD trivia: New domain default minimum password length is 7. Most production environments configure 8+.
Policy Retrieval by Access Level
Unauthenticated (null session — legacy misconfiguration):
rpcclient -U "" -N [DC_IP]
rpcclient $> getdompwinfo
enum4linux-ng -P [DC_IP] -oA enum_outputUnauthenticated (LDAP anonymous bind):
ldapsearch -H ldap://[DC_IP] -x -b "DC=INLANEFREIGHT,DC=LOCAL" -s sub "*" | grep -m 1 -B 10 pwdHistoryLengthAuthenticated:
nxc smb [DC_IP] -u [USER] -p [PASS] --pass-polFrom domain-joined Windows:
net accounts
Get-DomainPolicy # requires PowerViewUser List Building — Method Selection
| Context | Tool | Notes |
|---|---|---|
| Null session available | rpcclient enumdomusers / enum4linux -U | No creds needed |
| LDAP anon bind | ldapsearch / windapsearch -u "" | No creds needed |
| Authenticated | nxc smb --users | Shows badpwdcount — critical for safe spraying |
| Kerberos only | kerbrute userenum | No lockout; generates Event ID 4768 not 4625 |
The badpwdcount Check
Before spraying, always run nxc --users to check badpwdcount per user. Remove any account within 1-2 attempts of the lockout threshold from your target list. Failed login attempts are tracked per DC — for accurate counts, query the PDC Emulator FSMO role holder.
Operational Safety Rules
- If policy cannot be retrieved: assume threshold = 3, attempt max 1-2 passwords, wait 60+ minutes between rounds
- Log every spray: accounts targeted, DC IP, timestamp, passwords attempted
STATUS_ACCOUNT_LOCKED_OUT/ error 1909 = stop immediately
🛠️ Troubleshooting & Edge Cases
| Problem | Cause | Fix |
|---|---|---|
| nxc —pass-pol returns blank | Null session blocked | Use credentialed: nxc smb [DC] -u [USER] -p [PASS] --pass-pol |
| Policy shows ‘lockout threshold: 0’ | Lockout disabled | Confirm with net accounts /domain from inside — still limit spray rate to avoid detection |
| User list from ldapdomaindump is empty | No LDAP access | Use rpcclient -U '[USER]%[PASS]' [DC] -c 'enumdomusers' |
| Spray triggers lockout despite following policy | Fine-grained PSO applies | Check for PSOs: Get-ADFineGrainedPasswordPolicy -Filter * or dsquery * -filter "(objectClass=msDS-PasswordSettings)" |
| User list has disabled accounts | LDAP returns all objects | Filter: grep -v '66050|66082|514|546' users.txt — these UAC values indicate disabled |
📝 Reporting Trigger
Finding Title: Domain Password Policy Permits Targeted Password Spraying Impact: A lockout threshold ≥5 attempts or observation window >30 minutes allows an attacker to spray common passwords across all domain accounts without triggering lockouts, enabling credential compromise at scale. Root Cause: Default or weak domain password policy. No fine-grained password policies for privileged accounts. Absence of anomalous authentication alerting. Recommendation: Set lockout threshold to 3-5 attempts with 15-minute observation window. Implement fine-grained PSOs for privileged accounts with stricter settings. Deploy anomalous authentication alerts via Microsoft Sentinel or Defender for Identity.