ADPRIV-016: Privileged Accounts Weak Passwords

Platform
Active Directory
Category
AD Privileged Account Security
Severity
Critical
Zero Trust pillar
Identity (weight 2)
Golden fixtures
3
Branch coverage
Observed: fixtures prove the verdicts they exercise
Provenance
baseline

What it checks

The check performs a two-stage evaluation. Stage one builds the privileged principal set by querying every group with the well-known RID 512 (Domain Admins), 519 (Enterprise Admins), 518 (Schema Admins), 544 (Administrators), 548 (Account Operators), 551 (Backup Operators), 550 (Print Operators), 549 (Server Operators), and 516 (Domain Controllers), then expands every group recursively to the leaf user objects. It also pulls every user object where adminCount = 1 (principals stamped by AdminSDHolder/SDProp), every member of any group listed in DSHeuristics dsHeuristics positions 16+ (custom protected groups), and every account flagged in msDS-GroupManagedServiceAccount or with servicePrincipalName populated and a manual privileged role. Stage two evaluates password strength. With explicit operator consent and Replicating Directory Changes All rights, Guerrilla invokes the DSInternals Get-ADReplAccount cmdlet against a writable DC to retrieve the NT hash for each privileged principal via the MS-DRSR GetNCChanges RPC. The NT hash (a single unsalted MD4 over the UTF-16LE password) is compared against: - The HaveIBeenPwned NTLM hash list (over one billion compromised hashes) - The top one million entries of the RockYou breach corpus, NT-hashed offline - The SecLists Passwords/Common-Credentials and Passwords/Default-Credentials collections - An organization-supplied custom wordlist (company name, product names, location names, seasonal variants) - Hashcat rule transformations applied to the above (capitalization, leet substitution, year suffix) to catch trivially mutated passwords Any privileged account whose NT hash matches any entry in any of these corpora is flagged. The check also reports accounts with pwdLastSet older than 365 days, accounts with DONT_EXPIRE_PASSWORD set, accounts with userAccountControl bit ENCRYPTED_TEXT_PWD_ALLOWED (reversible encryption), and accounts whose UseDESKeyOnly or DONT_REQUIRE_PREAUTH bits expose them to Kerberos pre-auth roasting (AS-REP roasting cross-reference to ADKERB).

Why it matters

Password strength on privileged accounts is the lower bound of an environments resistance to credential attacks. Every other control (Tiering, ESAE, PAW, jump hosts, conditional access, MFA on RDP) presumes that the underlying secret cannot be guessed or cracked. Once a Domain Admin password matches a public corpus the entire defensive stack collapses: an attacker with a single authenticated foothold runs a password spray (3 attempts per account per lockout window to stay under the threshold), hits a privileged hit on the first or second wave, and authenticates directly via LDAPS, SMB, or WinRM to a domain controller. The blast radius is the entire forest. Offline cracking is even more permissive: an attacker who can extract NTDS.dit (via VSS, NTDSUtil IFM, replication from a stale DC, or a Backup Operators membership) can crack an 8-character mixed-case alphanumeric NT hash on a single consumer GPU in under one hour, and cracks all combinations under 9 characters within a few days. Microsoft, NIST SP 800-63B, and CISA all recommend a minimum of 14 characters for general users and substantially longer for privileged accounts (Guerrilla recommends 25+ characters via Fine-Grained Password Policy targeted at the privileged groups), explicitly because the entire post-2017 password guidance is that length defeats brute force and dictionary checks defeat password reuse. Standing privileged accounts with weak passwords are the single most common root cause in publicly reported AD ransomware incidents.

Attack path

