adminCount Drift Grants Ghost Tier-0 Rights
A helpdesk account added to Domain Admins for a migration keeps adminCount=1 after removal, so its ACL stays protected and inheritance from the OU stays blocked. Group membership reviews find nothing because the account left the group weeks ago, yet the elevated Active Directory ACL template stays put on every domain controller.
Operational summary
At a glance
- Symptom
- An account leaves Domain Admins or another protected group but the security descriptor and adminCount marker are never manually reset.
- Likely cause
- adminCount remains set to 1 and ACL inheritance stays disabled after an account is removed from a protected group.
- Impact
- The account keeps a stale, inheritance-blocked ACL that ignores later OU-level permission changes while remaining invisible to group-membership-based access reviews.
- Verification signal
- Re-run the detection test and a controlled negative-path test.
- Safe correction
- Query for adminCount=1 accounts with no current protected group membership and reset both the attribute and the inheritance flag as part of the offboarding workflow.
- Rollback or recovery
- Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
The Trap
adminCount attribute drift after removal from a protected group. Whenever an account is added to Domain Admins, Enterprise Admins, Schema Admins, Account Operators, Backup Operators, Server Operators, Print Operators, or Administrators, SDProp sets adminCount=1 on that account and copies AdminSDHolder’s access control list onto it, disabling ACL inheritance from the parent OU. Active Directory does not automatically clear adminCount or re-enable inheritance when the account is later removed from every protected group. The attribute and the protection flag persist until an administrator explicitly resets them.
The Default State
The usual sequence is a contractor or on-call engineer added temporarily to Domain Admins for a server migration or a domain controller promotion, then removed from the group once the task finishes. No follow-up job clears adminCount or restores inheritance. The account passes every routine check: dsa.msc shows a normal group membership tab, and the user object no longer appears in any privileged group listing. The residual ACL sits invisibly on the object’s security descriptor, untouched because build runbooks treat group removal as the end of the offboarding step.
The Blast Radius
The object is now a shadow Tier-0 account. Its nTSecurityDescriptor still carries the AdminSDHolder template: inheritance disabled, explicit deny entries for Account Operators, explicit allow entries tied to Domain Admins and Enterprise Admins. When the security team later tightens delegated OU permissions, for example revoking helpdesk password-reset rights across a whole OU, that new access control entry never propagates to this account because inheritance is blocked at the object level. It silently keeps whatever rights the old AdminSDHolder copy granted. In the other direction, an LDAP sweep for (adminCount=1) gives an attacker a ready-made target list of every account that has ever touched a protected group, regardless of current membership, since the flag is a permanent marker of past privilege rather than present privilege. Password policy changes, Kerberos delegation settings, and SPN hardening scripts written against OU-based inheritance quietly skip these objects because they were never designed to touch protected accounts directly.
The Lead Mechanic Fix
Run a recurring query to find drift: Get-ADUser -LDAPFilter "(&(adminCount=1)(!(memberOf=CN=Domain Admins,CN=Users,DC=corp,DC=example)))" -Properties adminCount,memberOf. For every account that matches and holds no current protected group membership, clear the marker and restore inheritance in two steps: Set-ADObject -Identity $dn -Clear adminCount followed by dsacls $dn /P:N to remove the protected flag and allow OU-level ACEs to flow down again. Tie this directly to the group-removal step in your privileged access workflow, PIM or JIT tooling, rather than leaving it as a manual afterthought that gets skipped under deadline pressure.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
Get-ADUser -LDAPFilter "(&(adminCount=1)(!(memberOf=CN=Domain Admins,CN=Users,DC=corp,DC=example)))" -Properties adminCount,memberOfSet-ADObject -Identity $dn -Clear adminCountdsacls $dn /P:NVerify, roll back or escalate
Verify
Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.
Rollback
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.