AdminSDHolder. SDProp. The Protected Users group. These are Active Directory's built-in security controls for privileged accounts - and the ones I see most frequently misconfigured, occasionally abused, and almost always misunderstood. They're supposed to protect your Domain Admins, Enterprise Admins, and other Tier 0 accounts. Misunderstood, they leave gaps - and in the wrong hands, AdminSDHolder becomes a persistence mechanism.

Why this matters: These mechanisms work quietly in the background. You don't see SDProp rewriting ACLs every hour, and you don't see which accounts it has stopped touching. That invisibility is exactly what attackers rely on.

What AdminSDHolder Actually Does

AdminSDHolder is an object in every domain (CN=AdminSDHolder,CN=System,...) whose ACL acts as the security template for privileged accounts and groups. A background process called SDProp (Security Descriptor Propagator), running on the PDC emulator, applies that template to every protected object: the protected groups themselves and all of their members, including nested members. The protected groups include Domain Admins, Enterprise Admins, Schema Admins, Administrators, Account Operators, Server Operators, Print Operators, Backup Operators, Replicator, Domain Controllers, Read-only Domain Controllers, Key Admins and Enterprise Key Admins.

When SDProp processes a protected object, it:

SDProp runs every 60 minutes by default (configurable via AdminSDProtectFrequency, though there's rarely a good reason to change it). This is why permission changes you make directly on a Domain Admin account keep disappearing - SDProp puts the template back within the hour.

# Check an account's adminCount Get-ADUser jdoe -Properties adminCount | Select-Object Name, adminCount # View AdminSDHolder's ACL Import-Module ActiveDirectory $dn = "CN=AdminSDHolder,CN=System,$((Get-ADDomain).DistinguishedName)" (Get-Acl "AD:$dn").Access | Select-Object IdentityReference, ActiveDirectoryRights, AccessControlType, ObjectType

The Misconfiguration Trap: Orphaned adminCount=1 Accounts

Here's the scenario I see constantly:

To be clear about what this does and doesn't mean: the account does not keep Domain Admin rights - those came from the group membership, which is gone. What it keeps is a frozen security descriptor. That causes real problems:

Finding them means comparing adminCount=1 against current recursive membership of the protected groups:

# Accounts marked adminCount=1 that are no longer in any protected group $protectedGroups = 'Domain Admins','Enterprise Admins','Schema Admins','Administrators', 'Account Operators','Server Operators','Print Operators','Backup Operators','Replicator', 'Domain Controllers','Read-only Domain Controllers','Key Admins','Enterprise Key Admins' $currentMembers = foreach ($g in $protectedGroups) { try { Get-ADGroupMember $g -Recursive -ErrorAction Stop | Select-Object -ExpandProperty DistinguishedName } catch { } # Enterprise/Schema Admins only exist in the forest root domain } $currentMembers = $currentMembers | Sort-Object -Unique Get-ADUser -Filter 'adminCount -eq 1' -Properties adminCount, Enabled, PasswordLastSet | Where-Object { $_.DistinguishedName -notin $currentMembers -and $_.SamAccountName -ne 'krbtgt' } | Select-Object Name, SamAccountName, Enabled, PasswordLastSet

(krbtgt is excluded because it's protected by design.)

Protected Users Group - What It Actually Does

The Protected Users group was introduced with Windows Server 2012 R2. It isn't a permission - it's a set of hardening restrictions applied to its members' authentication. Members get:

Those restrictions are exactly what makes it valuable for admin accounts - and exactly what breaks things if you add the wrong accounts without testing:

# Who is in Protected Users? Get-ADGroupMember 'Protected Users' -Recursive | Select-Object Name, SamAccountName, objectClass # Which Domain Admins are NOT in Protected Users? $pu = (Get-ADGroupMember 'Protected Users' -Recursive).SamAccountName Get-ADGroupMember 'Domain Admins' -Recursive | Where-Object { $_.objectClass -eq 'user' -and $_.SamAccountName -notin $pu } | Select-Object Name, SamAccountName

The second query is the useful one: every human Tier 0 admin account should be in Protected Users. Your break-glass account is the usual exception, so it can still log on if Kerberos is broken.

Know its limits: Protected Users makes privileged credentials much harder to steal and reuse. It doesn't stop an attacker who already has Domain Admin, and it doesn't stop a forged Golden Ticket. It's one layer, alongside tiering and PAWs - not a replacement for them.

The Abuse Scenario: AdminSDHolder as a Backdoor

Here's how attackers abuse AdminSDHolder in real incidents:

This is a classic "backdoor through the mechanism." Removing the attacker from groups doesn't help, because the permission lives on AdminSDHolder - and SDProp keeps re-applying it for them.

The good news: SDProp works for you once you fix the source. Remove the rogue ACE from AdminSDHolder and the next SDProp run overwrites the copies on every protected object.

My Detection and Remediation Approach

Here's my standard checklist when reviewing AdminSDHolder and Protected Users in an assessment:

1. Audit AdminSDHolder's ACL

# Who has write-type rights on AdminSDHolder? $dn = "CN=AdminSDHolder,CN=System,$((Get-ADDomain).DistinguishedName)" (Get-Acl "AD:$dn").Access | Where-Object { $_.AccessControlType -eq 'Allow' -and $_.ActiveDirectoryRights -match 'GenericAll|GenericWrite|WriteDacl|WriteOwner|WriteProperty' } | Select-Object IdentityReference, ActiveDirectoryRights, ObjectType

Expect SYSTEM, Domain Admins, Enterprise Admins and Administrators, plus a few built-in groups with narrow property writes (such as Cert Publishers). Anything else - especially a named user or an obscure group - needs an explanation. The best reference is the same query run against a clean lab domain at the same functional level.

If you prefer dsacls, remember it prints friendly names like "FULL CONTROL", "WRITE PERMISSIONS" and "WRITE OWNER" - searching its output for GenericAll or WriteDacl finds nothing.

2. Find Orphaned adminCount=1 Accounts

Use the script from earlier in this post. Disable anything that's no longer needed, and clean up the rest (see "The Fix" below).

3. Review Protected Users Membership

Every human Tier 0 admin should be in it; no service or computer accounts should be. Use the queries in the Protected Users section.

4. Monitor for Changes

The Fix: Cleaning Up an Account Properly

When an account leaves a protected group, removing the membership isn't enough. You also need to clear adminCount and turn inheritance back on:

# 1. Remove from the protected group Remove-ADGroupMember 'Domain Admins' -Members 'svc-app' -Confirm:$false # 2. Clear adminCount Set-ADUser 'svc-app' -Clear adminCount # 3. Re-enable permission inheritance on the account $user = Get-ADUser 'svc-app' $path = "AD:$($user.DistinguishedName)" $acl = Get-Acl $path $acl.SetAccessRuleProtection($false, $true) # stop protecting; keep existing explicit ACEs for review Set-Acl -Path $path -AclObject $acl # Equivalent with dsacls: # dsacls "CN=svc-app,OU=Service Accounts,DC=corp,DC=local" /P:N

Then review the explicit ACEs left over from AdminSDHolder and remove any that don't belong - and reset the account's password, since it was privileged while it held that membership.

Never "fix" an ACL by granting broad rights. A command like dsacls <DN> /G "Everyone:GA" grants every user full control of the account - which lets anyone reset its password and take it over. The goal is to restore inheritance, not to add permissions.

Final Thought

AdminSDHolder and Protected Users are powerful security features - if you understand how they actually work. Most organizations never look at them after the domain is built. They need ongoing management like any other access control.

My rule of thumb: if you add an account to a protected group, you need a process for removing it cleanly. AD doesn't clear adminCount or restore inheritance when someone leaves a protected group. You have to do it.

And if you're rolling out Protected Users, test first. Pilot it with a few admin accounts, check for NTLM-only and delegation-dependent tools, and make sure nobody relies on offline logons with those accounts. Then add the rest of your Tier 0 admins.