Monday, September 10, 2018

HackIT CTF 2018 - PyCry Writeup

This challenge was a teamwork challenge.

First, @diofeher discovered that we could run python on the server by sending code to the second input. He found this:

<< print locals()
<< {'funcwtf': <function funcwtf at 0x7f22e83de758>, 'string': <module 'string' from '/usr/lib/python2.7/string.pyc'>, 'name': 'test', 'hint': <function hint at 0x7f22e83de848>, '__builtins__': <module '__builtin__' (built-in)>, 'func4e': <function func4e at 0x7f22e83de7d0>, '__file__': '/home/pychal/chal.py', 'random': <module 'random' from '/usr/lib/python2.7/random.pyc'>, 'func3e': <function func3e at 0x7f22e83de6e0>, '__package__': None, 'sys': <module 'sys' (built-in)>, 'func1e': <function func1e at 0x7f22e83cd230>, '__name__': '__main__', 'func2e': <function func2e at 0x7f22e83de668>, 'os': <module 'os' from '/usr/lib/python2.7/os.pyc'>, '__doc__': None, 'drunkenc': <function drunkenc at 0x7f22e83de8c0>, 'dis': <module 'dis' from '/usr/lib/python2.7/dis.pyc'>}

Inside the hint function he found some guidance:

<< print locals().__getitem__('hint')().func_code
<< The flag was encrypted using func2e(func4e(func3e(func1e('flag{redacted}','a6105c0a611b41b08f1209506350279e'),'looooool')))
<< I think the output was : 0A3BFDA6EBFD5CEBFD1ADBFDBEDBFD1DFBFDF10CFD51FBFD51FBFDBEEBFDB3ABFD3DABFD2DABFD589BFDD79BFD9E9BFD10ABFDDFBBFDAFBBFDD4CBFD77CBFD42BBFD91BBFDC2BBFDB4BBFDE7BBFDB6BBFDF0BBFD2ABBFD22BBFD39BBFDE2BBFDB1CE
<< You guessed right, your goal is to retrieve the flag using your python, reversing and crypto skills
<< Good luck!

Then, he disassembled func1e, func2e, func3e, func4e and funcwtf for us:

<< print __builtins__.__import__('dis').dis(func1e)
<<  10           0 LOAD_CONST               1 ('')
<<               3 STORE_FAST               2 (tmp)
<<
<<  11           6 SETUP_LOOP             133 (to 142)
<<               9 LOAD_GLOBAL              0 (range)
<<              12 LOAD_GLOBAL              1 (len)
<<              15 LOAD_FAST                0 (str1)
<<              18 CALL_FUNCTION            1
<<              21 CALL_FUNCTION            1
<<              24 GET_ITER
<<         >>   25 FOR_ITER               113 (to 141)
<<              28 STORE_FAST               3 (i)
<<
<<  12          31 LOAD_FAST                3 (i)
<<              34 LOAD_CONST               2 (2)
<<              37 BINARY_MODULO
<<              38 LOAD_CONST               3 (0)
<<              41 COMPARE_OP               2 (==)
<<              44 POP_JUMP_IF_FALSE       94
<<
<<  13          47 LOAD_FAST                2 (tmp)
<<              50 LOAD_GLOBAL              2 (chr)
<<              53 LOAD_GLOBAL              3 (ord)
<<              56 LOAD_FAST                0 (str1)
<<              59 LOAD_FAST                3 (i)
<<              62 BINARY_SUBSCR
<<              63 CALL_FUNCTION            1
<<              66 LOAD_GLOBAL              3 (ord)
<<              69 LOAD_FAST                1 (str2)
<<              72 LOAD_FAST                3 (i)
<<              75 BINARY_SUBSCR
<<              76 CALL_FUNCTION            1
<<              79 BINARY_XOR
<<              80
<<  LOAD_CONST               4 (4)
<<              83 BINARY_ADD
<<              84 CALL_FUNCTION            1
<<              87 INPLACE_ADD
<<              88 STORE_FAST               2 (tmp)
<<              91 JUMP_ABSOLUTE           25
<<
<<  15     >>   94 LOAD_FAST                2 (tmp)
<<              97 LOAD_GLOBAL              2 (chr)
<<             100 LOAD_GLOBAL              3 (ord)
<<             103 LOAD_FAST                0 (str1)
<<             106 LOAD_FAST                3 (i)
<<             109 BINARY_SUBSCR
<<             110 CALL_FUNCTION            1
<<             113 LOAD_GLOBAL              3 (ord)
<<             116 LOAD_FAST                1 (str2)
<<             119 LOAD_FAST                3 (i)
<<             122 BINARY_SUBSCR
<<             123 CALL_FUNCTION            1
<<             126 BINARY_XOR
<<             127 LOAD_CONST               2 (2)
<<             130 BINARY_SUBTRACT
<<             131 CALL_FUNCTION            1
<<             134 INPLACE_ADD
<<             135 STORE_FAST               2 (tmp)
<<             138 JUMP_ABSOLUTE           25
<<         >>  141 POP_BLOCK
<<
<<  16     >>  142 LOAD_FAST                2 (tmp)
<<             145 LOAD_CONST               0 (None)
<<             148 LOAD_CONST               0 (None)
<<             151 LOAD_CONST               5 (-1)
<<             154 BUILD_SLICE              3
<<             157 BINARY_SUBSCR
<<
<<             158 RETURN_VALUE
<< None