Step 1: Foothold. The attacker obtains any authenticated identity in the domain. This can be a phishing-derived workstation user, a compromised vendor account, a guest credential, or even an unauthenticated session if SMB null binds are permitted. No special rights are required. Step 2: Privileged enumeration. The attacker runs net group "Domain Admins" /domain, Get-ADGroupMember "Domain Admins", BloodHound (highlights the DOMAIN ADMINS@DOMAIN node and inbound edges), or PowerView Get-NetGroupMember. All members are sAMAccountNames, all of which are visible to any authenticated principal by default. Step 3: Password spray. The attacker runs a low-and-slow spray with one or two guesses per account per lockout window (typically 30 minutes). Tools: Rubeus brute (Kerberos AS-REQ, does not increment badPwdCount on the targeted DC in some cases), kerbrute passwordspray, DomainPasswordSpray.ps1, or MailSniper (against Exchange OWA/EWS for hybrid identities). The first wave uses seasonal patterns (Summer2025!, Winter2026!), the second wave uses company-specific guesses, the third wave uses leaked credentials from public breach corpora correlated to the targets email domain. Step 4: Offline cracking (alternative path). If the attacker reaches Backup Operators, holds DCSync rights, or compromises a DC, they extract NT hashes via secretsdump.py (Impacket), Mimikatz lsadump::dcsync, or DSInternals Get-ADReplAccount. Hashes are loaded into Hashcat with mode 1000 (NTLM) and the HaveIBeenPwned hash list, RockYou, and a Hashcat rule set (best64.rule, OneRuleToRuleThemAll). On a single RTX 4090, Hashcat reaches roughly 100 gigahashes per second against NTLM, which exhausts all 8-character mixed alphanumeric passwords in under one hour. Step 5: Lateral. The recovered cleartext password (or for pass-the-hash, the raw NT hash) is replayed via Evil-WinRM, PsExec, Rubeus asktgt, or impacket-wmiexec to authenticate to a Tier 0 host as the compromised Domain Admin. Step 6: Domain dominance. From Tier 0, the attacker runs DCSync against krbtgt, mints a Golden Ticket (Rubeus golden), and establishes durable persistence. Average elapsed time from initial foothold to Domain Admin in environments with one weak privileged password is under 4 hours.

How Guerrilla assesses it

Guerrilla resolves the privileged principal set by enumerating well-known RIDs against the domain SID. For each domain in the forest it queries (objectSid=<domainSid>-512), (objectSid=<domainSid>-519), (objectSid=<domainSid>-518), and the local Administrators alias (S-1-5-32-544), then expands the member attribute recursively via Get-ADGroupMember -Recursive. It supplements with (adminCount=1) to catch SDProp-stamped principals that may have been removed from a protected group without resetting the bit. For the hash-strength evaluation, Guerrilla uses the DSInternals module (Michael Grafnetter) Get-ADReplAccount cmdlet to invoke MS-DRSR GetNCChanges against a writable DC. This requires Replicating Directory Changes (DS-Replication-Get-Changes, 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2) and Replicating Directory Changes All (DS-Replication-Get-Changes-All, 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2) rights on the domain naming context, which is itself a Tier 0 right and should be exercised only by an authorized operator from a Privileged Access Workstation. The returned NT hash for each privileged account is compared against the local HaveIBeenPwned NTLM hash corpus using Test-PasswordQuality (also from DSInternals). The cmdlet returns WeakPassword, DuplicatePasswordGroups (accounts sharing the same hash), DefaultPassword, and PasswordNotRequired collections. Guerrilla also computes Levenshtein distance between hash matches and known seasonal patterns to flag mutated reuse. The check intentionally never exfiltrates, transmits, persists, or logs cleartext or hashes; all comparisons are performed in-memory on the auditor workstation and only the boolean result (account name, weak/strong) is written to the Guerrilla report.

Recommended value

All privileged account passwords meet a minimum of 25 characters and are not found in common password dictionaries

Remediation

Test privileged account password strength by comparing NT hashes against known weak password lists (using tools like DSInternals). Force immediate password changes on any accounts with weak passwords. Implement FGPP requiring 25+ character passwords for privileged accounts

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.

Verdict scenarios for ADPRIV-016
ScenarioExpected verdict
cleanPASS
known-badFAIL
throttledNot Assessed

Framework mappings

NIST SP 800-53
IA-5(1)
ANSSI
R37
CIS AD Benchmark
4.5.1
MITRE ATT&CK
T1110.001, T1110.003, T1078.002