This blog post is part of a series about Active Directory attributes with values or behaviors that can be easily and inadvertently misinterpreted and misused. This series will provide information about these attributes, including both their limitations and their valid usages with respect to the administration of Active Directory.
Active Directory is the primary authentication service used by the vast majority of organizations, including more than 95% of Fortune 500 companies. Consequently, Active Directory objects with elevated administrative privileges are highly-valued targets for attack and compromise.
One of the more common methods used to identify and report on Active Directory objects that have inherited elevated administrative permissions is to monitor the value of an attribute named AdminCount. Since this post is part of a series about misunderstood Active Directory attributes, you can safely assume that the AdminCount attribute has some “gotchas” that are important to understand. So, let’s take a look at the AdminCount attribute and the underlying processes that control its behavior.
AdminCount and Protected Objects
Active Directory user, group, and computer objects possess an AdminCount attribute. The AdminCount attribute’s value defaults to <NOT SET>. Its utility comes from the fact when a user, group, or computer is added, either directly or transitively, to any of a specific set of protected groups its value is updated to 1. This can provide a relatively simple method by which objects with inherited administrative privileges may be identified.
The following table lists Active Directory’s default protected object sets, including the groups that may induce an update of the AdminCount attribute on its members:
OK, there’s a lot happening in that table. You may also have noticed that I said: “may induce an update of the AdminCount attribute”. That’s because there are a number of variables and subtle behaviors that influence membership in the protected object set. Luckily, I added some superscript annotations to the table that are going to make it a bit easier to explain what’s going on in the table and the associated implications:
1. The default protected object set varies across the different Active Directory functional levels. This results in differences in the groups that may produce an update of the AdminCount attribute on their members.
2. The default protected object set includes four groups that can be manually excluded from protection: Account Operators, Backup Operators, Print Operators, and Server Operators. This behavior was introduced in a hotfix for Windows Server 2000 and Windows Server 2003 and has persisted in subsequent releases (which may help to explain why the process itself is a little complicated). The mechanism that controls this behavior is located in an attribute named dsHeuristics on the CN=Directory Service,CN=Windows NT,CN=Services,<FOREST ROOT DN> object. The value of the dsHeuristics attribute is a Unicode string in which each character represents a single forest-wide configuration setting. The relevant character to this discussion is the 16th character, which, if it exists, represents the “dwAdminSDExMask”. Its value is interpreted as a hexadecimal character, but it’s a little easier to understand the behavior if we look at its binary representation. Each bit in the binary representation signifies a specific group. If the bit associated with any specific group is not 0, that group will be excluded from the protected object set.
3. The default protected object set includes two user objects, Administrator and krbtgt. These objects are explicitly protected, but they won’t induce AdminCount updates on other objects because you can’t make a user, group, or computer a member of a user object.
4. The default protected object set includes two groups that do not confer their protected status on their members, Domain Controllers and Read-Only Domain Controllers. While the DCPROMO command will add the associated computer object to the appropriate domain controller group when promoting a host as a domain controller, it won’t result in the object being added to the protected object set.
5. The default protected object set’s members are identified using their objectSid. While many of the default protected object set’s members are configured with systemFlag values that prevent them from being renamed or moved, Active Directory relies on the well-known objectSid values of these objects to ensure that they are consistently identified correctly.
As I said, there’s a lot going on. The important takeaway here is that there are a number of behaviors that complicate the process of understanding which groups in any particular domain will affect an AdminCount update on its members.
AdminSDHolder and SDPROP
Now that we have established a basic, if somewhat esoteric, understanding of the AdminCount attribute itself, it’s time to talk about the mechanism that controls the attribute’s behavior and why it exists. As mentioned at the beginning of this post, Active Directory objects with elevated administrative privileges are highly-valued targets for attack and compromise. These privileges are defined in each object’s security descriptor.
Security descriptors contain information about the object’s ownership, its primary group, the users and groups that are allowed or denied permission to access the object (the Discretionary Access Control List or “DACL”), and the auditable events that will generate a record in the security event log (the System Access Control List or “SACL”). They also contain control bits that can modify the security descriptor’s behavior.
To help secure objects known to possess elevated administrative privileges, Active Directory applies a strict security descriptor called the Authoritative Security Descriptor to every member of a domain’s protected object set. The Authoritative Security Descriptor is defined in the AdminSDHolder object located in the System container of every Active Directory domain’s default naming context (e.g., CN=AdminSDHolder,CN=System,<DOMAIN DN>).
The Authoritative Security Descriptor is designed to secure protected objects by:
1. Limiting the object’s Discretionary Access Control List to a restricted set of Access Control Entries (“ACEs”). These ACEs constrain the ability to modify the object to the NT AUTHORITY\System account and members of the Administrators, Domain Admins, and Enterprise Admins groups.
2. Enabling the object’s SE_DACL_PROTECTED security descriptor control bit. This disables security inheritance and prevents the protected object’s DACL from being modified by inheritable ACEs possessed by any of the protected object’s parent objects.
3. Restricting ownership of the protected object to the Domain Admins group. This limits the possibility of a non-privileged account taking ownership of a protected object, which would grant the non-privileged account modify permissions on the protected object and allow the non-privileged account to grant themselves full control of the protected object.
To be effective, this behavior requires that Active Directory can ensure that the security descriptors of existing members of the protected object set match, and will continue to match, the Authoritative Security Descriptor. It also requires that Active Directory can ensure that the Authoritative Security Descriptor is applied to objects when they become a member, either directly or transitively, of a member of the default protected object set. Active Directory addresses these needs with a task called the Security Descriptor Propagator (“SDPROP”). The SDPROP task is executed by the Local Security Authority Subsystem Service on domain controllers that own the PDC Emulator FSMO role every 60 minutes, by default. This period serves to limit the length of time that a modification of a highly-privileged object’s security descriptor, whether malicious or accidental, might persist while acknowledging the relatively computationally-expensive nature of SDPROP’s execution.
The default hourly SDPROP execution period can be overridden at the domain level by adding an AdminSDProtectFrequency entry to the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters registry key on the domain controller that owns the domain’s PDC Emulator FSMO role. The value of AdminSDProtectFrequency is interpreted as seconds and will accept values ranging from 60 to 7200 (i.e., 1 minute to 2 hours).
Overriding the default execution period is not recommended. Increasing the execution frequency can be expected to result in a substantial increase in CPU utilization on the PDC Emulator and decreasing the execution frequency lengthens the potential duration in which a modification to the security descriptor of a protected object can persist.
When SDPROP is executed it identifies the domain’s default protected object set and then recursively moves through each of those object’s membership trees to identify the complete membership of the protected object set. SDPROP compares the security descriptor of each of the identified protected objects to the Authoritative Security Descriptor. If an object’s security descriptor does not match the Authoritative Security Descriptor, an originating update replaces the object’s security descriptor with the Authoritative Security Descriptor and sets the value of the object’s AdminCount attribute to 1.
The fact that a dedicated task was created specifically to perform these checks may seem unnecessary, but Active Directory’s security descriptor propagation isn’t really set up to address this behavior. While updates to an object’s security descriptor or its distinguished name (unless the distinguished name change results in the object’s new parent being the domain’s Deleted Objects container) results in Active Directory propagating any associated inheritable Access Control List changes almost immediately, group membership changes don’t initiate that process. This means that adding an object to a protected group won’t trigger that process and can’t be used to apply the Authoritative Security Descriptor to the objects affected by the group membership update.
At this point, your eyes are probably starting to glaze over and you’re beginning to question your decision to read this post. I assure you that all this background information about the AdminCount attribute is important. Now that we’ve gotten all of that out of the way, let’s get into some of the AdminCount attribute’s limitations and the misunderstandings they can potentially create.
“Gotcha” #1: SDPROP executes on a schedule. As mentioned in the wall of text above, the SDPROP task only executes once an hour (by default). Consequently, it can take up to an hour for an account that has been added to a protected group to be identified by SDPROP as a member of the protected object set. This means that if an object becomes a member – either directly or transitively – of a protected object and that membership is removed before SDPROP executes, the object will not be identified as a protected object and the value of the object’s AdminCount attribute will remain unchanged.
“Gotcha” #2: SDPROP updates the AdminCount attribute when it modifies an object’s security descriptor. What’s the big deal here? Well, SDPROP doesn’t pay any attention to the value of an object’s AdminCount attribute. If SDPROP updates a protected object’s security descriptor, it will set the value of the AdminCount attribute to 1. If the protected object’s security descriptor matches the Authoritative Security Descriptor, SDPROP will leave the protected object’s AdminCount attribute unchanged, regardless of its value. Consequently, changing or removing the value of a protected object’s AdminCount attribute can effectively hide protected objects from simple reporting scans.
“Gotcha” #3a: SDPROP only looks at active members of the protected
object set. SDPROP scans start with the members of the default protected
object set and iterates through their memberships. However, highly-privileged
objects that are not members of the default protected object set are ignored by
SDPROP and the value of their AdminCount attribute will remain <NOT
SET>. As a result, the AdminCount attribute can’t be relied upon to
identify all of the highly-privileged objects in a domain.
“Gotcha” #3b: SDPROP only looks at active members of the protected object set. Once a protected object is removed from the groups it inherited its protected status from, it will subsequently be ignored by SDPROP while continuing to look exactly like a protected object. This is because Active Directory lacks a mechanism to undo the changes made by SDPROP when an object became a member of the protected object set. In this resulting “orphaned” state, the value of object’s AdminCount attribute remains set to 1 (or whatever its value was before its group membership changed). More importantly, the object’s security descriptor will also remain unchanged and will continue to block security inheritance from the object’s parents.
“Gotcha” #4: SDPROP doesn’t distinguish between group categories. When SDPROP iterates through the membership of the default protected object set it ignores the category of the member groups it identifies. Backing up a little bit, Active Directory groups fall into one of two categories: security groups and distribution groups. Security groups can pass privileges on to its members, while distribution cannot. While it may seem odd to protect objects that are transitive members of a default protected object through a distribution group – and, therefore, aren’t inheriting any privileges from the default protected object – a group’s category can be changed. As changing a group’s category will change its ability to confer privilege on its members, SDPROP ignores group categories entirely to prevent this behavior from being abused.
Kudos to those of you who have made it this far. If you only take one thing away from this post, let it be this: AdminCount, and the behaviors and processes associated with it, are a lot more complex than most people seem to appreciate.
Opening a PowerShell console session and running:
Get-ADObject -LDAPFilter "(adminCount=1)"
…doesn’t tell you which objects are being protected by SDPROP; it tells you which objects have an AdminCount attribute with a value of 1. The only reliable way to accurately identify protected objects is to do exactly what SDPROP does: identify the domain’s default protected object set and identify the complete membership of each of those objects.
At the end of the day, the AdminCount attribute is just a flag. In order to understand what that flag can tell you need to also understand what it isn’t able to tell you.
Michael Olig is a Technical Product Manager at STEALTHbits Technologies. He is currently responsible for the company’s StealthRECOVER platform and StealthAUDIT cloud and Exchange solutions.
Michael’s eclectic work history prior to joining the STEALTHbits team includes the titles “Product Manager, eDiscovery Solutions”, “Senior Manager of DevOps”, “Litigation Paralegal”, and “chef”.