Tuesday, January 28, 2014

PHD CTF Quals 2014 - yet another pyjail

For this python jail, they give us an IP to connect to as well as the source code:

import re
import sys
import string
from sys import stdout
sys.stderr = stdout

sanitize = re.compile(

trusted_builtins = """
    True False type int

alphabet = ' \n\r0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(),.:;<=>[]_{}'

t1 = ''.join(chr(code) for code in xrange(256))
t2 = []
for i in t1:
    if i in alphabet:
        t2.append(' ')
trans_table = string.maketrans(t1, ''.join(t2))

EXPECTED = 13.37

del alphabet, t1, t2, i, sys, string, re

def clear_builtins():
    orig = __builtins__.__dict__.copy()
    for i in trusted_builtins:
        __builtins__.__dict__[i] = orig[i]

part1_of_flag = '******************'
part2_of_flag = '******************'
egg = 'egg'

def main():

    if raw_input() != 'leetleetleetleet':

    print ('Welcome to pyjail!\n\n'
           'Try to get the flag!\n'
           'Use ctrl+D or --- to submit your code\n')

    code = []
    total_bytes = 0
    while True:
            value = raw_input()
            total_bytes += len(value)
            assert total_bytes < 1337
            if value == '---':
        except EOFError:
    code = sanitize("/*ERR*/", '\n'.join(code).translate(trans_table))

    def sandbox():

        t=r=y = t=o = s=o=l=v=e = t=h=e = d=i=v=i=s=i=o=n = q=u=i=z = 0

        def exec_in_context(ctx):
            exec code in ctx
            print 'Flag is',
                assert FLAG != part1_of_flag
                print FLAG
                print '********************'

        def we_must_be_sure_flag_part1_is_ready():
            global FLAG
            FLAG = part1_of_flag

        def we_must_be_sure_flag_part2_is_ready():
            global FLAG
            FLAG += part2_of_flag

        def divider(v1):
            a = "You are lucky!"
            b = "Try again!"

            def divider(v2):
                i,t,s,  n,o,t,  s,o,  h,a,r,d
                if int(v1) / int(v2) == EXPECTED:
                    print a
                    print b
            return divider
        exec_in_context({'div': divider})


if __name__ == '__main__':

This program sanitizes our code by replacing specified strings with /*ERR*/:


It also only allows the following characters to be used:

' \n\r0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(),.:;<=>[]_{}'

It also clears the current built ins and replaces them with the "safe" variables:

True False type int

Our code is then executed with access to the divider function which calls we_must_be_sure_flag_part1_is_ready() and returns a different divider function. Divider(v2) checks if int(v1) / int(v2)  is EXPECTED, which was initialized to 13.37.

So, somehow magically we're supposed to make two 'integers' equal a float?
import black_magic

I first decided to attempt to create another object that inherits the int object.  I modified __int__ to simply return what its given.  I also modified __div__ to return the float 13.37 every time it is called.  I used type to create my new pseudo-integer class object.
def fint(self):    return self
def fdiv(self,o):    return 13.37

a = type('new_int', (int,), {'__div__': fdiv, '__int__': fint})

This allows so that when I create a new_int using an integer, a(1), performing int(new_int) will return an integer value, but still be of type new_int.  Using a float, a(1.0), would cause int(new_int) to return a float value and throw an exception. 

The jail doesn't allow me to enter the quote ( ' ) character sadly. Good thing there is a listof strings in div.func_code.co_freevars:
('d', 'h', 'i', 'n', 'o', 'r', 's', 't', 'we_must_be_sure_flag_part1_is_ready', 'we_must_be_sure_flag_part2_is_ready')
I can also get the 'v' character from div.func_name

But wait!  The plus (+) operator cannot be used AND replace(), join(), format(), and translate() cannot be used!!!

Good thing there is a ljust() function then!
The ljust function takes a given string and returns a modified string at least n wide and fills any extra characters with optionally given padding (default is space).

s = div.func_code.co_freevars
_div_ = s[8][2].ljust(2, s[8][2]).ljust(3, s[0]).ljust(4, s[2]).ljust(5, div.func_name[2]).ljust(6, s[8][2]).ljust(7, s[8][2])
_int_ = s[8][2].ljust(2, s[8][2]).ljust(3, s[2]).ljust(4, s[3]).ljust(5, s[7]).ljust(6, s[8][2]).ljust(7, s[8][2])

print _div_;print _int_

 After this it was just plug and play:

Welcome to pyjail!

Try to get the flag!
Use ctrl+D or --- to submit your code

def fint(self):return self
def fdiv(self,o):return 13.37
a=type(s[0],(int,),{_div_: fdiv,_int_:fint})

You are lucky!
Flag is 7hE_0w15_4R3_n07_wh47_7h3Y_533m--7hEr3_15_4_m4n_1n_a_5m111n9_649

1 comment: