UFOCTF 2017: decompiling Python in King Arthur's quest (PPC600)


Greetings habrayuzer! Recently, the annual UFO CTF 2017 Information Security Olympiad has ended. In this article, there will be a single task wright from the PPC section called “King Arthur” , for which you could get the maximum number of points - 600.

Let's start


In the description of the assignment was the following:
We got some weird email attachment from a knight fan. Help me figure out what the author had in mind.

And the file was available for download:

sword.py
#!/usr/bin/python
import serpent
"""
           ___
          { _ }
           |/|
          {___}
           |_|
           |/|
 .         |/|         .
 (\________|w|________/)
 ( ___________________ )
  v       | | |       v
          | | |
          | | |
          | | |PO0.HN7
          | | |   *   TP0~~~<
         z| | |ON4.YH1
       OP6| | |
         ON2| |
          |PO3|
          | |PY0
          | | PY0
          | | |PY0
         z| | |PY0
        PY0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |PH0
          | | PY0
          | | |PY0
         z| | |PY0
        PY0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |HN3
          | | PY1
          | | |PY0
         z| | |PY0
        PY0 | |z
       PN3| | |
         PT0| |
          |PY0|
          | |PN3
          | | PY0
          | | |PY0
         z| | |TH3
        PY0 | |z
       PY0| | |
         NY2| |
          |PY0|
          | |PY0
          | | PN3
          | | |PH0
         z| | |PY0
        PN4 | |z
       PY0| | |
         PY0| |
          |NY2|
          | |PT0
          | | PY0
          | | |YP3
         z| | |PT0
        PY0 | |z
       PO4| | |
         PY0| |
          |PY0|
          | |PT0
          | | PN3
          | | |PY0
         z| | |PY0
        HN2 | |z
       YO1| | |
         PO0| |
          |PY0|
          | |PY0
          | | PY0
          | | |TN2
         z| | |YN3
        YY7 | |z
       YY7| | |
         YY7| |
          |YY7|
          | |PO3
          | | PY0
          | | |PY0
         z| | |PY0
        PY0 | |z
       PH0| | |
         PY0| |
          |PY0|
          | |PY0
          | | HO0
          | | |PY0
         z| | |PY0
        PY0 | |z
       PO0| | |
         PY0| |
          |PY0|
          | |PY0
          | | HN3
          | | |YY4
         z| | |PY0
        PY0 | |z
       PY0| | |
         OP3| |
          |PY0|
          | |PY0
          | | PN3
          | | |PT0
         z| | |PY0
        PO4 | |z
       PT0| | |
         PY0| |
          |NO3|
          | |PT0
          | | PY0
          | | |NH3
         z| | |PT0
        PY0 | |z
       PN3| | |
         PY0| |
          |PY0|
          | |PN3
          | | PH0
          | | |PY0
         z| | |PN3
        PO0 | |z
       PY0| | |
         YP4| |
          |PO0|
          | |PY0
          | | NP0
          | | |PN3
         z| | |PN0
        PY0 | |z
       TY3| | |
         PO0| |
          |PY0|
          | |HO3
          | | TO1
          | | |PY0
         z| | |PN3
        YP0 | |z
       PY0| | |
         YH2| |
          |YO2|
          | |TN3
          | | TN3
          | | |PY0
         z| | |NH3
        PT0 | |z
       PY0| | |
         PN3| |
          |PH0|
          | |PY0
          | | PN3
          | | |PY0
         z| | |PY0
        PN3 | |z
       PO0| | |
         PY0| |
          |YP4|
          | |PO0
          | | PY0
          | | |NP0
         z| | |PN3
        YT0 | |z
       PY0| | |
         TY3| |
          |PO0|
          | |PY0
          | | HO3
          | | |HO3
         z| | |PY0
        PN3 | |z
       YP0| | |
         PY0| |
          |YH2|
          | |YO2
          | | TN3
          | | |TO2
         z| | |PY0
        NH3 | |z
       PT0| | |
         PY0| |
          |PN3|
          | |YH0
          | | PY0
          | | |TY0
         z| | |PN3
        YO0 | |z
       PY0| | |
         TY0| |
          |PN3|
          | |PO0
          | | PY0
          | | |TY0
         z| | |YP4
        PO0 | |z
       PY0| | |
         NP0| |
          |PN3|
          | |YN0
          | | PY0
          | | |TY3
         z| | |PO0
        PY0 | |z
       HO3| | |
         OT4| |
          |PY0|
          | |PN3
          | | YP0
          | | |PY0
         z| | |YH2
        YO2 | |z
       TN3| | |
         YN1| |
          |PY0|
          | |NH3
          | | PT0
          | | |PY0
         z| | |PN3
        TP0 | |z
       PY0| | |
         TY0| |
          |PN3|
          | |PH0
          | | PY0
          | | |TY0
         z| | |PN3
        PO0 | |z
       PY0| | |
         TY0| |
          |YP4|
          | |PO0
          | | PY0
          | | |NP0
         z| | |PN3
        TY0 | |z
       PY0| | |
         TY3| |
          |PO0|
          | |PY0
          | | HO3
          | | |TN2
         z| | |PY0
        PN3 | |z
       YP0| | |
         PY0| |
          |YH2|
          | |YO2
          | | TN3
          | | |YP0
         z| | |PY0
        PN3 | |z
       TH0| | |
         PY0| |
          |YH2|
          | |YO2
          | | PN3
          | | |PY0
         z| | |PY0
        HN2 | |z
       YO1| | |
         TO0| |
          |PY0|
          | |PY0
          | | PY0
          | | |TN2
         z| | |HN3
        TO0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |YP2
          | | TN3
          | | |OP3
         z| | |YP3
        HO3 | |z
       PY1| | |
         TH3| |
          |YN3|
          | |TN3
          | | YP3
          | | |NY1
         z| | |PY1
        TP0 | |z
       YN3| | |
         TN0| |
          |PY0|
          | |PY0
          | | PY0
          | | |YN3
         z| | |PH0
        PY0 | |z
       PY0| | |
         PY0| |
          |OP3|
          | |YH0
          | | PY0
          | | |PY0
         z| | |PY0
        ON2 | |z
       TO2| | |
         YH3| |
          |NP1|
          | |OT3
          | | OT1
          | | |OT1
         z| | |HN3
        YP0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |YT2
          | | PT3
          | | |YN3
         z| | |TH3
        PT1 | |z
       OP3| | |
         YO0| |
          |PY0|
          | |PY0
          | | PY0
          | | |NP3
         z| | |PH2
        YT3 | |z
       PH2| | |
         YH3| |
          |NP1|
          | |OT3
          | | PT3
          | | |YN3
         z| | |HP0
        PY0 | |z
       PY0| | |
         PY0| |
          |YN3|
          | |PP0
          | | PY0
          | | |PY0
         z| | |PY0
        OP3 | |z
       YO0| | |
         PY0| |
          |PY0|
          | |PY0
          | | NP2
          | | |HT1
         z| | |HY2
        ON2 | |z
       HT3| | |
         YH3| |
          |YH3|
          | |PH2
          | | YN3
          | | |PT0
         z| | |PY0
        PY0 | |z
       PY0| | |
         OP3| |
          |YH0|
          | |PY0
          | | PY0
          | | |PY0
         z| | |HN1
        OH2 | |z
       OH1| | |
         OP1| |
          |TY3|
          | |YO3
          | | OH3
          | | |HN3
         z| | |YO0
        PY0 | |z
       PY0| | |
         PY0| |
          |HN2|
          | |OY3
          | | PO3
          | | |PO3
         z| | |YP3
        HN3 | |z
       HN3| | |
         PT1| |
          |YO1|
          | |PH0
          | | PY0
          | | |PY0
         z| | |PY0
        OP3 | |z
       YN0| | |
         PY0| |
          |PY0|
          | |PY0
          | | HO3
          | | |PT3
         z| | |OH3
        YY2 | |z
       YN3| | |
         TN3| |
          |HY3|
          | |OY3
          | | OP3
          | | |OP3
         z| | |PN0
        PY0 | |z
       PY0| | |
         PY0| |
          |TH3|
          | |YN3
          | | TN3
          | | |YP3
         z| | |YO1
        PH0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |HO2
          | | PN0
          | | |PY0
         z| | |PY0
        PY0 | |z
       HO2| | |
         YP0| |
          |PY0|
          | |PY0
          | | PY0
          | | |YO1
         z| | |PY0
        PY0 | |z
       PY0| | |
         PY0| |
          |YO1|
          | |PY0
          | | PY0
          | | |PY0
         z| | |PY0
        HN3 | |z
       HP2| | |
         PY0| |
          |PY0|
          | |PY0
          | | PN2
          | | |NY1
         z| | |NH2
        PN2 | |z
       HP3| | |
         OH3| |
          |TN3|
          | |TH3
          | | HP3
          | | |PT3
         z| | |PN3
        HN3 | |z
       NH2| | |
         OY2| |
          |YT2|
          | |HP2
          | | PO2
          | | |OP2
         z| | |YT2
        NH2 | |z
       OP2| | |
         PT2| |
          |HN2|
          | |TY2
          | | HN2
          | | |NH2
         z| | |HN3
        YP3 | |z
       HO3| | |
         HY3| |
          |YP3|
          | |TN3
          | | OP3
          | | |NH2
         z| | |HN3
        HP3 | |z
       OY3| | |
         HO3| |
          |PO3|
          | |YP3
          | | HN3
          | | |NH2
         z| | |HN2
        YP3 | |z
       HO3| | |
         HY3| |
          |YP3|
          | |TN3
          | | OP3
          | | |TO1
         z| | |TO3
        PT3 | |z
       HN3| | |
         OP3| |
          |YP3|
          | |HO3
          | | NH2
          | | |HN2
         z| | |YP3
        HO3 | |z
       HY3| | |
         YP3| |
          |TN3|
          | |OP3
          | | TO1
          | | |TO3
         z| | |PT3
        HN3 | |z
       OP3| | |
         YP3| |
          |HO3|
          | |NH2
          | | OP3
          | | |YP3
         z| | |HN3
        OP3 | |z
       TN1| | |
         HY3| |
          |NP3|
          | |HN3
          | | PN0
          | | |PY0
         z| | |PY0
        PY0 | |z
       OP3| | |
         PT3| |
          |HN3|
          | |TY3
          | | PN0
          | | |PY0
         z| | |PY0
        PY0 | |z
       HN3| | |
         HY0| |
          |PY0|
          | |PY0
          | | PY0
          | | |PY0
         z| | |PT0
        TH0 | |z
       PT0| | |
         NP0| |
          |PT0|
          | |YO0
          | | PT0
          | | |NP0
         z| | |PT0
        YO0 | |z
       PO0| | |
         NH0| |
          |PT0|
          | |TH1
          | | PH0
          | | |YO1
         z| | |PH0
        PY0 | |z
       PY0| | |
         PY0| |
          |OP3|
          | |YH0
          | | PY0
          | | |PY0
         z| | |PY0
        HN3 | |z
       YP3| | |
         HO3| |
          |HY3|
          | |YP3
          | | TN3
          | | |OP3
         z| | |OP3
        PN0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |OP3
          | | PT3
          | | |HN3
         z| | |TY3
        YO1 | |z
       PH0| | |
         PY0| |
          |PY0|
          | |PY0
          | | HO2
          | | |YT0
         z| | |PY0
        PY0 | |z
       PY0| | |
         HO2| |
          |YH0|
          | |PY0
          | | PY0
          | | |PY0
         z| | |YO1
        PY0 | |z
       PY0| | |
         PY0| |
          |PY0|
          | |YO1
          | | PY0
          | | |PY0
         z| | |PY0
        PY0 | |z
       HN3| | |
         HP2| |
          |PY0|
          | |PY0
          | | PY0
          | | |PN2
         z| | |NY1
        NH2 | |z
       PN2| | |
         HP3| |
          |OH3|
          | |TN3
          | | TH3
          | | |HP3
         z| | |PT3
        PN3 | |z
       HN3| | |
         NH2| |
          |OY2|
          | |YT2
          | | HP2
          | | |PO2
         z| | |OP2
        YT2 | |z
       NH2| | |
         OP2| |
          |PT2|
          | |HN2
          | | TY2
          | | |HN2
         z| | |NH2
        HN3 | |z
       YP3| | |
         HO3| |
          |HY3|
          | |YP3
          | | TN3
          | | |OP3
         z| | |NH2
        HN3 | |z
       HP3| | |
         OY3| |
          |HO3|
          | |PO3
          | | YP3
          | | |HN3
         z| | |NH2
        HN2 | |z
       YP3| | |
         HO3| |
          |HY3|
          | |YP3
          | | TN3
          | | |OP3
         z| | |TO1
        TO3 | |z
       PT3| | |
         HN3| |
          |OP3|
          | |YP3
          | | HO3
          | | |NH2
         z| | |HN2
        YP3 | |z
       HO3| | |
         HY3| |
          |YP3|
          | |TN3
          | | OP3
          | | |TO1
         z| | |TO3
        PT3 | |z
       HN3| | |
         OP3| |
          |YP3|
          | |HO3
          | | NH2
          | | |OP3
         z| | |YP3
        HN3 | |z
       OP3| | |
         TN1| |
          |HY3|
          | |NP3
          | | HN3
          | | |YO0
         z| | |PY0
        PY0 | |z
       PY0| | |
         NH1| |
          |TO3|
          | |HP3
          | | PN3
          | | |OY3
         z| | |TH3
        YP3 | |z
       PP1| | |
         PT0| |
          |PY0|
          | |PY0
          | | PY0
          | | |HN3
         z| | |PN0
        PY0 | |z
       PY0| | |
         PY0| |
          |TH0|
          | |PO0
          | | YN0
          | | |TO0
         z| | |zz 
          z | |
          z | |
          | | |
          | | |
           \|/
            v
"""