<< print __builtins__.__import__('dis').dis(func3e)
<<
<<  37           0 BUILD_LIST               0
<<               3 STORE_FAST               2 (encryped)
<<
<<  38           6 SETUP_LOOP              91 (to 100)
<<               9 LOAD_GLOBAL              0 (enumerate)
<<              12 LOAD_FAST                0 (msg)
<<              15 CALL_FUNCTION            1
<<              18 GET_ITER
<<         >>   19 FOR_ITER                77 (to 99)
<<              22 UNPACK_SEQUENCE          2
<<              25 STORE_FAST               3 (i)
<<              28 STORE_FAST               4 (c)
<<
<<  39          31 LOAD_GLOBAL              1 (ord)
<<              34 LOAD_FAST                1 (key)
<<              37 LOAD_FAST                3 (i)
<<              40 LOAD_GLOBAL              2 (len)
<<              43 LOAD_FAST                1 (key)
<<              46 CALL_FUNCTION            1
<<              49 BINARY_MODULO
<<              50 BINARY_SUBSCR
<<              51 CALL_FUNCTION            1
<<              54 STORE_FAST               5 (key_c)
<<
<<  40          57 LOAD_GLOBAL              1 (ord)
<<              60 LOAD_FAST                4 (c)
<<              63 CALL_FUNCTION            1
<<              66 STORE_FAST               6 (msg_c)
<<
<<  41          69 LOAD_FAST                2 (encryped)
<<              72 LOAD_ATTR                3 (append)
<<              75 LOAD_GLOBAL              4 (chr)
<<              78 LOAD_FAST                6 (msg_c)
<<              81 LOAD_FAST                5 (key_c)
<<              84 BINARY_ADD
<<              85 LOAD_CONST               1 (127)
<<              88 BINARY_MODULO
<<              89 CALL_FUNCTION            1
<<              92 CALL_FUNCTION            1
<<              95 POP_TOP
<<              96 JUMP_ABSOLUTE           19
<<         >>   99 POP_BLOCK
<<
<<  42     >>  100 LOAD_CONST               2 ('')
<<             103 LOAD_ATTR                5 (join)
<<             106 LOAD_FAST                2 (encryped)
<<             109 CALL_FUNCTION            1
<<             112 RETURN_VALUE
<< None
<<

<< print __builtins__.__import__('dis').dis(funcwtf)
<<
<<  47           0 LOAD_FAST                0 (n)
<<               3 LOAD_CONST               1 (0)
<<               6 COMPARE_OP               4 (>)
<<               9 POP_JUMP_IF_FALSE       25
<<
<<  48          12 LOAD_FAST                0 (n)
<<              15 LOAD_CONST               2 (20)
<<              18 BINARY_MODULO
<<              19 STORE_FAST               0 (n)
<<              22 JUMP_FORWARD            12 (to 37)
<<
<<  50     >>   25 LOAD_FAST                0 (n)
<<              28 UNARY_NEGATIVE
<<              29 LOAD_CONST               2 (20)
<<              32 BINARY_MODULO
<<              33 UNARY_NEGATIVE
<<              34 STORE_FAST               0 (n)
<<
<<  51     >>   37 LOAD_GLOBAL              0 (string)
<<              40 LOAD_ATTR                1 (ascii_lowercase)
<<              43 STORE_FAST               1 (lc)
<<
<<  52          46 LOAD_GLOBAL              0 (string)
<<              49 LOAD_ATTR                2 (ascii_uppercase)
<<              52 STORE_FAST               2 (uc)
<<
<<  53          55 LOAD_GLOBAL              0 (string)
<<              58 LOAD_ATTR                3 (maketrans)
<<              61 LOAD_FAST                1 (lc)
<<              64 LOAD_FAST                2 (uc)
<<              67 BINARY_ADD
<<
<<  54          68 LOAD_FAST                1 (lc)
<<              71 LOAD_FAST                0 (n)
<<              74 SLICE+1
<<              75 LOAD_FAST                1 (lc)
<<              78 LOAD_FAST                0 (n)
<<              81 SLICE+2
<<              82 BINARY_ADD
<<              83 LOAD_FAST                2 (uc)
<<              86 LOAD_FAST                0 (n)
<<              89 SLICE+1
<<              90 BINARY_ADD
<<              91 LOAD_FAST                2 (uc)
<<              94 LOAD_FAST                0 (n)
<<              97 SLICE+2
<<              98 BINARY_ADD
<<              99 CALL_FUNCTION            2
<<             102 STORE_DEREF              0 (trans)
<<
<<  55         105 LOAD_CLOSURE             0 (trans)
<<             108 BUILD_TUPLE              1
<<             111 LOAD_CONST               3 (<code object <lambda> at 0x7f78b15b80b0, file "/home/pychal/chal.py", line 55>)
<<             114 MAKE_CLOSURE             0
<<             117 RETURN_VALUE
<< None
<<

<< print __builtins__.__import__('dis').dis(func2e)
<<
<<  21           0 SETUP_EXCEPT           212 (to 215)
<<
<<  22           3 LOAD_GLOBAL              0 (random)
<<               6 LOAD_ATTR                1 (randint)
<<               9 LOAD_CONST               1 (1)
<<              12 LOAD_CONST               2 (1024)
<<              15 CALL_FUNCTION            2
<<              18 STORE_FAST               1 (k)
<<
<<  23          21 LOAD_CONST               3 (0)
<<              24 STORE_FAST               2 (n)
<<
<<  24          27 LOAD_CONST               4 ('')
<<              30 STORE_FAST               3 (f)
<<
<<  25          33 SETUP_LOOP             116 (to 152)
<<              36 LOAD_GLOBAL              2 (range)
<<              39 LOAD_GLOBAL              3 (len)
<<              42 LOAD_FAST                0 (d)
<<              45 CALL_FUNCTION            1
<<              48 CALL_FUNCTION            1
<<              51 GET_ITER
<<         >>   52 FOR_ITER                96 (to 151)
<<              55 STORE_FAST               4 (i)
<<
<<  26          58 LOAD_FAST                2 (n)
<<              61 LOAD_CONST               1 (1)
<<              64 INPLACE_ADD
<<              65 STORE_FAST               2 (n)
<<
<<  27          68 LOAD_FAST                2 (n)
<<              71 LOAD_FAST                2 (n)
<<              74 BINARY_MULTIPLY
<<              75 LOAD_CONST               5 (62)
<<              78 BINARY_XOR
<<              79 STORE_FAST
<<   5 (c)
<<
<<  28          82 LOAD_FAST                3 (f)
<<              85 LOAD_CONST               6 ('00000')
<<              88 LOAD_GLOBAL              4 (hex)
<<              91 LOAD_GLOBAL              5 (int)
<<              94 LOAD_GLOBAL              6 (oct)
<<              97 LOAD_GLOBAL              7 (ord)
<<             100 LOAD_FAST                0 (d)
<<             103 LOAD_FAST                4 (i)
<<             106 BINARY_SUBSCR
<<             107 CALL_FUNCTION            1
<<             110 LOAD_FAST                1 (k)
<<             113 LOAD_CONST               7 (720451)
<<             116 BINARY_XOR
<<             117 LOAD_CONST               8 (3775139)
<<             120 BINARY_XOR
<<             121 LOAD_FAST                5 (c)
<<             124 BINARY_XOR
<<             125 BINARY_XOR
<<             126 CALL_FUNCTION            1
<<             129 CALL_FUNCTION            1
<<             132 CALL_FUNCTION            1
<<             135 LOAD_CONST               9 (2)
<<             138 SLICE+1
<<             139 BINARY_ADD
<<             140 LOAD_CONST              10 (-6)
<<             143 SLICE+1
<<             144 INPLACE_ADD
<<             145 STORE_FAST               3 (f)
<<             148 JUMP_ABSOLUTE           52
<<         >>  151 POP_BLOCK
<<
<<  29     >>  152 LOAD_CONST              11 ('000')
<<             155 L
<< OAD_GLOBAL              4 (hex)
<<             158 LOAD_FAST                1 (k)
<<             161 LOAD_CONST              12 (2719)
<<             164 BINARY_XOR
<<             165 LOAD_CONST              13 (59262)
<<             168 BINARY_XOR
<<             169 CALL_FUNCTION            1
<<             172 LOAD_CONST               9 (2)
<<             175 SLICE+1
<<             176 BINARY_ADD
<<             177 LOAD_CONST              14 (-4)
<<             180 SLICE+1
<<             181 LOAD_FAST                3 (f)
<<             184 BINARY_ADD
<<             185 LOAD_CONST               0 (None)
<<             188 LOAD_CONST               0 (None)
<<             191 LOAD_CONST              15 (-1)
<<             194 BUILD_SLICE              3
<<             197 BINARY_SUBSCR
<<             198 LOAD_ATTR                8 (upper)
<<             201 CALL_FUNCTION            0
<<             204 STORE_FAST               3 (f)
<<
<<  30         207 LOAD_FAST                3 (f)
<<             210 RETURN_VALUE
<<             211 POP_BLOCK
<<             212 JUMP_FORWARD             8 (to 223)
<<
<<  31     >>  215 POP_TOP
<<             216 POP_TOP
<<             217 POP_TOP
<<
<<  32         218 LOAD_CONST              15 (-1)
<<             221 RETURN_VALUE
<<             222 END_FINALLY
<<         >>  223 LOAD_CONST               0 (None)
<<             226 RETURN_VALUE
<< None
<<

