AD-ACC-003: Privileged accounts whose password never expires¶
medium Accounts MITRE ATT&CK T1078.002 Reads: LDAP
Enabled members of tier-0 groups have 'password never expires' set, so a leaked admin password stays valid indefinitely.
Why it matters¶
DONT_EXPIRE_PASSWORD (userAccountControl bit 0x10000) exempts an account from the
domain's maximum password age. On an administrator that means the password chosen years
ago, possibly typed into scripts, scheduled tasks and jump hosts since then, is still
the password today.
Admin credentials leak in many ways: a cached logon on a compromised workstation, a script in a share, a cracked Kerberos ticket (AD-KRB-003), an old backup. Without rotation, a password obtained once keeps working as Domain Admin until someone notices.
How ad-doctor detects it¶
From the LDAP snapshot: every enabled user (computers and managed service accounts
excluded) that is a transitive member of a tier-0 group and has DONT_EXPIRE_PASSWORD
(0x10000) set.
The tier-0 groups are Domain Admins, Schema Admins, Enterprise Admins, Administrators,
Account Operators, Server Operators, Print Operators and Backup Operators. Membership
through primaryGroupID counts, since it never shows in member.
Each object is the account's sAMAccountName, for example adm_bob. The built-in
Administrator is reported like any other member when it has the flag.
Attributes and probes read
userAccountControlmember (transitive) of the tier-0 groupsprimaryGroupID
Severity¶
Always medium. The flag does not open a path by itself; it removes the forced rotation that would eventually invalidate a stolen tier-0 password. Old passwords are reported separately by AD-ACC-011.
False positives and limits¶
- Break-glass accounts whose long random password is kept in a safe and rotated by a procedure, not by expiry. Record them in an accepted-risk file with an owner and an expiry date.
- Smart-card-only admins (
SMARTCARD_REQUIRED) in a domain that rolls their NTLM secret on expiry are still reported; ad-doctor does not look at that setting.
Fix¶
-
List the privileged accounts with the flag (read-only;
adminCount = 1is a close approximation of tier-0 membership): -
Rotate the password first, then remove the flag. If the password is already older than the maximum password age, removing the flag makes it expire immediately.
-
Give administrators their own password policy if the domain policy is too weak or has no maximum age:
-
Take service accounts out of tier-0 groups. A service that needs a non-expiring password should run as a group managed service account, not as a Domain Admin.
Verify¶
To accept the risk instead, add an entry to the file passed with --accept (see accepted risks):
[[accept]]
check = "AD-ACC-003"
objects = ["..."] # optional; without it the whole check is accepted
reason = "..."
expires = 2027-03-31
Related checks¶
References¶
Microsoft and vendors
- Maximum password age
- Attractive accounts for credential theft
- ANSSI Active Directory checklist (vuln_dont_expire_priv)
MITRE ATT&CK
Available since 0.1.0.