The import serpent line immediately attracts attention . At the request of "python serpent sword" in Google, you can stumble upon a git repository , which judging by the description is just what we need.

Having studied the code serpent.py we find there a description of the principle of "turning" the code into a sword:

Given the hex digit 65:
65% 32 = 1
floor (65/32) = 2
so the alphabet symbol for 65 is the symbol at index 1: 'PT'.
65 is also the 3rd occurrence of a 32-modulus of 1 (with 1
being the first occurrence and 33 being the second of course.)
So the code for this hex symbol is PT2

But the most interesting is located below:

elif scriptType is _SERPENT:
    pyc = _serpent_sword_alphabet_to_hex(_lex_hex(sys.argv[0]))
    pycout = ".".join(sys.argv[0].split(".")[0:-1])+".pyc"
    with open(pycout, "wb") as f:
        for val in pyc:
        f.write(chr(val))

This code reads the converted file and translates it back to .pyc . Rename the file sword.py to sword.ss.py and try to reverse it:

redihi@kali:KingArthur$ python sword.ss.py 
RuntimeError: Bad magic number in .pyc file

By slightly changing the code so that the sword.ss.pyc file is not deleted, we find that the received file has a damaged header.

After a long search, we find an approximate description of the header of the .pyc file. But all attempts to change it to true did not succeed.

