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
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)
|
|
|