Upgrading Your Reverse Shell (WITH EXPLANATION)

A situation we’ve all found ourselves in. Our netcat listener returns a shell, you accidentally hit the arrow keys and now your input looks like:

#unam^[[C^[[D^[[D^[[A

You cancel out of it with CTRL+C accidentally killing the shell and you’re left angrily staring at your empty, lonely, blinking terminal window.

So how do we combat this? Well we upgrade our shell of course! There are plenty of guides out there already on how to do this and I’m not claiming to be bringing anything new to the table, but I wanted to write this post as I struggled to find any guides that actually explained what each step was doing, so let’s go.

Step 1 - The problem - The Netcat shell

 
 

So here we are. Our reverse shell is active, we eagerly type “id” in the hopes that we’re already root. But alas, we’re not lucky this time and there’s more work to be done. The default Netcat shell by itself can be pretty limiting. No command history, no arrow keys, no termination and trying to move into text editors like VIM and Nano will only end in disaster.

Suddenly our terminal looks like the below image and the impending “CTRL+C” is moments away.

 
 

But WHY does this happen? Well netcat doesn’t have the ability to translate our raw input into anything other than that. Raw input. When we press the left arrow key, a typical shell will receive a signal such as “^[[D” from our keyboard, it’ll understand that this signal means we want to move to the left and will move the cursor accordingly. Netcat can’t handle this, so it simply adds “^[[D” as a string of text at the command line.

So to make life easier, we must upgrade to a “smarter” shell.

Step 2 - Spawning a new shell with Python

Presuming our remote machine has python installed, we’ll first want to run the following command:

python3 -c 'import pty; pty.spawn("/bin/bash")'

This uses Python’s “pty” library (Psuedo-terminal utilities) to create a new bash shell using “pty.spawn”. Already we should be feeling better as we can now see our user context and location on the file system:


Step 3 - Matching properties

As we build out our new shell, to ensure complete compatibility, we’ll want to check a few properties before we move forwards. Start by issuing:

Ctrl+Z

This will background our current process letting us interact with our own system whilst we try to determine a few things.

Now we can run:

echo $TERM

This will show us our terminal type environmental variable. 99% of the time this will be “xterm-256color”. This specifies the terminal type as “xterm” with support for 256 colours enabled.

Next, we’ll run:

stty -a

stty stands for Settings TeleTYpewriter, essentially your terminal settings. (-a) simply displays them all. We’ll want to note down our values for rows and columns to be used later.

Our terminal will likely look something like this:

So our noted values for later are:

  • $TERM = xterm-256color

  • Rows = 42

  • Columns = 237


Step 4 - Setting new properties

Next, we’ll run:

stty raw -echo

The “-” means disable, so here we’re disabling command echoing. I.E. when you enter a command, it won’t print it back to you as it executes as can be seen by default with Netcat shells.

“raw” means that input and output remains raw. Sometimes processing can be applied to skip certain characters or render them differently. We don’t want this, raw disables any of this processing.


Step 5 - Returning to our reverse shell

Next, we’ll run:

fg

This will bring the process that we most recently put to the background using “CTRL+Z” back into the foreground, thus putting us back into our initial reverse shell.

At this point things MIGHT get a bit strange. Character spacing may not be accurate and your typed characters may appear invisible BUT trust the process and continue as if things were normal.

Next, now inside of our reverse shell, we’ll run:

reset

As can be seen, the text is a little off here

This simply resets the terminal and will let it take on the stty changes that we just made.

If you’re asked for a terminal type, enter just the terminal type without colour settings E.G. “xterm”:

At this point you should now be back into your shell with user@directory displayed.

Next, we’ll want to paste 3 commands into our reverse shell:

[1] export SHELL=/bin/bash
[2] export TERM=<$TERM value>                        (E.G. export TERM=xterm-256color
[3] stty rows <count> columns <count>                (E.G. stty rows 42 columns 237)

The first and second commands set our shell and terminal environmental variables such that any child processes take on these values as well. The third command then sets our terminal size in terms of rows and columns. This makes sure our reverse shell’s theoretical size matches its physical size on our screen so that when using fullscreen programs such as VIM, things will fit to our terminal appropriately.

And there we have it, you should now have a fully functioning shell free to use arrow keys and text editors to your heart’s content.

In Summary

In your reverse shell

  • [1] python3 -c 'import pty; pty.spawn("/bin/bash")'
  • [2] Ctrl-Z

In your machine

  • [3] echo $TERM (Note down the value)

  • [4] stty -a (Note down your rows and columns values)

  • [5] stty raw -echo
  • [6] fg

In your reverse shell

  • [7] reset
  • [7b] If asked for a terminal type, enter JUST terminal type E.G. "xterm"
  • [8] export SHELL=/bin/bash
  • [9] export TERM=<$TERM value>
  • [10] stty rows <num> columns <cols>