ADPRIV-011: Privileged Users Password Not Required
- Platform
- Active Directory
- Category
- AD Privileged Account Security
- Severity
- Critical
- Zero Trust pillar
- Identity (weight 3)
- Golden fixtures
- 3
- Branch coverage
- Observed: fixtures prove the verdicts they exercise
- Provenance
- baseline
What it checks
The check enumerates the set of privileged principals in the domain and inspects their userAccountControl attribute. The protected set is defined as: - Members of the protected groups stamped by SDProp (Domain Admins, Enterprise Admins, Schema Admins, Administrators, Account Operators, Backup Operators, Print Operators, Server Operators, Domain Controllers, Read-only Domain Controllers, Replicator, Cert Publishers, Key Admins, Enterprise Key Admins) - Any object with adminCount=1 (current or orphaned protected ACE) - Members of AdminSDHolder-tracked groups resolved recursively, including nested groups and foreign-security-principals For each principal the check reads userAccountControl and tests for the PASSWD_NOTREQD bit using a bitwise AND with 0x0020 (32). The LDAP filter used is (&(objectCategory=person)(objectClass=user)(adminCount=1)(userAccountControl:1.2.840.113556.1.4.803:=32)) which leverages the LDAP_MATCHING_RULE_BIT_AND OID so the DC evaluates the bit test rather than returning every protected user. The check also records pwdLastSet (where pwdLastSet=0 combined with PASSWD_NOTREQD is the literal blank-password case), pwdLastSet age, lastLogonTimestamp, Enabled state (UAC bit 0x0002), DONT_EXPIRE_PASSWORD (UAC bit 0x10000), and the source group that placed the account in the protected set. Findings are emitted per-principal with the full UAC integer, the decoded flag list, and the path that brought the account into scope.
Why it matters
PASSWD_NOTREQD is a SAM-level override of password policy. When the flag is set, the password change codepath in the LSA skips the complexity, length, and history checks defined in the Default Domain Policy and any Fine-Grained Password Policy that would otherwise apply. The account can then be set to a blank password through LDAP, SAMR, or net user, and Kerberos pre-authentication and NTLM authentication will both succeed with that blank password. Pre-authentication with an empty password yields a valid TGT, NTLM authentication with the LM/NT hash of an empty string (AAD3B435B51404EEAAD3B435B51404EE / 31D6CFE0D16AE931B73C59D7E0C089C0) succeeds against SMB, LDAP, and HTTP services that accept NTLM, and tools such as crackmapexec, nxc, and impacket all check for this hash automatically during spray operations. The flag is also frequently set by legacy provisioning scripts, third party identity products, and migration tools (ADMT in particular sets PASSWD_NOTREQD on staged accounts), and it is a known attacker persistence primitive: an adversary with WriteProperty on userAccountControl can set the flag, blank the password, authenticate at will, and then clear the flag to evade casual auditing. On any account with adminCount=1, the result is direct, unauthenticated, and trivially scripted access to a Tier-0 identity, which is the single highest impact misconfiguration in Active Directory after a compromised krbtgt.
Attack path
1. Enumeration: from any authenticated context the attacker queries every account with the PASSWD_NOTREQD bit set using a single bitwise LDAP filter. Get-ADUser -LDAPFilter "(&(adminCount=1)(userAccountControl:1.2.840.113556.1.4.803:=32))" -Properties userAccountControl, pwdLastSet returns the candidate set. The same query is exposed by BloodHound (the dontreqpreauth and passwordnotreqd properties), by ldapsearch, and by PowerView Get-DomainUser -PreauthNotRequired -or- -SPN style filters. 2. Candidate triage: the attacker prioritises principals that are Enabled, have adminCount=1, and have pwdLastSet old enough that the password is unlikely to have been rotated by an interactive admin since the flag was set. A pwdLastSet of 0 is the smoking gun: the account has literally never had a password set. 3. Authentication attempt with empty password: the attacker tries an empty-password authentication against SMB, LDAP, and Kerberos. Examples: nxc smb <dc> -u <sam> -p "", crackmapexec ldap <dc> -u <sam> -p "", impacket-getTGT <domain>/<sam>: with no password, and Rubeus asktgt /user:<sam> /password:"". Any one of these returning success confirms the blank-password case. 4. Authentication with the empty-string NT hash: if the account refuses an empty password string but accepts NTLM, the attacker uses the static NT hash of the empty string (31D6CFE0D16AE931B73C59D7E0C089C0) for pass-the-hash via nxc, crackmapexec, impacket-psexec, or Rubeus asktgt /user:<sam> /rc4:31D6CFE0D16AE931B73C59D7E0C089C0. 5. Password spray fallback: if the account does have a non-empty password but the PASSWD_NOTREQD flag has bypassed policy, the password is almost always trivial (Password1, Welcome1, the sam account name, the company name). A short spray (Spray-AD, kerbrute passwordspray) lands the credential without lockout risk because pre-auth failures do not always increment badPwdCount in the same way as interactive failures. 6. Privilege use: with valid credentials for a Tier-0 account, the attacker runs DCSync (mimikatz lsadump::dcsync /user:krbtgt or impacket-secretsdump -just-dc), forges a Golden Ticket, and establishes long-term persistence. Where the attacker introduced the flag themselves (WriteProperty primitive on userAccountControl), they clear PASSWD_NOTREQD after the credential is captured so that routine audits do not flag the account.
How Guerrilla assesses it
Guerrilla executes a single LDAP query against a writable DC using the LDAP_MATCHING_RULE_BIT_AND OID to ask the DC to evaluate the bit test server-side: (&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=32)). The result set is then filtered to the protected population by joining against the SDProp-protected groups and against any object with adminCount=1. The bit test approach is preferred over enumerating every user and testing the flag client side because it is constant-time on the DC, returns only the affected accounts, and works correctly in domains with hundreds of thousands of users. For each match the check reads userAccountControl, pwdLastSet, lastLogonTimestamp, adminCount, memberOf, and primaryGroupID in the same LDAP call to minimise round trips. The pwdLastSet value is interpreted: a value of 0 means the password has never been set (the account is in the literal blank-password state), and any non-zero value combined with PASSWD_NOTREQD is still a Critical finding because the flag itself defeats policy. The check is LDAP-only and does not require RSAT or domain-joined execution, so it runs from a Privileged Access Workstation against a remote forest over LDAPS.
Recommended value
No privileged accounts with PASSWD_NOTREQD flag set
Remediation
Identify accounts using Get-ADUser -Filter {PasswordNotRequired -eq $true -and AdminCount -eq 1}. Clear the PASSWD_NOTREQD flag immediately and set a strong password on all identified accounts. Investigate how this flag was set as it may indicate compromise
Fixture-proven verdicts
Every verdict below is proven by a golden fixture in the module's gating test suite. This table derives from the last green run; it cannot be edited by hand.
| Scenario | Expected verdict |
|---|---|
| clean | PASS |
| known-bad | FAIL |
| throttled | Not Assessed |
Framework mappings
- NIST SP 800-53
- IA-5(1), AC-2
- ANSSI
- R36
- CIS AD Benchmark
- 4.3.2
- MITRE ATT&CK
- T1078.002