You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

230 lines
8.6 KiB

---
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 = <undef>; eol2 = <undef>; swtch = <undef>; 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<enter><enter>` 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<enter><enter>`, 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<enter><enter>
```
:::
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 </dev/tty
local columns=$(stty -a < /dev/tty | grep -oE 'columns [0-9]+' | cut -d' ' -f2)
local rows=$(stty -a < /dev/tty | grep -oE 'rows [0-9]+' | cut -d' ' -f2)
notify-send "Terminal dimensions" "Rows: $rows\nColumns: $columns\nstty command on clipboard"
echo "stty rows $rows cols $columns
export TERM=\"xterm-256color\"" | xclip -i -selection clipboard
stty raw -echo < /dev/tty; fg
```
Notice that we need to provide the stty command `/dev/tty` as we are not running it interactively.
Put that all together:
```zsh
function fg-bg() {
if [[ $#BUFFER -eq 0 ]]; then
local backgroundProgram="$(jobs | tail -n 1 | awk '{print $4}')"
case "$backgroundProgram" in
"nc"|"ncat"|"netcat")
# Make sure that /dev/tty is given to the stty command by doing </dev/tty
local columns=$(stty -a < /dev/tty | grep -oE 'columns [0-9]+' | cut -d' ' -f2)
local rows=$(stty -a < /dev/tty | grep -oE 'rows [0-9]+' | cut -d' ' -f2)
notify-send "Terminal dimensions" "Rows: $rows\nColumns: $columns\nstty command on clipboard"
echo "stty rows $rows cols $columns
export TERM=\"xterm-256color\"" | xclip -i -selection clipboard
stty raw -echo < /dev/tty; fg
;;
*)
fg
;;
esac
fi
}
zle -N fg-bg
bindkey '^Z' fg-bg
```
There we go. Once you have a reverse shell, you still have to manually type the python command to get a tty. I suggest looking into your terminal emulator's settings to see if you can map the command to a key binding. Once that is done, simply press `ctrl+z` twice, then `enter`. You will be able to paste the stty command in order to set the correct width and height.
![Reverse shell upgraded quickly](/assets/rev-shell/reverse-shell.gif)