点击此处 获得更好的阅读体验
来源
来自Prowes5
原创投稿
题目考点
解题思路
下载下来题目,发现是一个类似于xml格式的文本文件。看到头部有magic字段。
1 2 3 4 5 6 7 8 9 magic 03f30d0a moddate 6d4a695b (Tue Aug 7 15:29:49 2018) <code> <argcount> 0 </argcount> <nlocals> 0</nlocals> <stacksize> 5</stacksize> <flags> 0040</flags> <code> ......
看出这个应该是pyc文件生成的,在网上一番查找。找到了这个项目https://github.com/hinus/railgun/blob/master/src/main/python/rgparser/show.py
先编译一个简单的pyc测试一下。
1 2 3 4 # filename: 1.py import base64 a = 1 b = 2
编译并解析pyc
1 2 python -m py_compile 1.py python show.py 1.pyc
输出如下效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 magic 03f30d0a moddate b272325f <code> <argcount> 0 </argcount> <nlocals> 0</nlocals> <stacksize> 2</stacksize> <flags> 0040</flags> <code> 6400006401006c00005a00006402005a01006403005a020064010053</code> <dis> 1 0 LOAD_CONST 0 (-1) 3 LOAD_CONST 1 (None) 6 IMPORT_NAME 0 (base64) 9 STORE_NAME 0 (base64) 3 12 LOAD_CONST 2 (1) 15 STORE_NAME 1 (a) 4 18 LOAD_CONST 3 (2) 21 STORE_NAME 2 (b) 24 LOAD_CONST 1 (None) 27 RETURN_VALUE </dis> <names> ('base64', 'a', 'b')</names> <varnames> ()</varnames> <freevars> ()</freevars> <cellvars> ()</cellvars> <filename> '1.py'</filename> <name> '<module>'</name> <firstlineno> 1</firstlineno> <consts> -1 None 1 2 </consts> <lnotab> 0c020601</lnotab> </code>
可以很明显的看到,这里是有dis也就是字节码字段的,猜测出题人把这段删除了。没有了字节码,还原没有那么容易,不过由于还有其他字段,也可以进行还原,最后比较一下二进制就可以了。大致逻辑清楚之后可能会有某些细节解析文件中是不存在的,这时候就可以对比二进制数据进行尝试。
就这样不断的尝试,编译,解析,一顿操作之后,终于还原出来了大部分代码。原文件名为re2.py 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import base64 from hashlib import md5 import random import string f = 'flag{*******}' def _(b): __ = ''.join(random.sample(string.digits,4)) ___ = '' for i in range(len(b)): ___ += chr(ord(b[i])^ord(__[i % 4])) return ___ def ____(a): ___ = md5() ___.update(a) return ___.digest() e = _(f[:12])+____(f[12:19])+_(f[19:]) print base64.b64encode(e) e = 'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU='
有了源代码,写一下解密代码问题也不大,可以看到_()的加密与解密之后的位数是相同的。所以就可以分成三段解密,前边一段由于flag格式为flag{
,所以随机的key反推出来也不是问题。而中间的是一段md5,拿出来转成32位,网上解密即可。而对于后面这部分,除了最后一位是}的信息,其他都不知道,所以需要将符合条件的全部输出出来,去找看上去像flag的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import base64 e = 'U1VQU05pSHdqCEJrQu7FS7Vngk1OTQ58qqghXmt2AUdrcFBBUEU=' e = base64.b64decode(e) print 'md5: '+e[12:12+16].encode('hex') before = e[0:12] after = e[28:] for i in range(0x30, 0x3a): for i1 in range(0x30, 0x3a): for i2 in range(0x30, 0x3a): for i3 in range(0x30, 0x3a): tmp = '' key = chr(i)+chr(i1)+chr(i2)+chr(i3) for a in range(len(after)): tmp += chr(ord(after[a]) ^ ord(key[a % 4])) #if tmp[0:4] == 'flag': # print tmp if tmp[-1] == '}': print tmp #_N0t_Hard}
这里就给出爆破后一段flag的脚本,爆破前一段的改一下就行,不爆破也行。
FLAG
1 flag{PyC_1s_613u21i_N0t_Hard}