Risky userAccountControl flags: finding and fixing them before an attacker does
The userAccountControl attribute packs dozens of account settings into a single integer. Some bits, set once to fix an application or inherited from a provisioning script, weaken security for years without anyone remembering why.
One attribute, many behaviours
Every user and computer account carries a userAccountControl attribute in which each bit turns on a behaviour: account disabled, password never expires, account trusted for delegation, and so on. Management consoles only expose some of these options, and some can only be changed by script or the attribute editor. That is exactly why they slip through reviews.
Flags to watch
| Flag | Value | Risk |
|---|---|---|
| PASSWD_NOTREQD | 0x20 | The account may have an empty password, regardless of password policy |
| ENCRYPTED_TEXT_PWD_ALLOWED | 0x80 | The password is stored with reversible encryption, so anyone with access to the directory database can recover it in clear text |
| DONT_EXPIRE_PASSWORD | 0x10000 | The password never expires; acceptable with a long unique password, dangerous on a shared or old account |
| TRUSTED_FOR_DELEGATION | 0x80000 | Unconstrained delegation: the account receives and keeps the TGTs of users who connect to it |
| USE_DES_KEY_ONLY | 0x200000 | Restricts Kerberos to DES, a broken algorithm disabled by default |
| DONT_REQ_PREAUTH | 0x400000 | No Kerberos pre-authentication: a reply encrypted with the account's key can be obtained without a password and attacked offline |
Finding them
An LDAP query using the bitwise matching rule 1.2.840.113556.1.4.803 tests a specific bit. For example, the filter (userAccountControl:1.2.840.113556.1.4.803:=32) returns accounts with PASSWD_NOTREQD; replace 32 with the decimal value of the other flags. The Active Directory PowerShell module also exposes direct properties such as PasswordNotRequired, TrustedForDelegation or DoesNotRequirePreAuth. Include disabled accounts: they can be re-enabled, and their configuration often reveals practices still in use.
Fixing them without breaking things
- PASSWD_NOTREQD: remove the flag, then check that the account really has a compliant password. This bit is often set by provisioning tools or old scripts, so fix the source too.
- Reversible encryption: removing the flag is not enough, as the stored password remains recoverable until the next change. Force a password change. Also check that the equivalent setting is not enabled in the domain password policy or in a fine-grained password policy.
- DES: remove the flag and make sure the account has AES keys, which may require a password change.
- Pre-authentication disabled: turn it back on. Applications that need it disabled are extremely rare today.
- Unconstrained delegation: legitimate on domain controllers only. Elsewhere, move to constrained or resource-based delegation, and mark privileged accounts as "sensitive and cannot be delegated".
- Password never expires: acceptable for a gMSA or an account with a long, managed password; justify it case by case for the others.
Watch for regressions. These flags often come back after a fix, through a provisioning tool or a hurried administrator. Changes to userAccountControl are logged on controllers (event 4738): alert when the most sensitive bits are added.
What an audit checks
- the list of enabled and disabled accounts carrying each risky flag;
- privileged accounts affected, handled first;
- the domain password policy and fine-grained policies allowing reversible encryption;
- non-controller accounts trusted for unconstrained delegation.