ADPWD-007: Password History Enforcement
- Platform
- Active Directory
- Category
- AD Password & Lockout Policies
- Severity
- Medium
- 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 binds to the domain NC and reads the pwdHistoryLength attribute on the domain object (DC=domain,DC=tld), which is set through Default Domain Policy and is the only place the legacy domain-wide policy is enforced. It then enumerates every msDS-PasswordSettings (Fine-Grained Password Policy / PSO) object under CN=Password Settings Container,CN=System,DC=domain,DC=tld and reads msDS-PasswordHistoryLength on each. Both the domain value and every PSO value are evaluated against the recommended floor of 24. For context, the check also reads the companion attributes that determine whether the history is meaningful: - minPwdAge on the domain object (and msDS-MinimumPasswordAge on each PSO): must be at least 1 day or a user can self-cycle through the whole history in one session. - maxPwdAge / msDS-MaximumPasswordAge: informational only for this check. - pwdProperties bit DOMAIN_PASSWORD_NO_CLEAR_CHANGE: informational. The LDAP query is performed against any reachable DC; the values are domain-wide and replicated, so no per-DC iteration is required.
Why it matters
Password history is the only AD control that prevents trivial password reuse on rotation. Without it (pwdHistoryLength = 0) a user can satisfy a forced reset by setting the password back to the value it had ten seconds ago. With a shallow history (1 to 5) a user with knowledge of a rotation requirement can rotate through a handful of throwaway passwords and land back on the original. In both cases, every other control that depends on rotation invalidating a leaked credential (post-incident password resets, scheduled rotations on shared service accounts, mandatory resets after a phishing event) is silently defeated. The blast radius is largest in three scenarios. First, after a credential dump (DCSync, NTDS.dit extraction, LSASS scrape) the IR team typically forces a domain-wide reset; if pwdHistoryLength is low, users restore previous passwords and the attacker hashes from the dump remain valid. Second, on privileged service accounts whose password is known to multiple administrators, weak history lets a departing admin re-establish a password they know. Third, weak history undermines the assumption behind detection rules built around AS-REP roasting and Kerberoasting: if a service account password rotates but reverts to an old offline-cracked value, the offline crack stays valid.
Attack path
1. Discovery: the attacker queries the domain policy via LDAP (Get-ADDomain, or a direct search for pwdHistoryLength and minPwdAge on the domain object). PSOs are enumerated under the Password Settings Container. No special rights are required; any authenticated user can read these attributes. 2. Targeting: the attacker (or, more commonly, an insider) identifies an account whose password they once knew, or a service account whose old password is documented in a runbook or chat log. 3. Cycling: if minPwdAge is 0 and pwdHistoryLength is low, the attacker (operating as the compromised user) issues N+1 password changes in rapid succession (net user, Set-ADAccountPassword, or the SAMR password change RPC) to push the original value out of the history buffer, then sets the password back to the originally known value. The account now has a known password and the reset has been defeated. 4. Post-IR persistence: after an incident-response domain-wide reset, the attacker who still controls one compromised endpoint waits for the user to log in, captures the new password via keylogger or LSASS, then performs the cycle attack to restore a password that matches an older NTLM hash already in their possession (useful for offline Pass-the-Hash and lateral movement). 5. Service-account abuse: on a shared service account, an operator who knows the old password cycles forward and back to defeat the mandated rotation, keeping their out-of-band copy valid. This is a frequent finding in audits of privileged service accounts that lack gMSA conversion.
How Guerrilla assesses it
Guerrilla uses Get-ADDomain to read pwdHistoryLength, minPwdAge, maxPwdAge, lockoutThreshold, and pwdProperties in a single bind, then enumerates Get-ADFineGrainedPasswordPolicy -Filter * to pull every PSO and its msDS-PasswordHistoryLength, msDS-MinimumPasswordAge, and msDS-PasswordSettingsPrecedence. Each value is compared to a recommended floor of 24 (history) and 1 day (minimum age). A finding is emitted per scope (domain or PSO) with the observed value, the recommended value, the precedence (for PSOs), and the AppliesTo set so triage can identify which users are affected. The check does not require Domain Admin: read of msDS-PasswordSettings is restricted by default, but the check tolerates AccessDenied on individual PSOs and reports them as Unknown rather than failing the run. For change detection, security teams should pair this check with Event ID 4739 (Domain Policy was changed) and 4719 (System audit policy was changed) forwarded from every DC, plus DS object auditing (4662) on the domain object and on CN=Password Settings Container to surface ad-hoc PSO modifications.
Recommended value
Password history remembering at least 24 previous passwords
Remediation
Configure in Default Domain Policy: Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy > 'Enforce password history' = 24 passwords remembered. Also ensure 'Minimum password age' is set to at least 1 day to prevent rapid cycling through the history
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)
- CIS Benchmark
- 1.1.1
- ANSSI
- R34
- CIS AD Benchmark
- 5.2.3
- MITRE ATT&CK
- T1110.001