Who Can Modify Domain Admins & Asking the Right Question

9 min readMar 19, 2022

TL;DR How to query who can modify an AD group & a brief discussion of whether this is even the right question to be asking.


Welcome to part IV of our Auditing AD series.

Part I: 3 warm up questions to get us started

Part II: Who can reset the CISO’s password?

Part III: Who can execute DCSync?

Part IV: Who can modify the Domain Admins group?

Part V: Domain dominance in minutes via ACL abuse (i.e. why auditing AD matters)

Part VI: Why Allow statements matter a LOT more than Deny ones

Part VII: Sneaky persistence via hidden objects in AD

Part VIII: SDDL, what is it, does it matter?

Part IX: Do you know who owns your domain?

Part X: Who can push ransomware via Group Policy?

Part XI: Free ways to simplify auditing AD

Part XII: Sidenote on arcane rights like Self

Part XIII: Sidenote on the ScriptPath right

Part XIV: Self & so called “Effective Permissions”

Part XV: Inheritance Explained & an Example

Part XVI: Summary of our Auditing AD Series

Annex A: Scrubbing Group Policy for local admins

Annex B: What Property Sets in AD are, & why they matter

Annex C: Dangerous Rights & RE GUIDs Cheatsheet

Annex D: Mishky’s Blue Team Auditor

Annex E: Even ChatGPT gets this stuff wrong

Annex F: Get-ADPermission Cheatsheet

Annex G: Mishky’s Red Team Enumerate & Attack tools

This question was posed by a vendor on Day 8 of AD for Cyber Security Experts. While they did not explicitly label it as such, Day 7 was ‘Who can reset the CISO’s password?’

Their blog makes an error though on this one. The author states that:

“Specifically, technically speaking, all that one needs to determine is exactly who has Write Property effective permissions to modify the Member attribute on the Domain Admins object, cn=Domain Admins,cn=Users,dc=… in Active Directory.”

There are a couple of issues with this statement:

  • Most obvious, the path is ‘cn=domain admins,cn=users,dc=corp,dc=local’, or just do
(Get-ADGroup “Domain Admins”).DistinguishedName

The importance of that last bullet point is driven home in Part V of this series; Domain Dominance in Minutes via ACL Abuse.

Others have covered this, most noticeably Sean Metcalf, but here is a quick recap of the privileges that matter for security on a group in AD:

Sidenote; GenericWrite and WriteProperty with ObjectType 00000000–0000–0000–0000–000000000000 are technically not the same thing, however they confer the exact same permissions, that is the ability to modify any and all properties on an AD object. If this is a group then that includes adding members.

GUI vs PowerShell

In ADUC, the ability to change members of a group is shown as ‘Write Members’.

In PowerShell it is shown as ‘WriteProperty’ with ObjectType bf9679c0–0de6–11d0-a285–00aa003049e2.

Why is this? In AD everything is an object. Objects have attributes. One of the attributes of an AD group is members.

Therefore when we query the ACL of Domain Admins in PowerShell we are looking for who has ‘Allow’ on ‘Write-Property’ for the attribute ‘members’.

(Get-Acl (Get-ADGroup "Domain Admins").DistinguishedName).Access | Where-Object {($_.ActiveDirectoryRights -like "*WriteProperty*") -and (($_.ObjectType -eq "bf9679c0–0de6–11d0-a285–00aa003049e2") -or ($_.ObjectType -eq "bc0ac240-79a9-11d0-9020-00c04fc2d4cf") -or ($_.ObjectType -eq "00000000–0000–0000–0000–000000000000")) -and ($_.AccessControlType -eq "Allow")}

This handy table maps the GUIDs to the attributes you may need to query.

As seen in the table earlier though, there are other rights which include WriteProperty on members, so a more comprehensive query would be:

(Get-Acl (Get-ADGroup "Domain Admins").DistinguishedName).Access | Where-Object {(($_.ActiveDirectoryRights -like "*WriteProperty*") -and (($_.ObjectType -eq "bf9679c0–0de6–11d0-a285–00aa003049e2") -or ($_.ObjectType -eq "bc0ac240-79a9-11d0-9020-00c04fc2d4cf") -or ($_.ObjectType -eq "00000000–0000–0000–0000–000000000000"))) -or ($_.ActiveDirectoryRights -like "*GenericAll*") -or ($_.ActiveDirectoryRights -like "*GenericWrite*") -and ($_.AccessControlType -eq "Allow")}

Personally, I find the PowerShell method much more logical. Critically, PowerShell also allows you to query multiple things by simply using parenthesis and operators. This allows the auditor to find exactly what they are looking for. The GUI simply does not possess this functionality and granularity.

Asking the right question

This brings us to the point of all this; how vital it is to ask the right question. The real question is NOT “who can change the members of Domain Admins?” The real question is “who can escalate to Domain Admin and do something nefarious?”

For example suppose that either through sloppiness, poor management, human error, or the actions of a disgruntled insider an account was given WriteDACL or WriteOwner privileges on the Domain Admins group? We could audit for who can change the members, but what good is that? An attacker could just modify the ACL later and give themselves the privileges, or take ownership of the object and then do so.

Therefore we need to check for several privileges, namely the ones listed in the table earlier, as any one of them would allow an attacker to modify the group.

How to find the answer

One way to go about this is to use Invoke-ACLScanner in PowerView, as it handles finding exploitable ACLs really well and is free. In this case one could run the query:

(Invoke-ACLScanner -ResolveGUIDs | ?{(($_.ActiveDirectoryRights -like “*WriteDACL*”) -or (($_.ActiveDirectoryRights -like “*WriteProperty*”) -and (($_.ObjectType -eq “bf9679c0–0de6–11d0-a285–00aa003049e2”) -or ($_.ObjectType -eq “bc0ac240-79a9-11d0-9020-00c04fc2d4cf”) -or ($_.ObjectType -eq “00000000–0000–0000–0000–000000000000”))) -or ($_.ActiveDirectoryRights -like “*GenericWrite*”) -or ($_.ActiveDirectoryRights -like “*GenericAll*”) -or ($_.ActiveDirectoryRights -like “*WriteDACL*”) -or ($_.ActiveDirectoryRights -like “*WriteOwner*”) -or ($_.ActiveDirectoryRights -like “*Self*”)) -and ($_.ObjectDN -like “*Domain Admins*”) -and ($_.AccessControlType -eq “Allow”)}).IdentityReference

The downside is that PowerView will trip Microsoft Defender alerts, get blocked by RealTimeMonitoring, etc. Hence you would need change requests, approved exemptions, etc to put it on the network and use it.

Therefore I lean towards the builtin PowerShell AD commands. I already had a working template from Part II in the Auditing AD series for finding who can screw with a given user account. I simply tweaked it for groups.

Simple query to find who can mess with the Domain Admins group:

$ErrorActionPreference = “SilentlyContinue”
Import-Module ActiveDirectory
Set-Location AD:
$DN = (Get-ADGroup -Identity “Domain Admins” -Properties *).DistinguishedName
$owner = (Get-Acl $DN).owner
Write-Host "$owner owns this object. Owners have implicit privilege to do anything."
((Get-ACL $DN).Access | Where {((($_.ActiveDirectoryRights -like "*WriteProperty*") -and (($_.ObjectType -eq "bf9679c0-0de6-11d0-a285-00aa003049e2") -or ($_.ObjectType -eq "bc0ac240-79a9-11d0-9020-00c04fc2d4cf") -or ($_.ObjectType -eq "00000000-0000-0000-0000-000000000000"))) -or ($_.ActiveDirectoryRights -like "*GenericWrite*") -or ($_.ActiveDirectoryRights -like "*GenericAll*") -or ($_.ActiveDirectoryRights -like "*WriteDACL*") -or ($_.ActiveDirectoryRights -like "*WriteOwner*") -or (($_.ActiveDirectoryRights -like "*Self*") -and (($_.ObjectType -eq "bf9679c0-0de6-11d0-a285-00aa003049e2") -or ($_.ObjectType -eq "00000000-0000-0000-0000-000000000000"))) -and ($_.AccessControlType -eq "Allow"))}).IdentityReference

Or, if you need to show all nested members of the groups that have privileges:

$ErrorActionPreference = "SilentlyContinue"
Import-Module ActiveDirectory
Set-Location AD:
$GroupInQuestion = Read-Host "Please enter an AD group"
$DN = (Get-ADGroup -Identity $GroupInQuestion -Properties *).DistinguishedName
$owner = (Get-Acl $DN).owner
Write-Host "$owner owns this object. Owners have implicit privilege to do anything."
$suspects = ((Get-ACL $DN).Access | Where {((($_.ActiveDirectoryRights -like "*WriteProperty*") -and (($_.ObjectType -eq "bf9679c0-0de6-11d0-a285-00aa003049e2") -or ($_.ObjectType -eq "bc0ac240-79a9-11d0-9020-00c04fc2d4cf") -or ($_.ObjectType -eq "00000000-0000-0000-0000-000000000000"))) -or ($_.ActiveDirectoryRights -like "*GenericWrite*") -or ($_.ActiveDirectoryRights -like "*GenericAll*") -or ($_.ActiveDirectoryRights -like "*WriteDACL*") -or ($_.ActiveDirectoryRights -like "*WriteOwner*") -or (($_.ActiveDirectoryRights -like "*Self*") -and (($_.ObjectType -eq "bf9679c0-0de6-11d0-a285-00aa003049e2") -or ($_.ObjectType -eq "00000000-0000-0000-0000-000000000000"))) -and ($_.AccessControlType -eq "Allow"))}).IdentityReferenceWrite-Host "These groups can change the given group. Nested users in those groups listed below:"
$suspects = $suspects -replace "Value"," "
$suspects | Out-File C:\Temp\suspects.txt -Append
ForEach($suspect in $suspects)
$temp = ($suspect -split " \ ")[0]
$group = ($temp.Split("\")[1])
$members = (Get-ADGroupMember -Identity $group -Recursive).Name
$members | Out-File C:\Temp\suspects.txt -Append
Get-Content C:\Temp\suspects.txt | Sort-Object -Unique
$Count = (Get-Content C:\Temp\suspects.txt | Sort-Object -Unique).count
Write-Host "$Count users & groups can screw with the specified group"

Acting on this information

Of course this still leaves a rather large elephant in the room, namely with this many groups and users with privileges to modify the Domain Admin group … well, how many users can modify those users? After all, if an attacker can impersonate a user who in turn can modify Domain Admins then the attacker can modify Domain Admins. We walked through this exact scenario in Part V of the Auditing AD Series. The attacker was able to pivot through two user accounts after compromising an initial account. They were able to do this even though that initial account was explicitly denied access to modify Domain Admins.

This is why band aid fixes to poorly architected domains are not the best idea. If this fictional company was real then they’d need a serious audit, a penetration test, a lot of re-design of the architecture and privileges, and another audit to verify. Their current AD is sloppy and ripe for abuse.

The kicker? We haven’t even audited the Group Policy yet. So far we have only looked at AD groups, their privileges, nested users, and ACLs.

This is commonly known as ‘Miconfiguration Debt’, and over time it can become just as lethal as unpatched vulnerabilities. It can be far more costly, time consuming, and difficult to mitigate though.

A more holistic view

This is where BloodHound really comes in handy. It can show all paths to Domain Admins, for example:

Or show paths between a given user and a given target:

Some have criticized BloodHound as being [in their words] “bloody inaccurate”. They make this claim because BloodHound does not take Deny statements in ACLs into account. BloodHound only looks at Allow and only for privileges which can be used to take control of other AD objects. The authors of the free tool state this. They also state that in most environments they have tested they have noticed very little usage of Deny in ACLs.


Another valid question though is this: why would you allow a user or group a privilege in AD only to deny it in another ACL elsewhere? Why not just fix the original Allow in the original ACL? We saw in Part V how a user who was explicitly denied a privilege in an ACL was still able to move laterally and perform the denied action anyway due to sloppiness in other ACLs.

PowerShell is free, PowerView is free [but requires exemptions in Defender], BloodHound is free. You can start auditing and cleaning up your Misconfiguration Debt using readily available and free tools, or you can spend a lot of money on a paid tool.

Either way, the important thing is to do it.

Audit effectively, ask the right question, take advantage of builtin tools, and most of all fix the underlying issue! Stay safe!















I work various IT jobs & like Windows domain security as a hobby. Most of what’s here is my notes from auditing or the lab.