There, in the article, there is an example of compiling not a whole file, but a separate section of code. Probably in the same way, bypassing the header, it will restore the rest of the code. We are throwing a small script for these purposes:

unserpent.py
#!/usr/bin/python
import re
import sys
import marshal
alphabet = [
    "PY", "PT", "PH", "PO", "PN", "YP", "YT", "YH", "YO", "YN", "TP", "TY", "TH", "TO", "TN",
    "HP", "HY", "HT", "HO", "HN", "OP", "OY", "OT", "OH", "ON", "NP", "NY", "NT", "NH", "NO",
    "PP", "YY"
]
def _serpent_sword_alphabet_to_hex(sentence):
    "Convert the serpent alphabet string back to python bytecode"
    return [alphabet.index(symbol[0:-1]) + int(symbol[-1]) * 32 for symbol in sentence]
def _lex_hex(infile):
    "Extract the serpent string from the ss file"
    with open(infile, 'r') as source:
        regex = re.compile(r'[PYTHON][PYTHON][0-9]')
        tokens = []
        for line in source:
            pos = 0
            while(pos < len(line)):
                match = regex.match(line, pos)
                if match:
                    tokens.append(match.group(0))
                pos += 1
        return tokens
def convert_to_pyc(fname):
    pyc = _serpent_sword_alphabet_to_hex(_lex_hex(fname))
    pycout = ".".join(fname.split(".")[0:-1]) + ".pyc"
    with open(pycout, "wb") as f:
        for val in pyc:
            f.write(chr(val))
    f.close()
    return pycout
