🏢Active DirectoryAccountsPrivileged AccessGroups

Active Directory Disabled Expired Locked Accounts Privileged Groups: Why Admin Rights Persist

A disabled admin account is a missed offboarding step. A locked one can mean an active password-spray attack. Here's how to find both, plus the orphaned adminCount residue between them.

Younes AZABARBy Younes AZABAR9 min read
Active Directory Disabled Expired Locked Accounts Privileged Groups: Why Admin Rights Persist

Active Directory disabled expired locked accounts privileged groups is the exact gap this article covers: an account whose own state — disabled, expired, or locked out — says it should not have access, yet it is still formally a member of Domain Admins, Enterprise Admins, or another Tier 0 group. A disabled admin account is almost always a missed offboarding step. An expired one is a scheduled access grant nobody revoked from the group when the clock ran out. A locked one can just be a helpdesk ticket — or it can be the tail end of an active password-spray attack still in progress. This article covers all three states, plus their close cousin, the orphaned adminCount flag, and how to find and fix them.

ℹ️

ℹ️ Note: this is a different problem from stale privileged accounts (privilege that nobody has used in a long time, judged on last-logon age) and from privileged access drift (privilege that creeps back in over time through nesting and ACLs). Here the account itself is in an outright invalid state — disabled, expired, or currently locked out — while still being a formal member of a Tier 0 group.

Active Directory Disabled Expired Locked Accounts Privileged Groups: The Four States

Each of these four conditions has a precise, attribute-level definition, and each has its own way of quietly ending up inside a privileged group without anyone deciding it should.

StateAD attributeTypical causePrimary risk if ignored
DisableduserAccountControl bit ACCOUNTDISABLE (0x2)Offboarding stopped at "disable," never removed from the groupFull privilege returns instantly if the account is re-enabled
ExpiredaccountExpires (FILETIME in the past)Time-boxed access; expiration set instead of a group removalMembership persists silently past the intended end date
Locked outlockoutTime / msDS-User-Account-Control-ComputedFailed logon attempts — a mistyped password or an active attackCan be the tail of an in-progress password-spray run
adminCount orphanedadminCount=1 with no current protected membershipSDProp does not revert the flag when membership is removedStale ACL and audit noise that mask where privilege actually is

The Disabled Account

The ACCOUNTDISABLE bit (0x2) is set in userAccountControl; the account cannot interactively log on. (Microsoft Learn: UserAccountControl property flags) Disabled accounts accumulate because offboarding checklists usually stop at "disable the account." That blocks logon, but it does nothing to group membership — the SID stays in Domain Admins. Any attack-path tool that maps "who could become a domain admin," BloodHound, PingCastle, or EtcSec included, still counts it, because those graphs are built on group membership, not userAccountControl. If the account is ever re-enabled — by mistake, by a delayed ITSM ticket, or by an attacker who has obtained rights to flip one bit — its old privilege is live again immediately, with no further work required.

The Expired Account

accountExpires holds a value other than 0 or the "never expires" sentinel 9223372036854775807, and that value (a Windows FILETIME) is in the past. (Microsoft Learn: Account-Expires attribute) This attribute is frequently used for contractor or temporary-elevation accounts with a planned end date. The KDC refuses to issue a ticket-granting ticket past that date, so the account stops working for interactive or network logon — but accountExpires does not touch group membership either. If the actual intent was "this person's admin access ends on this date," setting an expiration date without also removing the group membership only blocks the front door.

The Locked-Out Account

lockoutTime is non-zero and the account's lockout duration has not yet elapsed. Windows never sets a UF_LOCKOUT bit in the stored userAccountControl value itself; the live locked/unlocked state is exposed through the constructed attribute msDS-User-Account-Control-Computed. (MS-ADLS: lockoutTime, MS-ADTS: msDS-User-Account-Control-Computed) Unlike disable or expire, lockout is not an administrative decision — it is a side effect of failed logon attempts hitting the account lockout policy. A privileged account can be locked because someone genuinely mistyped a password, or because it is currently being targeted: a password-spray or brute-force run that reaches a privileged account and is about to succeed can produce the exact same signature just before it authenticates.

The Orphaned adminCount Flag

adminCount=1 marks members of AdminSDHolder-protected groups, but an account can carry it while no longer belonging to any protected group — the inverse case: not live privilege with a dead account, but a dead marker left over from privilege that has already been removed. The mechanism behind it is the SDProp process, which runs on the domain controller holding the PDC Emulator role (by default about once an hour), walks the membership of protected groups, sets adminCount=1 on each member, copies the AdminSDHolder ACL onto the object, and disables ACL inheritance. (Microsoft Community Hub: Five common questions about AdminSDHolder and SDProp) When the account is later removed from the protected group, SDProp simply stops touching it — it does not revert adminCount to 0 or restore inheritance. The flag and the disconnected ACL are left behind as a silent, permanent-looking marker of privilege the account no longer has.

Detection

Attributes and PowerShell Queries

Direct membership queries with the AD PowerShell module surface all four conditions quickly:

