The Active Directory Cleartext Password Reversible Encryption Problem
Active directory cleartext password reversible encryption exposure is one of the few misconfigurations that skips the usual attacker workflow entirely — no hash to crack, no ticket to relay, just a literal password sitting in the directory, unlike a stolen NT hash reused via Pass-the-Hash. Active Directory is designed to never store a usable copy of a password: every unicodePwd value is one-way hashed (NT hash, and Kerberos long-term keys) before it reaches the database, and the attribute itself cannot be read back over LDAP. Microsoft's MS-SAMR protocol documentation is explicit about the one case that breaks this guarantee: storage of the cleartext password for an object is configured only when the account's Effective-PasswordReversibleEncryptionEnabled value is set, or when its userAccountControl attribute contains the ENCRYPTED_TEXT_PWD_ALLOWED (0x0080) bit — after which the next password set or change operation writes a decryptable copy into supplementalCredentials.
This article covers two distinct, attribute-level failure modes that both end with an attacker holding a usable plaintext credential straight out of the directory:
- The legacy
userPasswordLDAP attribute, which some environments populate directly with a literal string. - The reversible-encryption flag on
userAccountControl, which tells every domain controller to keep a decryptable copy of the password alongside the normal hashes.
Both are flagged Critical in etc-collector's audit catalogue (PASSWORD_CLEARTEXT_STORAGE, REVERSIBLE_ENCRYPTION) because compromising either one gives an attacker the literal password, not just a hash to crack or relay. That distinction matters operationally: a cracked hash or a relayed session can be contained by rotating a credential and reviewing what it touched, but a recovered plaintext password is often reused elsewhere — personal accounts, other systems, other domains — so the blast radius of either exposure extends well past the AD object where it was found.
⚠️ Warning: this is a different mechanism from WDigest cleartext credentials (which exposes passwords in LSASS memory on a compromised host) and from passwords left in AD description fields (free-text metadata, not a password-storage attribute). Both mechanisms below live in the directory's own password-handling logic, not in a side channel.
How It Works
The userPassword attribute
userPassword is a standard LDAP attribute (defined outside Microsoft's schema, in RFC 4519-derived directory standards) that Active Directory also recognizes under specific conditions. Per MS-ADTS §3.1.1.3.1.5.2, AD will let clients write and use userPassword to set an account's password only when:
- the DC is running as AD LDS, or
- the DC is AD DS, the domain functional level is Windows Server 2003 or higher, and
- the
fUserPwdSupportheuristic in thedSHeuristicsattribute is enabled.
When that heuristic is off (the default for AD DS), userPassword is just an ordinary, unindexed attribute with no special semantics — which is exactly the trap: many admins and provisioning scripts (LDAP migration tools, HR sync jobs, legacy Unix/LDAP integrations) still write a literal password string into userPassword out of habit or interoperability need. Unlike unicodePwd, this attribute is readable by anyone with generic read rights on the object, so a plain (userPassword=*) LDAP search can dump every cleartext value that was ever written there — no privilege escalation required, no hash cracking, nothing to relay.
ldapsearch -x -H ldap://dc01.corp.local -D "[email protected]" -W \
-b "DC=corp,DC=local" "(userPassword=*)" sAMAccountName userPassword
The reversible-encryption UAC flag
The second mechanism is the ENCRYPTED_TEXT_PWD_ALLOWED bit (decimal 128 / hex 0x0080) inside userAccountControl, exposed on the user object as the ms-DS-User-Encrypted-Text-Password-Allowed attribute and, in the AD PowerShell module, as the AllowReversiblePasswordEncryption account property. Setting it does not itself write a cleartext password — it changes what happens on the next password set or change: AD stores the new password in supplementalCredentials in a form that can be decrypted with the domain's own encryption key, in addition to the normal hashes.
This flag exists to support legacy authentication protocols that need the actual password to compute a challenge-response, most commonly CHAP for RADIUS/IAS dial-up and VPN, and older IIS Digest Authentication deployments. Microsoft's own security-policy documentation is blunt about the trade-off: enabling it "is essentially the same as storing plaintext versions of the passwords," and it should never be turned on domain-wide unless an application genuinely requires it.
If a domain needs this for a small set of legacy service accounts, scoping it narrowly matters: a Fine-Grained Password Policy (Password Settings Object) can set msDS-PasswordReversibleEncryptionEnabled on just the OU or group that needs it, instead of flipping the "Store passwords using reversible encryption" setting in the Default Domain Policy for every account in the domain.
🚨 Danger: MITRE ATT&CK T1556.005 documents this as an active adversary technique, not just a legacy misconfiguration: with enough rights, an attacker can run
Set-ADUser -AllowReversiblePasswordEncryption $trueagainst a target account, wait for (or force) a password reset, and then recover the plaintext from the account's next stored credential — turning a single write permission into a standing credential-harvesting backdoor that survives password rotation.
Why this still shows up in modern environments
Neither mechanism is a relic that only affects Windows 2000-era domains. userPassword reappears whenever an organization runs identity synchronization tooling that was originally built for OpenLDAP or another RFC-4519 directory and gets pointed at Active Directory without adjustment — the sync job writes the field it was built to write, and nothing in AD stops it if dSHeuristics happens to allow it. Reversible encryption reappears the same way: a RADIUS appliance, a legacy VPN concentrator, or an old intranet application using IIS Digest Authentication gets configured once, the domain-wide policy gets flipped to make it work, and the setting quietly outlives the application that needed it — often for years, across every account in the domain, long after anyone remembers why it's there.
Both cases share the same root cause: the setting is a domain-wide or object-wide switch, but the actual need is almost always narrow — one application, one integration, one legacy protocol. Nothing forces a review when the dependency goes away, so the exposure persists as pure technical debt until an audit, or an attacker, finds it. It belongs on the same watchlist as the most common AD security misconfigurations that survive years past their original justification.
Detection
| Indicator | Event ID | Source | Description |
|---|---|---|---|
userPassword attribute populated | — | LDAP query | (userPassword=*) returns any object; the attribute is plainly readable, no privilege escalation needed |
dSHeuristics allows userPassword writes | — | LDAP query on Configuration NC | fUserPwdSupport character in CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,... is not 0/2 |
userAccountControl has bit 128 set | 4738 | Security event log (DC) | "User Account Control" changes list Encrypted Text Password Allowed; also fires 4720 on new-account creation with the flag pre-set |
Account has AllowReversiblePasswordEncryption = True | — | AD PowerShell / LDAP bitwise filter | Standing exposure, independent of when the flag was set |
# Accounts with reversible encryption currently enabled
Get-ADUser -Filter {AllowReversiblePasswordEncryption -eq $true} `
-Properties AllowReversiblePasswordEncryption, PasswordLastSet |
Select-Object SamAccountName, PasswordLastSet
# Equivalent LDAP bitwise-AND filter (matching-rule OID 1.2.840.113556.1.4.803)
Get-ADUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=128)" `
-Properties userAccountControl | Select-Object SamAccountName
💡 Tip: Get-ADUser -Filter {AllowReversiblePasswordEncryption -eq $true} only shows the current state. Alert on Event ID 4738 too, so a flag flip on a privileged account is caught the moment it happens — not weeks later during the next audit cycle.
For userPassword, since it is a generic attribute AD does not index or specially protect by default, a scheduled LDAP sweep for (userPassword=*) across the domain is the only reliable way to catch it; there is no dedicated security event for writing to this attribute, so periodic re-auditing matters as much as the initial sweep.
Remediation
💡 Quick Win: disable "Store passwords using reversible encryption" in the Default Domain Policy unless you can name the specific legacy application requiring it.
- Audit before you touch anything. Run both queries above domain-wide and record every hit — flipping the flag or clearing
userPasswordforces a password change for that account, so plan the rotation with the account owners first. - Disable reversible encryption domain-wide via Group Policy (Computer Configuration > Policies > Windows Settings > Security Settings > Account Policies > Password Policy > "Store passwords using reversible encryption" = Disabled), unless a specific legacy CHAP/RADIUS or IIS Digest Authentication dependency exists.
- If a legitimate dependency exists, scope it with a Fine-Grained Password Policy (
msDS-PasswordReversibleEncryptionEnabled) applied only to the OU or group running that application — never domain-wide. - Clear any
userPasswordvalues found, and setfUserPwdSupportback to disabled indSHeuristicsunless the DC is intentionally running AD LDS or a documented integration needs it. - Force a password reset for every affected account. Disabling the flag or clearing the attribute does not retroactively remove credential material already written to
supplementalCredentials— only the next password change does. - Lock down write access to
userAccountControlanduserPasswordfor standard users; both should only be modifiable by tier-0 identity administration, and any GenericAll/GenericWrite grant that reaches them deserves the same scrutiny as a path to Domain Admin. - Wire Event ID 4738 into your SIEM with an alert on
Encrypted Text Password Allowedappearing in the changed-attributes list, scoped at minimum to privileged and service accounts, and re-run the LDAP sweeps on a recurring schedule rather than treating this as a one-time cleanup. - Document any surviving exception. If a legacy application genuinely still needs reversible encryption, record the owner, the review date, and the decommission plan — an undocumented exception is indistinguishable from an unnoticed one during the next audit.
How EtcSec Detects This
EtcSec's audit catalogue tracks both mechanisms as separate, Critical-severity checks: PASSWORD_CLEARTEXT_STORAGE flags any object where userPassword holds a value, and REVERSIBLE_ENCRYPTION flags any account with AllowReversiblePasswordEncryption (the ENCRYPTED_TEXT_PWD_ALLOWED UAC bit) enabled. Both sit in the Password category alongside related checks like password policy compliance and GPO SYSVOL cPassword secrets — together they cover the handful of ways a directory can end up holding a password an attacker doesn't even need to crack.
ℹ️ Note: EtcSec automatically checks for this vulnerability during every AD audit. Run a free audit to verify your environment doesn't have either flag exposed.
Explore the identity security pages that support this topic