<< print __builtins__.__import__('dis').dis(func4e)
<<
<<  59           0 LOAD_GLOBAL              0 (funcwtf)
<<               3 LOAD_CONST               1 (1337)
<<               6 CALL_FUNCTION            1
<<               9 STORE_FAST               1 (enc)
<<
<<  60          12 LOAD_FAST                1 (enc)
<<              15 LOAD_FAST                0 (str)
<<              18 CALL_FUNCTION            1
<<              21 RETURN_VALUE
<< None
<<

After about 3 hours of hard, manual work, @diofeher and I managed to decompile this ugly thing:

def func1e(str1, str2):
    tmp = ''
    for i in range(len(str1)):
        if i % 2 == 0:
            tmp += chr((ord(str1[i]) ^ ord(str2[i])) + 4)
        else:
            tmp += chr((ord(str1[i]) ^ ord(str2[i])) - 2)

    return tmp[::-1]

def func2e(d):
    try:
        k = random.randint(1, 1024)
        n = 0
        f = ''

        for i in range(len(d)):
            n += 1
            c = (n*n) ^ 62
            f += ('00000' + hex(int(oct(ord(d[i]) ^ (k ^ 720451 ^ 3775139 ^ c))))[2:])[-6:]

        f = (('000' + hex((k ^ 2719) ^ 59262)[2:])[-4:] + f)[::-1].upper()
        return f
    except:
        return -1

def func3e(msg, key):
    encryped = []
    for i, c in enumerate(msg):
        key_c = ord(key[i % len(key)])
        msg_c = ord(c)

        encryped.append(chr((msg_c + key_c) % 127))

    return ''.join(encryped)

def func4e(str_):
    enc = funcwtf(1337)
    return enc(str_)

def funcwtf(n):
    if n > 0:
        n = n % 20
    else:
        n = -(-n % 20)

    lc = string.ascii_lowercase
    uc = string.ascii_uppercase

    trans = string.maketrans(lc + uc, lc[n:] + lc[:n] + uc[n:] + uc[:n])
    return lambda x: x.translate(trans)

