[Из песочницы] UFOCTF 2017: декомпилируем Python в задании King Arthur (PPC600)

9ea6ced680854d2d9697763a380d8047.png
Приветствую тебя хабраюзер! Недавно, закончилась ежегодная олимпиада по информационной безопасности UFO CTF 2017. В этой статье будет райтап одного задания из раздела PPC, под названием «King Arthur», за который можно было получить максимальное количество очков — 600.

Начнём


В описании к заданию было следующее:
Мы получили какое-то странное вложение в e-mail от фаната рыцарей. Помогите разобраться, что имел в виду автор.

И был доступен для скачивания файл:
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
"""


Сразу привлекает внимание строка import serpent. По запросу «python serpent sword» в гугл можно наткнуться на git-репозиторий, который судя по описанию — как раз то что нам нужно.

Изучив код serpent.py находим там описание принципа «превращения» кода в меч:

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

Но самое интересное расположено ниже:
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))

Этот код, считывает преобразованный файл, и переводит его обратно в .pyc. Переименуем файл sword.py в sword.ss.py и попробуем его обратить:
gh0st3rs@gh0st3rs-pc:KingArthur$ python sword.ss.py 
RuntimeError: Bad magic number in .pyc file

Немного изменив код, таким образом чтобы файл sword.ss.pyc не удалялся обнаруживаем, что у полученного файла поврежден заголовок.

После долгих поисков, находим примерное описание заголовка .pyc файла. Но все попытки изменить его на верный не увенчались успехом.

Там же в статье, есть пример компиляции не всего файла, а отдельного участка кода. Вероятно таким же образом получится минуя заголовок, восстановить оставшуюся часть кода. Накидаем для этих целей небольшой скрипт:

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


После запуска получаем вывод:
gh0st3rs@gh0st3rs-pc: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 к рейтингу!

Комментарии (1)

  • 3 апреля 2017 в 18:19

    0

    Там в заголовке 0×0A 0×0D не хватало.

© Habrahabr.ru