UIUCTF - Are we out of the woods yet? Reversing 350p

CTF / April 30, 2017 • 4 min read

It looks like this python script was run through a custom packer. It's just Python*, which means it must be easy to reverse, right?

*v3.6.1:69c0db5

https://www.youtube.com/watch?v=y8qQsXpcZXA

This was fun little challenge that our team (https://chalmersctf.se) solved together. You are presented with the file packed.py that contains the following code:

import marshal, zlib, base64, itertools
def xor_strings(_left, _k):
    out = b''
    for l, r in zip(_left, itertools.cycle(_k)):
        out += (l ^ ord(r)).to_bytes(1, byteorder='big')
    return out

def YET_eval_code(p1, p2):
    YET_code = marshal.loads(zlib.decompress(xor_strings(base64.b64decode(p2), p1)))
    eval(YET_code)

YET_eval_code("YET", b'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')

We could quickly deduce that there was an embedded python program encoded with base64. In order to reveal the program we decompiled YET_code variable with dis.

import dis
print(dis.disassemble(YET_code))

This revealed more embedded data:

1           0 LOAD_CONST               0 (0)
            2 LOAD_CONST               1 (None)
            4 IMPORT_NAME              0 (marshal)
            6 STORE_NAME               0 (marshal)
            8 LOAD_CONST               0 (0)
           10 LOAD_CONST               1 (None)
           12 IMPORT_NAME              1 (zlib)
           14 STORE_NAME               1 (zlib)
           16 LOAD_CONST               0 (0)
           18 LOAD_CONST               1 (None)
           20 IMPORT_NAME              2 (base64)
           22 STORE_NAME               2 (base64)
           24 LOAD_CONST               0 (0)
           26 LOAD_CONST               1 (None)
           28 IMPORT_NAME              3 (itertools)
           30 STORE_NAME               3 (itertools)

2          32 LOAD_CONST               2 (<code object xor_strings at 0x10f9fbf60, file "YETpacked", line 2>)
           34 LOAD_CONST               3 ('xor_strings')
           36 MAKE_FUNCTION            0
           38 STORE_NAME               4 (xor_strings)

8          40 LOAD_CONST               4 (<code object CLEAR_eval_code at 0x10fa38930, file "YETpacked", line 8>)
           42 LOAD_CONST               5 ('CLEAR_eval_code')
           44 MAKE_FUNCTION            0
           46 STORE_NAME               5 (CLEAR_eval_code)

12          48 LOAD_NAME                5 (CLEAR_eval_code)
           50 LOAD_CONST               6 ('CLEAR')
           52 LOAD_CONST               7 (b'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')
           54 CALL_FUNCTION            2
           56 POP_TOP
           58 LOAD_CONST               1 (None)
           60 RETURN_VALUE
None

By substituting the YET_eval_code function call with the key CLEAR and with the above base64 encoded data, we get an other embedded python program. This process continues 3 more times until we reach the last and final program. This is how it looks like disassembled:

1           0 LOAD_CONST               0 (1338)
            2 STORE_NAME               0 (x0)

2           4 LOAD_CONST               1 (413)
            6 STORE_NAME               1 (x1)

3           8 LOAD_CONST               2 (3.5699)
           10 STORE_NAME               2 (r)

4          12 LOAD_NAME                0 (x0)
           14 STORE_NAME               3 (xn)

5          16 LOAD_NAME                1 (x1)
           18 STORE_NAME               4 (xk)

7          20 LOAD_CONST               3 ('')
           22 STORE_NAME               5 (accumulated)

9          24 LOAD_NAME                6 (input)
           26 LOAD_CONST               4 ('I N P U T: ')
           28 CALL_FUNCTION            1
           30 STORE_NAME               7 (user)

10          32 SETUP_EXCEPT            14 (to 48)

11          34 LOAD_NAME                8 (bytes)
           36 LOAD_ATTR                9 (fromhex)
           38 LOAD_NAME                7 (user)
           40 CALL_FUNCTION            1
           42 STORE_NAME               7 (user)
           44 POP_BLOCK
           46 JUMP_FORWARD            36 (to 84)

12     >>   48 DUP_TOP
           50 LOAD_NAME               10 (ValueError)
           52 COMPARE_OP              10 (exception match)
           54 POP_JUMP_IF_FALSE       82
           56 POP_TOP
           58 POP_TOP
           60 POP_TOP

13          62 LOAD_NAME               11 (print)
           64 LOAD_CONST               5 ('Non-hex byte entered!')
           66 CALL_FUNCTION            1
           68 POP_TOP

14          70 LOAD_NAME               12 (exit)
           72 LOAD_CONST              14 (-1)
           74 CALL_FUNCTION            1
           76 POP_TOP
           78 POP_EXCEPT
           80 JUMP_FORWARD             2 (to 84)
      >>   82 END_FINALLY

15     >>   84 LOAD_CONST               7 (b'\x8b*<LH~\xdc\xc4\xfc\xad\xff9\xe8h\x8d^\xf2\xc3\xa7\xc9&\x8f \xeaE_\xb0T\x05\xe5\xff\x9cD\x9e\x84\x13k\x0f~\xb5\x9cUm\x08\\')
           86 STORE_NAME              13 (flag)

16          88 SETUP_LOOP             152 (to 242)
           90 LOAD_NAME               14 (zip)
           92 LOAD_NAME               13 (flag)
           94 LOAD_NAME                7 (user)
           96 CALL_FUNCTION            2
           98 GET_ITER
      >>  100 FOR_ITER               138 (to 240)
          102 UNPACK_SEQUENCE          2
          104 STORE_NAME              15 (flagchar)
          106 STORE_NAME              16 (userchar)

17         108 LOAD_NAME                2 (r)
          110 LOAD_NAME                3 (xn)
          112 BINARY_MULTIPLY
          114 LOAD_CONST               6 (1)
          116 LOAD_NAME                3 (xn)
          118 BINARY_SUBTRACT
          120 BINARY_MULTIPLY
          122 LOAD_NAME                0 (x0)
          124 BINARY_MODULO
          126 STORE_NAME               3 (xn)

18         128 LOAD_NAME               17 (int)
          130 LOAD_NAME                3 (xn)
          132 LOAD_CONST              15 (100)
          134 BINARY_MULTIPLY
          136 CALL_FUNCTION            1
          138 LOAD_CONST              10 (255)
          140 BINARY_MODULO
          142 STORE_NAME               3 (xn)

20         144 LOAD_NAME                2 (r)
          146 LOAD_NAME                4 (xk)
          148 BINARY_MULTIPLY
          150 LOAD_CONST               6 (1)
          152 LOAD_NAME                4 (xk)
          154 BINARY_SUBTRACT
          156 BINARY_MULTIPLY
          158 LOAD_NAME                1 (x1)
          160 BINARY_MODULO
          162 STORE_NAME               4 (xk)

21         164 LOAD_NAME               17 (int)
          166 LOAD_NAME                4 (xk)
          168 LOAD_CONST              16 (100)
          170 BINARY_MULTIPLY
          172 CALL_FUNCTION            1
          174 LOAD_CONST              10 (255)
          176 BINARY_MODULO
          178 STORE_NAME               4 (xk)

23         180 LOAD_NAME               18 (chr)
          182 LOAD_NAME                3 (xn)
          184 LOAD_NAME               15 (flagchar)
          186 BINARY_XOR
          188 CALL_FUNCTION            1
          190 STORE_NAME              19 (flagbyte)

24         192 LOAD_NAME               18 (chr)
          194 LOAD_NAME                4 (xk)
          196 LOAD_NAME               16 (userchar)
          198 BINARY_XOR
          200 CALL_FUNCTION            1
          202 STORE_NAME              20 (userbyte)

26         204 LOAD_NAME               19 (flagbyte)
          206 LOAD_NAME               20 (userbyte)
          208 COMPARE_OP               3 (!=)
          210 POP_JUMP_IF_FALSE      230

27         212 LOAD_NAME               11 (print)
          214 LOAD_CONST              11 ('Still in the woods')
          216 CALL_FUNCTION            1
          218 POP_TOP

28         220 LOAD_NAME               12 (exit)
          222 LOAD_CONST              17 (-1)
          224 CALL_FUNCTION            1
          226 POP_TOP
          228 JUMP_ABSOLUTE          100

30     >>  230 LOAD_NAME                5 (accumulated)
          232 LOAD_NAME               20 (userbyte)
          234 INPLACE_ADD
          236 STORE_NAME               5 (accumulated)
          238 JUMP_ABSOLUTE          100
      >>  240 POP_BLOCK

32     >>  242 LOAD_NAME               11 (print)
          244 LOAD_CONST              12 ('Looking at it now, it all seems so simple:')
          246 LOAD_NAME                5 (accumulated)
          248 CALL_FUNCTION            2
          250 POP_TOP
          252 LOAD_CONST              13 (None)
          254 RETURN_VALUE
None

We managed to reverse engineer the above output into the following python source:

import sys

x0 = 1338
x1 = 413
r = 3.5699
xn = x0
xk = x1

accumulated = ''

#user = input("I N P U T: ")
# user = bytes.fromhex(user)
# if not user:
#     print('Non-hex byte entered!')
#     sys.exit()


flag = b'\x8b*<LH~\xdc\xc4\xfc\xad\xff9\xe8h\x8d^\xf2\xc3\xa7\xc9&\x8f \xeaE_\xb0T\x05\xe5\xff\x9cD\x9e\x84\x13k\x0f~\xb5\x9cUm\x08\\'

for flagchar in flag:
    xn = ((r * xn) * (1 - xn)) % x0
    xn = int(xn * 100) % 255

    #xk = ((xk * r) * (1 - xk)) % x1
    #xk = (int(xk * 100)) % 255

    flagbyte = chr(xn ^ flagchar)
    #userbyte = chr(xk ^ userchar)

    # if flagbyte != userbyte:
    #     print('Still in the woods')
    #     sys.exit()
    # else:
    accumulated += flagbyte

print('Looking at it now, it all seems so simple: {}'.format(accumulated))

Which revealed:

Looking at it now, it all seems so simple: flag{th3_m0nst3rs_turned_0ut_2_be_ju5t_tr33s}

For more writeups visit https://github.com/ChalmersCTF/Writeups and don’t forget to check out my friend’s and fellow team member’s website https://www.nindoda.com/.