UFOCTF 2017: Decompiler Python job King Arthur (PPC600)

Welcome you abrowser! Recently, ended with the annual Olympiad in information security UFO CTF 2017. This article will ritap one task from section PPC, called "King Arthur", which could get the maximum amount of points is 600.
the
Start
The task was:
We got a strange attachment in an e-mail from a fan of the knights. Help to understand what is meant by the author.
And was available for download file:
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
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
"""
Immediately attracted the attention of the line import serpent. For query "python serpent sword" in Google you can stumble on git repository, which judging by the description — just what we need.
After examining the code serpent.py we find there is a description of the principle of "transformation" code in the sword:
Given the hex digit 65:
65% 32 = 1
floor( 65 / 32 ) = 2
so the alphabet symbol for the 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 below:
the
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 into .pyc. Rename the file sword.py sword.ss.py and try to convert:
the
redihi@kali:KingArthur$ python sword.ss.py
Runtime error: Bad magic number in .pyc file
Slightly changing the code so that the file sword.ss.pyc was not deleted find that the resulting file has a damaged header.
After a long search, we find a rough description of title .pyc file. But all attempts to change it to true did not succeed.
There in article there is an example of not compiling the entire file, but a separate piece of code. Probably the same way will turn out bypassing the header, to restore the remainder of the code. Distribute for these purposes a small script:
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):
"The serpent alphabet Convert 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 from the ss string 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 running the resulting output:
the
redihi@kali: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:\Downloads\UFOCTF\TASKS\serpent\sources\Serpent-master\Serpent-master\test.py", line 4>
filename: D:\Downloads\UFOCTF\TASKS\serpent\sources\Serpent-master\Serpent-master\test.py
firstlineno: 1
flags: 0
name: <module>
nlocals: 0
stacksize: 2
varnames count: 2
'serpent'
'task'
As you can see, there is still 1 object code, it is exactly what we need. Let's add a couple of rows for decompiling:
the
import uncompyle6
...
uncompyle6.main.uncompyle(2.7, code.co_consts[2], sys.stdout)
Again, run and get a ready script:
the
# uncompyle6 version 2.9.10
# The bytecode of Python 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!'
It remains the case for small, according to the received code recoverable flag:
ufoctf{XBMggg9qvX6P61yYBwfhBkg497vWa3}
Job passed! +600 rating!
Комментарии
Отправить комментарий