# Disabled accounts still in a privileged group
Get-ADGroupMember -Identity "Domain Admins" -Recursive |
    Get-ADUser -Properties Enabled, userAccountControl |
    Where-Object { -not $_.Enabled }

# Expired accounts (accountExpires set and in the past — 0 and the max
# int64 sentinel both mean "never expires")
Get-ADGroupMember -Identity "Domain Admins" -Recursive |
    Get-ADUser -Properties accountExpires |
    Where-Object {
        $_.accountExpires -gt 0 -and
        $_.accountExpires -lt 9223372036854775807 -and
        [datetime]::FromFileTime($_.accountExpires) -lt (Get-Date)
    }

# Currently locked-out accounts, cross-referenced against privileged groups
Search-ADAccount -LockedOut -UsersOnly |
    Where-Object { (Get-ADPrincipalGroupMembership $_.SamAccountName).Name -contains "Domain Admins" }

# adminCount=1 with no current membership in a protected group (orphaned flag)
Get-ADUser -LDAPFilter "(adminCount=1)" -Properties adminCount, memberOf, Enabled

LDAP Filter for Raw Bind Tooling

For tooling that does not go through the AD PowerShell module, the disabled-bit check uses the LDAP_MATCHING_RULE_BIT_AND OID against userAccountControl:

(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=2))

This matches AD's own bitwise-AND semantics: the clause is satisfied only if the second bit (0x2, ACCOUNTDISABLE) is set on the stored value. (MS-ADTS: LDAP_MATCHING_RULE_BIT_AND)

Windows Event IDs to Correlate

These events are logged on domain controllers under Account Management auditing — see the full Active Directory monitoring event ID reference for the broader logging setup:

Event IDTriggerWhat to correlate
4725A user account was disabledWas it still a member of a privileged group at the time?
4740A user account was locked outCaller computer/workstation name — a spray pattern shows as many different accounts locking from the same source in a short window
4767A user account was unlockedWho performed the unlock, and how quickly after the 4740
4738A user account was changedCovers accountExpires changes — an expiration date pushed out or cleared on a privileged account
4728Member added to a security-enabled global groupDomain Admins is a global group; this is the event for new members
4756Member added to a security-enabled universal groupEnterprise Admins and Schema Admins are universal groups, scoped forest-wide

Sources: Event ID 4725, Event ID 4740, Event ID 4767, Event ID 4738, Event ID 4728, Event ID 4756.

⚠️

⚠️ Warning: an unusual number of lockouts across many accounts in a short window — privileged or not — is a documented password-spray signature, not routine noise. Treat a lockout landing specifically on a Tier 0 account as a higher-priority alert than the same event on a standard user.

Remediation

💡

💡 Quick Win: run the four queries above against every AdminSDHolder-protected group today. This is a five-minute check that most environments have never run on a schedule. For the wider recurring process this should fit into, see how to audit Active Directory security.

For Disabled and Expired Accounts

  1. Make the check recurring, not just part of offboarding. A leaver process that disables the account when someone leaves will not catch an expiration date that already lapsed six months ago. Schedule the queries above, or an equivalent SIEM/EASM rule, to run regularly.
  2. Tie offboarding to group removal, not just account disable. Disabling blocks logon; it does not revoke the group membership that attack-path graphs, delegated rights, and some application authorization logic still read. When access should end, remove the account from the privileged group explicitly.
  3. Treat expiration dates as a trigger for membership review, not a substitute for it. If an account was time-boxed into a privileged group, set a corresponding reminder to remove the membership on or before the expiration date — do not rely on accountExpires alone to represent "this person is no longer an admin."

For Locked-Out Privileged Accounts

Because unlocking an account is a cheap, fast, delegable operation — a distinct AD permission from resetting the password — a locked-out Tier 0 account is one unlock away from being fully usable again. Route Event ID 4740 on members of protected groups to its own alert path, separate from routine helpdesk lockout noise, and confirm the unlock (Event ID 4767) came from an expected administrator before treating the incident as closed.

For Orphaned adminCount Flags

For each account with adminCount=1 but no current membership in a protected group, confirm it is genuinely no longer privileged — check nested memberships, not just direct ones, since a removal from a directly-assigned group can still leave indirect privilege in place. Once confirmed clean, reset adminCount and re-enable ACL inheritance rather than leaving the stale flag and disconnected ACL in place; leaving it muddies every future permissions audit on that object.

How EtcSec Detects This

EtcSec checks for all four conditions on every Active Directory audit: DISABLED_ACCOUNT_IN_ADMIN_GROUP and EXPIRED_ACCOUNT_IN_ADMIN_GROUP flag privileged-group members whose own account state should have already blocked their access, LOCKED_ACCOUNT_ADMIN surfaces currently locked-out privileged accounts for the password-spray scenario above, and ADMIN_COUNT_ORPHANED catches the inverse case — the residual adminCount=1 marker left behind once a protected membership has already been removed.

ℹ️

ℹ️ Note: EtcSec automatically checks for this vulnerability cluster during every AD audit. Run a free audit to verify your environment.

Explore the identity security pages that support this topic