Технологии безопасности

Сайт посвященный вопросам безопасности

UFOCTF 2017: декомпилируем Python в задании King Arthur (PPC600)


Приветствую тебя читатель! Недавно, закончилась ежегодная олимпиада по информационной безопасности 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(‘tt%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(‘tt%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
<type ‘NoneType’>: None
<type ‘int’>: -1
<type ‘code’>: <code object task at 0x7f7e85aa9eb0, file "D:DownloadsUFOCTFTASKSserpentsourcesSerpent-masterSerpent-mastertest.py", line 4>
filename: D:DownloadsUFOCTFTASKSserpentsourcesSerpent-masterSerpent-mastertest.py
firstlineno: 1
flags: 0
name: <module>
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:DownloadsUFOCTFTASKSserpentsourcesSerpent-masterSerpent-mastertest.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 к рейтингу!