Upgrading the Shell

INTRODUCTION

Background: Why a reverse shell?

A successful attack on a target almost always involves creating a reverse shell. For anyone unfamiliar, a reverse shell is when you - the attacker - open up a listener on your machine, then force the target to connect back to your machine. This opens a shell on the target machine that is accessible by the attacker machine.

Why is that important? From the target machine’s perspective, the connection is formed using outbound traffic. If the connection happens over an innocuous-looking port, then it’s very likely that a firewall on the target will not prevent this connection from forming. After all, the point of internet access is well… to access the internet! It would be unreasonable for any kind of security system to block your outbound connection.

The problem

Any hacker with even a modest degree of experience knows the pain of working out of a “dumb” shell. There’s always the instinctive Ctrl+C keypress to worry about. Additionally, more often than not, all the modern niceties are missing:

  • tab completion
  • command recall (hitting up arrow)
  • colors
  • scripting

Initially, reverse shells are such fragile things. How can you regain some of the above functionality without accidentally closing your connection? I’ll go over the basics in this article.

Note: not all of the steps in this article are always necessary. It would be foolish to do every one every time you make a reverse shell. Only take these steps as you need them.

BASH IT AROUND

Before catching the shell

Prior to forming the reverse shell, it is useful to switch to bash on your attacker machine. I find this gives the most flexibility when upgrading the shell on linux targets.

Upgrading the shell won’t always enable command recall. It’s good to prepare your reverse shell with rlwrap. This is a tool that I deem essential against Windows targets, but isn’t always necessary against a Linux target. That being said, I don’t really see any downside in using it.

☝️ If I’m being honest, I still don’t know why sometimes command recall/history works and sometimes it doesn’t!

Lastly, I’ve found that using a socat reverse shell provides best control over all of these options. Again though, that’s just anecdotal.

Putting all of the above together, this is my stereotypical prep for catching a reverse shell:

# Open a port on the firewall
sudo ufw allow from $RADDR to any port 4444 proto tcp
# Switch to bash (I'm usually in zsh)
bash
# Use socat listener with rlwrap
rlwrap socat -d TCP-LISTEN:4444 STDOUT

Choose your weapon

The first step is to find out what tools you have available on the target. Check for python and perl, and check the location of bash.

This article assumes that the target does indeed have bash. Other shells will work too, with slight modification to the steps taken.

which python python3 perl bash

We’ll use python, perl, or even bash itself to open a proper bash shell. For python, use the pty module:

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

For perl, just exec it directly:

perl -e 'exec "/bin/bash"'

You may be asking yourself “why not just open the reverse shell using those in the first place”? Sometimes you can. Often, that type of this is prevented by application sandboxing. Even moderately-sensible settings for nginx and apache prevent this type of thing.

Finally, if neither python nor perl are present, just set the terminal to bash directly:

SHELL=/bin/bash script -q /dev/null

Make it pretty

Open another terminal on your attacker machine. Set it up exactly like you did before opening your reverse shell listener. Personally, I usually roll in Kali, using zsh: I prepare for a reverse shell by opening a new tab in QTerminal then switching to bash:

[Ctrl+Shift+T]
bash

Then, check what your current settings look like. Most importantly, we want to know the dimensions (rows and columns) and the current terminal. Jot down the output from these:

# Check the color profile
echo $TERM
# Check how many rows and columns are present
stty -a

terminal settings

Better yet, perform the above steps before you even start up your listener.

Keep in mind that the rows and columns will vary, depending on the state of your terminal window (if you’re using your operating system in windowed mode).

Now that you’ve recorded your settings, switch back to your reverse shell and do the upgrade:

# Background the current process
[Ctrl+Z]
# Put the terminal into raw / no-echo mode
# and foreground the original process
stty raw -echo; fg [Enter] [Enter] 

Finally, put your original settings into this terminal:

export TERM=xterm-256color # From what you recorded earlier
export SHELL=bash
stty rows 35 columns 120 # From what you recorded earlier

If everything went according to plan, you should have a pleasant reverse shell with things like colors and tab-completion, and command recall using the up arrow:

colors tab completion

The result of everything should be a fully interactive TTY terminal. You can even use full-screen programs like less and pspy from a reverse shell that has been upgraded like this.

tldr;

Do this on your attacker machine:

sudo ufw allow from $RADDR to any port 4444 proto tcp
bash
echo $TERM
stty -a | grep rows
rlwrap socat -d TCP-LISTEN:4444 STDOUT

Catch the reverse shell, then do this via the reverse shell:

which python python3 perl bash
python3 -c 'import pty; pty.spawn("/bin/bash")
[Ctrl+Z] stty raw -echo; fg [Enter] [Enter] 
export TERM=xterm-256color
export SHELL=bash
stty rows 35 columns 120

Still not working?

If all of the above failed, as a last resort just use interactive sh. Do this in you reverse shell:

export TERM=xterm
/bin/sh -i

Troubleshooting:

“None of your instructions worked. You’re an idiot, 4wayhandshake.”

You probably forgot to switch to bash before opening your reverse shell listener. Terminate your reverse shell, switch to bash, then open your listener and re-exploit.

“On every line break, my shell is offset to the column of the last character of the previous line. Lists get printed out diagonally.”

It’s likely you forgot to switch to a bash shell before using stty raw -echo mode. You’ll have to terminate your reverse shell, re-exploit, and try again. Make sure to do the steps listed in here first.

“Backspace doesn’t work! Neither do arrows! Only letters seem to work. Anything else appears as a control character like ‘^B’! “ 🙀

You might not need to terminate your reverse shell. In short, your stty settings are messed up. Try resetting it back to default:

# This might fix your arrow keys and tab
stty sane
export TERM=linux

Then try writing something and hitting backspace. Does it appear as “^H”? If so, do this extra step:

stty erase ^H

You can always check which control characters are being used by reading the second line of stty -a.

CONCLUSION

The above steps will become a rote habit as you form more and more reverse shells. When you find yourself lacking functionality that you need, see if any of these techniques will help you upgrade your way out of it. Without too much effort, you’ll soon be able to produce a reverse shell with a similar look and feel to an SSH connection.

I hope you find these techniques as useful as I have. Shout out to @ropnop for writing their blog post that taught me to do the majority of this in the first place.


Thanks for reading

🤝🤝🤝🤝
@4wayhandshake