What is required to delete admin accounts that is member of a protected group like Domain Admins or Enterprise Admins?
The most common answer is whoever has the Delete Right on the user object. But when it comes to ACLs in Active Directory it’s not always that easy. ACLs is a powerful and complex thing in Active Directory.
If we read the Microsoft documentation on how the system evaluates if a Security Principal is allowed and denied access: Discretionary Access Control Lists (DACLs) and Access Control Entries (ACEs)
When access is requested to an Active Directory object, the Local Security Authority (LSA) compares the access token of the account that is requesting access to the object to the DACL. The security subsystem checks the object’s DACL, looking for ACEs that apply to the user and group SIDs referenced in the user’s access token. The security subsystem then steps through the DACL until it finds any ACEs that allow or deny access to the user or to one of the user’s groups. The subsystem does this by first examining ACEs that have been explicitly assigned to the object and then examining ones that have been inherited by the object. The following illustration shows the important parts of an access token and a DACL when a request is evaluated.
If an explicit deny is found, access is denied. Explicit deny ACEs are always applied, even if conflicting allow ACEs exist. Explicit allow ACEs are examined, as are inherited deny and allow ACEs. The ACEs that apply to the user are accumulated. Inherited deny ACEs overrule inherited allow ACEs but are overruled themselves by explicit allow permissions. If none of the user SIDs or group SIDs in the access token match the DACL, the user is denied access implicitly.
Now to the fun part, when talking about deletion you must consider a little more than just this logic to determine the effective permissions granting a user to delete a Domain Admin user.
Just to mention protected groups which can be read about here: AdminSDHolder pitfalls and misunderstandings. It’s a special set of high-privileged groups like Domain Admins, Enterprise Admins, Account Operators etc. These have a special Security Descriptor held by the AdminSDHolder container and maintained by the AdminSDHolder background task on the PDC DC default every 60 minutes (nothing to do with SDProp: How the Active Directory – Data Store Really Works (Inside NTDS.dit) – Part 3 ).
When a Security Principal or Distribution group is added to any of the protected groups the objects will be stamped with the Authoritative Security Descriptor from the AdminSDHolder containers nTSecurityDescriptor attribute. If it hasn’t been tampered with, inheritance will be disabled on the members, so it is solely relied on explicit permissions set in the DACL, right?
We can take a quick look on the DACL of a user that is member of Domain Admins:
And a picture of AdminSDHolder itself:
As we can see it has a strict set of ACEs and basically its only DA, EA, BA and SYSTEM that has sensitive access to control these objects. With this quick glance we could assume you have to be SYSTEM (Domain Controller) or a member of the mentioned high-privileged groups to being able to delete members of protected groups, right?
We can all agree that it would be dangerous if a lower privileged user could delete the most sensitive admin accounts, right?
Just to make sure, we also have the Effective Permissions tab in ADUC where we can “analyze” what a user can do on a specific object. In this example we can see that Tony isn’t allowed much more than reading this object and its attributes and he isn’t allowed to delete the user account named Hank (that is member of Domain Admins).
Now, if we look at the options we have regarding deleting objects in Active Directory you may have seen that there is three different types of Delete:
- Delete (DELETE Access Mask)
- Delete Child (ADS_RIGHT_DS_DELETE_CHILD Access Mask)
- Delete Subtree (ADS_RIGHT_DS_DELETE_TREE Access Mask)
And here it gets interesting. When performing a delete action, the system verifies the security descriptor for both the object and its parent object before allowing or denying the deletion.
An ACE that explicitly denies Delete access to a user has no effect if the user has Delete Child access on the parent. Similarly, an ACE that denies Delete Child access on the parent can be overridden if DELETE access is allowed on the object itself.
Delete and Delete Child examples:
My admin user Tony don’t have the ACE: Allow Delete access on a Domain Admin user named Hank. Tony could still delete the account if he has the ACE: Allow Delete Child User on the parent OU.
If an Explicit ACE: Deny Delete access is set on Hank. Tony would still be able to delete the account if he has the ACE: Allow Delete Child User on the parent OU.
And if we reverse it: Tony ends up in an ACE: Deny Delete Child User on the parent OU, he could still delete it if he has Explicit Allow Delete in an ACE on the user object.
So, with this we can see that the AdminSDHolder Security Descriptor doesn’t really protect the admins or nested groups in all scenarios. If you look back at the effective permissions picture, Tony had no rights on the user to delete it. But he has the ACE: Allow Delete Child User on the parent OU and will be able to delete Hank which is a member of Domain Admins group.
Accidental Deletion protection:
What about the Protect object from accidental deletion feature introduced in Windows Server 2008? Default when you create a new OU in Windows Server 2008+ you get the tick box “Protect object from accidental deletion” checked on the object.
What actually happens is that an Explicit Deny ACE is created on the object. Default it’s Deny Delete, Delete Subtree for the Everyone principal.
This is default enabled when you create a new OU with the admin tools like Active Directory Users and Computers MMC Snap-In or AD PowerShell cmdlets. There are some interesting behaviors with this feature:
The Deny ACE isn’t specified on the defaultSecurityDescriptor in the Schema for object class Organizational-Unit. It’s a feature in the management tools.
If you create an OU with ADSI scripting like this:
$objDomain = [adsi]'LDAP://DC=demo,DC=secid,DC=se'
$objOU = $objDomain.Create("organizationalUnit", "ou=Corp OU")
It won’t be protected. So, it can vary dependent on how you create objects in your Active Directory environment if this is enabled or not.
The other thing is how it works with the evaluation if a user actually can delete a protected object. As we stated earlier it depends on if it has an Allow Delete on the end object or Allow Delete Child on the parent. To add an explicit Deny for Everyone wouldn’t be enough, and that’s true. When enabling an object for Protection against accidental deletion it also adds Deny Delete All Child objects for Everyone on the parent OU if the user has access to modify that on the parent (Else he can’t enable protection against accidental deletion).
This can create headaches if you for instance would enable this on an end user or group. If you have a delegation model configured a group is granted Delete child objects from that OU, they will now be denied that. And this ACEs can vary a lot depending on how you created the OU and what objects you have enabled this protection for (and delete child isn’t removed if you remove the protection from a child object).
Will this help protect Domain Admin users? There are a few risks here.
Since the DACL is derived from AdminSDHolder and that one isn’t default configured for accidental deletion the users won’t be stamped with any Deny ACE.
If we configure it with e.g. PowerShell:
Get-ADObject "CN=AdminSDHolder,CN=System,DC=demo,DC=secid,DC=se" | Set-adobject -ProtectedFromAccidentalDeletion $true
The parent container CN=System,DC=X will get an ACE with Deny Delete All Child Objects for Everyone which maybe isn’t recommended.
When the updated DACL will be updated on the members of protected groups, the AdminSDHolder process wont set that on the users’ parent container and this won’t be a good solution.
Delete Subtree Server Control:
Last, we also have the Delete Subtree access which is interesting. If we have deep OU structure and those are protected from deletion in any ways or if there are some child objects you don’t have any Delete/Delete Child access granted, we can make life easier and use the Delete Subtree server control.
The LDAP_SERVER_TREE_DELETE_OID 1.2.840.1135188.8.131.525 is a LDAP v3 extended control you can use to tell the DC in a delete operation to recursively delete the entire subtree of objects located under the object specified in the search request.
With this function we can delete an entire tree and all we need is Delete and Delete Subtree on the top OU and then trigger the Delete Subtree server control and all the ACLs on child objects will be ignored.
As you can see there could be a complex task to evaluate who can delete different object like Users, Computers, Groups and Even Domain Admins. It’s not enough to only evaluate explicit ACEs and inheritance on objects that has been propagated by SDprop, you also needs to evaluate explicit ACEs on the end object and the parent container.
A malicious user can do a lot of harm and create a DoS attack just by deleting an entire tree. If he manages to delete all the admins as well, then you’re in a tough situation.
Luckily one account that can’t be deleted this way is the Built-In Administrator (as well the DA/EA and other built-in groups). This account will be the one that saves the day in an emergency, and I hope you know the password that day.
And it could be easier than you think to accidentally delete a structure, use an existing script where you didn’t think of that DeleteTree was used or you targeted the wrong OU:
$obj = [System.DirectoryServices.DirectoryEntry] "LDAP://OU=Corp,DC=demo,DC=secid,DC=se"
Now when we have talked about the delete access rights it could be a good idea to think about who has these powers default in Active Directory? Besides the obvious ones like Administrators, Domain Admins, Enterprise Admins we of course also have the well-known Account Operators group.
Account Operators has default explicit Full Control on User, Computer, Group and InetOrgPerson objects. They don’t have that explicit access granted on the AdminSDHolder Security Descriptor, but they do have an explicit Create/Delete Child User, Group, Computer and InetOrgPerson on Organizational Units. If the parent OU to the Domain Admins don’t have the explicit Deny Delete Child Users AO will be able to delete Domain Admin users.
(This can be removed from the defaultSecurityDescriptor attribute of the object class defined in Schema if you’re up to the task)
Another angle is targeting group nesting, if users has DA membership via group nesting just delete that group and they won’t be DA anymore.
These are a few reasons why I want to have Admin OU separated as a Root OU to minimize the risks of faulty delegation.
A favorite is to separate them like:
- Service Owners (DS Team/Tier-0)
- Admins (And the Tier levels needed)
Create crystal clear Ownerships of the top OUs. The rootDNS object is of course owned by the DS/T0 team. Keep delegation close to the end objects and never use Account Operators.
To summarize this, it could be a complex task to evaluate if a user can delete different types of objects. The three different Delete Rights isn’t evaluated together in the built-in “Effective Permissions” tool, this is one example that it isn’t enough to only look at the permissions granted on the end object, you will need to look at the explicit ACEs at the parents as well. This can be a nightmare in a complex structure with 18 years of bad ideas.
Sorry, I just realized I’ve been only talking about explicit permissions here. So, I will add this: If Tony would have an Explicit Deny Delete Child Objects and Inherent Allow Delete on all User objects set on an OU, he could still delete users in that OU.
Having fun with systemFlags:
A way to make the system ignore the ACLs is to use the systemFlags attribute and set FLAG_DISALLOW_DELETE, 0x80000000. If this is set on a user, you won’t be able to delete that user. You won’t even get the delete option in ADUC.
And if you try to do a subtree delete it won’t be possible to delete the protected object.
The sad part with this is that you normally can’t set that flag on User objects, I added that value with mimikatz DCShadow module and replicated that value in to the database. Just to see the behavior ?
Having fun doesn’t mean this should be done or is recommended, just a way to see how things behave or work. If you want to read more about deletion in AD on a deep level with other rules I would recommend to read this: How the Active Directory–Data Store Really Works (Inside NTDS.dit)–Part 4