I quickly wrote functions to reverse func1e, func3e and func4e, but we were left with func2e, which had a random component. Luckily, @Alisson googled some pieces of it and found out it was stypr and that the random component doesn’t matter at all. We grabbed the decrypt function from here.

Final exploit:

#! /usr/bin/env python
import string

def func1e(str1, str2):
    tmp = ''
    for i in range(len(str1)):
        if i % 2 == 0:
            tmp += chr((ord(str1[i]) ^ ord(str2[i])) + 4)
        else:
            tmp += chr((ord(str1[i]) ^ ord(str2[i])) - 2)

    return tmp[::-1]

def func1erev(str1, str2):
    tmp = ''
    str1 = str1[::-1]
    for i in range(len(str1)):
        if i % 2 == 0:
            tmp += chr((ord(str1[i]) - 4) ^ ord(str2[i]))
        else:
            tmp += chr((ord(str1[i]) + 2) ^ ord(str2[i]))

    return tmp

def func2ek(d, k):
    try:
        n = 0
        f = ''

        for i in range(len(d)):
            n += 1
            c = (n*n) ^ 62
            f += ('00000' + hex(int(oct(ord(d[i]) ^ (k ^ 720451 ^ 3775139 ^ c))))[2:])[-6:]

        f = (('000' + hex((k ^ 2719) ^ 59262)[2:])[-4:] + f)[::-1].upper()
        return f
    except:
        return -1

def func2erev(d):
    try:
        e = d[::-1]
        k = int(e[:4], 16) ^ 0xA9F ^ 0xE77E
        t = e[4:]
        f = ""
        n = 0
        for i in range(0, len(t), 6):
            n += 1
            c = (n * n) ^ 0x3E
            # $tmp = $tmp . chr(octdec(hexdec(substr($Temps, $i, 6))) ^ ($MyKey ^ hexdec("AFE43") ^ hexdec("399AA3") ^ $cal));
            f += chr(int(str(int(t[i:i+6], 16)), 8) ^ (k ^ 0xAFE43 ^ 0x399AA3 ^ c))
        return f
    except:
        return -1

def func3e(msg, key):
    encryped = []
    for i, c in enumerate(msg):
        key_c = ord(key[i % len(key)])
        msg_c = ord(c)

        encryped.append(chr((msg_c + key_c) % 127))

    return ''.join(encryped)

def func3erev(msg, key):
    encryped = []
    for i, c in enumerate(msg):
        key_c = ord(key[i % len(key)])
        msg_c = ord(c)

        encryped.append(chr((msg_c - key_c) % 127))

    return ''.join(encryped)

def func4e(str_):
    enc = funcwtf(1337)
    return enc(str_)

def func4erev(str_):
    dec = funcwtfrev(1337)
    return dec(str_)

def funcwtf(n):
    if n > 0:
        n = n % 20
    else:
        n = -(-n % 20)

    lc = string.ascii_lowercase
    uc = string.ascii_uppercase

    trans = string.maketrans(lc + uc, lc[n:] + lc[:n] + uc[n:] + uc[:n])
    return lambda x: x.translate(trans)

def funcwtfrev(n):
    if n > 0:
        n = n % 20
    else:
        n = -(-n % 20)

    lc = string.ascii_lowercase
    uc = string.ascii_uppercase

    trans = string.maketrans(lc[n:] + lc[:n] + uc[n:] + uc[:n], lc + uc)
    return lambda x: x.translate(trans)

if __name__ == '__main__':
    out = '0A3BFDA6EBFD5CEBFD1ADBFDBEDBFD1DFBFDF10CFD51FBFD51FBFDBEEBFDB3ABFD3DABFD2DABFD589BFDD79BFD9E9BFD10ABFDDFBBFDAFBBFDD4CBFD77CBFD42BBFD91BBFDC2BBFDB4BBFDE7BBFDB6BBFDF0BBFD2ABBFD22BBFD39BBFDE2BBFDB1CE'

    print func1erev(func3erev(func4erev(func2erev(out)), 'looooool'), 'a6105c0a611b41b08f1209506350279e')

Capture the Flag , Miscellaneous , Writeup