fname = sys.argv[1]
pycout = convert_to_pyc(fname)
f = open(pycout, "rb").read()
for x in range(len(f)):
    try:
        code = marshal.loads(f[x:])
        print('Offset found: %d' % x)
        print('\targcount: %s' % code.co_argcount)
        print('\tcode: %s' % code.co_code.encode('hex'))
        print('\tconsts count: %d' % len(code.co_consts))
        for item in code.co_consts:
            print('\t\t%s: %r' % (type(item), item))
        print('\tfilename: %s' % code.co_filename)
        print('\tfirstlineno: %s' % code.co_firstlineno)
        print('\tflags: %s' % code.co_flags)
        print('\tname: %s' % code.co_name)
        print('\tnlocals: %s' % code.co_nlocals)
        print('\tstacksize: %s' % code.co_stacksize)
        print('\tvarnames count: %d' % len(code.co_varnames))
        for item in code.co_varnames:
            print('\t\t%r' % item)
        break
    except ValueError:
        continue


After starting, we get the output:

redihi@kali:KingArthur$ ./unserpent.py sword.ss.py
Offset found: 7
	argcount: 0
	code: 6401006400006c00005a00006402008400005a01006501008300000164000053
	consts count: 3
		: None
		: -1
		: 
	filename: D:\Downloads\UFOCTF\TASKS\serpent\sources\Serpent-master\Serpent-master\test.py
	firstlineno: 1
	flags: 0
	name: 
	nlocals: 0
	stacksize: 2
	varnames count: 2
		'serpent'
		'task'

Как можно заметить, тут есть ещё 1 объект code, он то нам и нужен. Добавим ещё пару строк, для его декомпиляции:

import uncompyle6
...
uncompyle6.main.uncompyle(2.7, code.co_consts[2], sys.stdout)

Снова запускаем и получаем готовый скрипт:

# uncompyle6 version 2.9.10
# Python bytecode 2.7
# Decompiled from: Python 2.7.6 (default, Oct 26 2016, 20:30:19) 
# [GCC 4.8.4]
# Embedded file name: D:\Downloads\UFOCTF\TASKS\serpent\sources\Serpent-master\Serpent-master\test.py
line = raw_input('Enter line: \n')
if line[:14:2] != 'XMg9v66':
    print 'Fail!'
elif line[14::2] != 'yBfBg9va':
    print 'Fail!'
elif line[-15:-30:-2] != 'Y1PXqggB':
    print 'Fail!'
elif line[-1:-14:-2] != '3W74khw':
    print 'Fail!'
else:
    print 'Success!'

Осталось дело за малым, по полученному коду восстанавливаем флаг:
ufoctf{XBMggg9qvX6P61yYBwfhBkg497vWa3}

Задание пройдено! +600 к рейтингу!

Also popular now: