Active Directory Delegation Model Audit: How to Find Non-Standard Permissions That Grant Unexpected Control Over AD Objects

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Active Directory delegations accumulate silently. A helpdesk team gets ResetPassword delegation on the entire Users OU in 2015 for a password self-service project. The project ends but the delegation remains. In 2020, the helpdesk role is consolidated and the old security group is no longer actively managed -- but it still has membership and still has the delegation. In 2024, a former helpdesk member whose account was never disabled uses that delegation to reset a target user's password. The audit process exposes these paths before attackers discover them.
High-Risk Delegation Rights and What They Enable
Not all delegated rights create equal risk. These are the most dangerous:
GenericAll / Full Control: Grants every possible permission on the object. Equivalent to Domain Admin for the specific object. Allows password reset, group membership modification, disabling the account, and modifying group memberships.
WriteDACL: Allows modifying the object's DACL. An attacker holding WriteDACL can grant themselves GenericAll on the object, effectively a privilege escalation step. This right alone is sufficient for complete object compromise.
WriteOwner: Allows changing the object's owner. Owners have implicit permission to modify the DACL. WriteDACL and WriteOwner are functionally equivalent paths to object control.
GenericWrite: Allows writing any non-protected attribute. On user objects: can modify servicePrincipalName (enabling targeted Kerberoasting), scriptPath (adds malicious script to run at logon), homeDirectory, and other attributes.
AllExtendedRights: Grants all extended rights on the object. On user objects, this includes the User-Force-Change-Password extended right (force password reset without knowing current password).
WriteProperty (Member): On group objects, allows adding or removing group members. A holder of this right on Domain Admins can add themselves to the group.
ResetPassword / User-Force-Change-Password: Allows resetting the account's password without knowing the current password. Dangerous for delegations scoped to privileged user OUs.
Auditing OU-Level Delegations with PowerShell
The AD PowerShell module provides Get-ACL for AD objects, but reading AD ACLs requires the AD provider:
# Import AD provider for ACL access
Import-Module ActiveDirectory
# Audit delegations on a specific OU and all child OUs
function Get-ADOUDelegations {
param([string]$OUPath)
$ous = Get-ADOrganizationalUnit -Filter * -SearchBase $OUPath -SearchScope Subtree
foreach ($ou in $ous) {
$acl = Get-Acl -Path "AD:\$($ou.DistinguishedName)"
foreach ($ace in $acl.Access) {
# Skip built-in high-privilege accounts that are expected
if ($ace.IdentityReference -notmatch "BUILTIN\\|NT AUTHORITY\\|S-1-5-18|S-1-5-32") {
# Flag high-risk rights
$isHighRisk = $false
$highRiskRights = @("GenericAll","GenericWrite","WriteDacl","WriteOwner","AllExtendedRights")
foreach ($right in $highRiskRights) {
if ($ace.ActiveDirectoryRights -match $right) { $isHighRisk = $true }
}
if ($isHighRisk -or $ace.ActiveDirectoryRights -match "WriteProperty") {
[PSCustomObject]@{
OU = $ou.DistinguishedName
Identity = $ace.IdentityReference
Rights = $ace.ActiveDirectoryRights
InheritanceType = $ace.InheritanceType
ObjectType = $ace.ObjectType
IsInherited = $ace.IsInherited
AccessControlType = $ace.AccessControlType
}
}
}
}
}
}
# Run against your user and admin OUs
Get-ADOUDelegations -OUPath "OU=Users,DC=domain,DC=com" |
Where-Object {-not $_.IsInherited} | # Focus on explicitly set ACEs first
Sort-Object OU, Identity |
Export-Csv ou-delegations.csv -NoTypeInformation
The IsInherited = $false filter surfaces explicit delegations -- these are the ones that were deliberately granted and are the most likely to be non-standard.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Identifying Who Has Delegation Over Privileged User OUs
The highest-priority delegation audit is over the OUs containing Tier 0 and Tier 1 admin accounts:
# Identify all non-admin accounts with any write permissions on admin user OUs
$adminOUs = @(
"OU=Tier0Admins,OU=AdminAccounts,DC=domain,DC=com",
"OU=Tier1Admins,OU=AdminAccounts,DC=domain,DC=com"
)
$expectedIdentities = @(
"BUILTIN\Administrators",
"NT AUTHORITY\SYSTEM",
"NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS",
"DOMAIN\Domain Admins",
"DOMAIN\Enterprise Admins",
"Creator Owner"
)
foreach ($ouDN in $adminOUs) {
$acl = Get-Acl -Path "AD:\$ouDN"
$unexpectedACEs = $acl.Access | Where-Object {
($expectedIdentities -notcontains $_.IdentityReference.ToString()) -and
($_.AccessControlType -eq "Allow")
}
if ($unexpectedACEs) {
Write-Output "[!] Non-standard ACEs on $ouDN:"
$unexpectedACEs | Format-Table IdentityReference, ActiveDirectoryRights, InheritanceType
}
}
For each unexpected ACE found:
- Identify the current members of the identity (Get-ADGroupMember if it is a group)
- Determine when the delegation was added and by whom (review AD audit events for the target OU using Event ID 5136 in Security log)
- Verify whether the delegation is still operationally required
- Remove if not required:
Set-Acl -Path "AD:\$ouDN" -AclObject $modifiedACL
Automated Discovery with BloodHound
BloodHound automates delegation discovery across the entire domain by collecting ACLs on all objects and computing attack paths to Domain Admin:
Using BloodHound CE (Community Edition) for delegation audit:
# Collect AD data with SharpHound collector
.\SharpHound.exe --CollectionMethods ACL,Group,ObjectProps,Trusts --OutputDirectory C:\temp\bh-output
BloodHound queries for delegation-based attack paths:
// Find all principals with GenericWrite on any admin user
MATCH (n)-[:GenericWrite]->(m:User)
WHERE m.admincount = true
RETURN n.name, m.name, labels(n)
// Find all principals with WriteDACL on privileged objects
MATCH (n)-[:WriteDacl]->(m)
WHERE m.admincount = true OR m.objectid CONTAINS 'Domain Admins'
RETURN n.name, m.name, labels(n)
// Shortest path from a specific low-privilege account to Domain Admin
MATCH p=shortestPath((s:User {name:'user@domain.com'})-[*1..]->(t:Group {name:'DOMAIN ADMINS@DOMAIN.COM'}))
RETURN p
BloodHound is more comprehensive than manual ACL review for finding multi-hop paths (user A has WriteDACL on group B, group B has GenericWrite on user C who is in Domain Admins), but manual ACL review is faster for targeted audits of specific OUs.
Remediating Non-Standard Delegations
After identifying non-standard delegations, remediation follows this sequence:
Step 1: Verify the delegation is not operationally active Check whether the delegated identity is still used. If a service account had delegation, verify the service still exists. Check the last login date for any users in a delegated group.
Step 2: Remove the ACE with PowerShell
# Remove a specific ACE from an OU
$ouDN = "OU=Users,DC=domain,DC=com"
$targetIdentity = "DOMAIN\old-helpdesk-group"
$path = "AD:\$ouDN"
$acl = Get-Acl $path
# Filter out the specific ACE to remove
$aceToRemove = $acl.Access | Where-Object {
$_.IdentityReference.ToString() -eq $targetIdentity
}
foreach ($ace in $aceToRemove) {
$acl.RemoveAccessRule($ace) | Out-Null
}
Set-Acl -Path $path -AclObject $acl
Write-Output "Removed $($aceToRemove.Count) ACE(s) for $targetIdentity on $ouDN"
Step 3: Verify with BloodHound re-collection Re-collect AD data with SharpHound after remediation and re-run the attack path queries to confirm the path is gone.
Step 4: Establish a delegation register Document all intended delegations in a configuration management database or a simple CSV that is reviewed annually. Include: OU path, delegated identity, rights granted, business justification, approval ticket, and review date. Any delegation not in the register is a finding.
The bottom line
AD delegation audits consistently surface privilege escalation paths that standard group membership reviews miss. WriteDACL, GenericWrite, and ResetPassword on privileged user OUs are the highest-priority findings. Use the PowerShell ACL audit for targeted OU reviews and BloodHound for comprehensive multi-hop path analysis. Establish a delegation register so any future ACE that is not documented is immediately identifiable as non-standard.
Frequently asked questions
How often should AD delegation audits be run?
For Tier 0 and Tier 1 admin OUs, run a delegation audit quarterly. For the full domain, annually is typically sufficient unless you have a high-change-rate environment. More importantly, configure AD audit logging to alert on ACE changes (Event ID 5136 -- A directory service object was modified) for sensitive OUs in real time. The quarterly audit catches accumulated drift; real-time alerting catches active manipulation.
Does removing a delegation break any Group Policy application?
GPO application uses Kerberos authentication and does not rely on delegated LDAP permissions on user objects. Removing delegated permissions does not affect GPO processing. The only scenario where ACE removal could affect Group Policy is if you remove read access for Authenticated Users on the OU itself, which would prevent standard clients from reading GPO links. Standard delegation remediation (removing over-permissive write rights) does not affect GPO processing.
What is the AdminSDHolder and how does it relate to delegation audits?
AdminSDHolder is an AD container whose ACL is periodically copied to all protected accounts (Domain Admins, Schema Admins, etc.) by the SDProp process, which runs every 60 minutes by default. This means any delegation you set on a protected account's object will be overwritten within 60 minutes by the AdminSDHolder ACL. Conversely, if someone modifies the AdminSDHolder ACL itself, those modified permissions will propagate to all protected accounts within 60 minutes. The AdminSDHolder object's ACL is a critical audit target -- run `Get-Acl 'AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=com'` and compare against a known-good baseline.
How do I find all accounts that can reset passwords on user objects in a specific OU?
Run: `$acl = Get-Acl 'AD:\OU=Users,DC=domain,DC=com'; $acl.Access | Where-Object { $_.ActiveDirectoryRights -match 'ExtendedRight' -and ($_.ObjectType -eq '00299570-246d-11d0-a768-00aa006e0529' -or $_.ObjectType -eq '00000000-0000-0000-0000-000000000000') }`. The GUID `00299570-246d-11d0-a768-00aa006e0529` is the ObjectType GUID for the User-Force-Change-Password extended right. The all-zeros GUID means the ACE applies to all extended rights. Both GUIDs, combined with InheritanceType that includes descendant User objects, indicate password reset delegation.
How do I baseline and compare AD ACL changes over time to detect delegation abuse?
Export ACL snapshots using `Get-ADObject -SearchBase 'DC=domain,DC=com' -SearchScope Subtree -LDAPFilter '(objectClass=*)' -Properties nTSecurityDescriptor | Export-Clixml -Path acl-snapshot-$(Get-Date -Format yyyyMMdd).xml` on a schedule (weekly). Compare snapshots with `Compare-Object` on the nTSecurityDescriptor property to surface new ACEs. For continuous monitoring, enable DS Access auditing (Audit Directory Service Changes) and alert on Event ID 5136 (Directory Service Object Modified), which logs the specific attribute modified and the old and new values. SDProp changes (originating from the SYSTEM account) are expected and can be filtered; all other ACE modifications on high-value objects warrant investigation.
What is AdminSDHolder and how does it affect Active Directory delegation?
AdminSDHolder is an AD object (CN=AdminSDHolder,CN=System,DC=domain,DC=com) whose ACL serves as the template for ACLs on protected accounts (members of privileged groups like Domain Admins, Enterprise Admins, Schema Admins). Every 60 minutes, the SDProp background process compares the ACL of each protected account against the AdminSDHolder template and resets any differences. This means custom delegation ACEs added to Domain Admin accounts are silently removed every hour. If you need to delegate permissions on protected accounts (for example, allowing a helpdesk group to reset a specific protected user's password), add the permission to the AdminSDHolder object itself rather than the user object. Be aware that the AdminSDHolder ACL is a high-value target: unauthorized modifications to it grant persistent permissions on all protected accounts.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
