--- title: Upgrading reverse shells date: 2020-06-01 description: Once you've got a reverse shell, there are a number of things you can do to make it nicer to work with. I go through those and at the end, automate the annoying bit with zsh. tags: - ZSH - Pentesting - Linux --- So, you've managed to get a reverse shell through one means or another. Unfortunately, it's pretty basic. You can't use your arrow keys, the line gets overwritten if your command is too long and you can't backspace properly. All in all, it's not a nice experience. ## Pseudo terminal The first thing I like to do is spawn a TTY. This tells terminal tools that they can be run interactively. For example, if you run `ls` in a normal terminal, you are likely to see it output the files in columns: ```bash localhost$ ls burppro Desktop GitRepos Pictures 'Calibre Library' Documents go Templates Contacts Downloads Music Videos ``` However, in your reverse shell, you won't see the output in columns ```bash reverseshell$ ls burppro 'Calibre Library' Contacts Desktop Documents Downloads GitRepos go Music Pictures Templates Videos ``` This is because in the first instance, ls "knows" that it is being used in an interactive session. However, in the reverse shell, ls doesn't know that it is being used interactively, so it gives the most simple output possible. If you were to pipe ls to another program, it would do the same. ```bash localhost$ ls | cat burppro 'Calibre Library' Contacts Desktop Documents Downloads GitRepos go Music Pictures Templates Videos ``` This is why you can use ls with tools like `grep` without worrying about joining all the columns. It is possible to spawn a tty (or at least a pseudo tty) with python2 or 3 with the same command: ```bash python 'import pty; pty.spawn("/bin/bash")' python2 'import pty; pty.spawn("/bin/bash")' python3 'import pty; pty.spawn("/bin/bash")' ``` On most Linux boxes you come across, one of these should work. If `/bin/bash` isn't available, you could try `/bin/sh`, although this is more limited than bash. ## Setting the window size On our reverse shell, the width and height are not set and, as a result, default to 80x24. This is usable, but not particularly nice. To change it, we will background the reverse shell and take a note of the actual terminal size. To background, simply press ctrl+z. ```bash reverseshell$ ^Z zsh: suspended nc -lvnp 4444 localhost$ ``` To get the current size of your terminal window (in rows and columns), you can run the command: ```bash localhost$ stty -a speed 38400 baud; rows 59; columns 136; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; 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 ``` The important part here is on the first line, in this example, I have 59 rows and 136 columns. Your values are likely to be different. We could foreground the session by typing `fg` and running the command ```bash stty rows 59 columns 136 ``` However, while we have the session backgrounded, there is some more stuff we can do. ## Special keys and correct size Special key codes, such as escape and key cords that begin with ctrl won't work. This makes using programs like vim almost impossible. In order to rectify this, we need to make our local shell send the raw characters through netcat. Hopefully, you still have the netcat session backgrounded, if not, you can just push ctrl+z again. We can then run the following command in order to make our shell pass the raw characters and prevent it from echoing those characters. ```bash stty raw -echo ``` In order to get the reverse shell back, type `fg`, although you won't see this on the screen as we have asked the terminal not echo commands (that is what the `-echo` does). :::{.tip} If your local shell is zsh, you will have to run the fg command before the prompt is re-drawn, otherwise the `-echo` option will be reset. If you are interested in why, read about it [here](https://www.zsh.org/mla/workers/2014/msg00870.html). The remediation is simply this: ```zsh stty raw -echo; fg ``` ::: We now simply need to tell the shell running on the remote server what the window size is. This is done with another stty command: ```bash stty rows 59 columns 136 ``` By this point, you should have a pretty nice terminal. You can use programs such as vim or tmux that take over the whole terminal and rely on being able to hijack specific keys. ## Terminfo Teaminfo is a database that describes the capabilities of terminals. This covers things like the ability to display colour or italics. It is unlikely that your specific terminal has entries in the servers terminfo database, so a good choice is to set your `TERM` variable to `xterm-256color`. This is a common setting that is very likely to be on servers, it also enables a lot of the capabilities that most people want out of a terminal, including colour support. To do so, run: ```bash EXPORT TERM="xterm-256color" ``` ## Making it easier By this point you should have quite a nice terminal to use. You can use your arrow keys, vim, tmux (if it's installed) and just about anything else. Importantly, you will also be able to use sudo or su which can ask for user input whilst running. However, I would like to share with you my method that automates a significant part of this. You will need to be using zsh on your host machine for this to work. It may be possible in bash, I have just never spent the time trying to implement it. Firstly, I took and adapted the [widget used by Greg Hurrell](https://github.com/wincent/wincent/blob/d6c52ed5528f353e652799e50d0e9250f1bec6ce/aspects/dotfiles/files/.zshrc#L280-L289) uses to foreground tasks with `ctrl+z`. ```zsh function fg-bg() { if [[ $#BUFFER -eq 0 ]]; then fg fi } zle -N fg-bg bindkey '^Z' fg-bg ``` This means that after pushing ctrl+z to background a task, you can then push ctrl+z again to foreground it. However, if we have backgrounded netcat, I would like to perform some extra steps. In the code below I check if the program is `nc`, `ncat` or `netcat`. ```zsh local backgroundProgram="$(jobs | tail -n 1 | awk '{print $4}')" case "$backgroundProgram" in "nc"|"ncat"|"netcat") # Our NC logic needs to go here ;; *) fg ;; esac ``` Inside the netcat case, I get the rows and columns, put the commands that need running on the server on my clipboard and run the `stty raw -echo` command on my host. That looks like this: ```zsh # Make sure that /dev/tty is given to the stty command by doing