今天碰到了一个比较有意思的综合题目,记下来给大家看看。

题目和脚本地址:you_need_python_by_sunnyelf
解压zip有两个文件,一个是flag.py,一个是key_is_here_but_do_you_know_rfc4042。
首先第一步看提示rfc4042的文件,查询可得,它是utf9或者是utf18的编码。在github上找到utf9解码的库,代码如下:

import utf9

dt = ''
with open('key_is_here_but_do_you_know_rfc4042', 'r') as f:
    t = f.read()
    dt = utf9.utf9decode(t)
print dt

解出为

_____*((__//__+___+______-____%____)**((___%(___-_))+________+(___%___+_____+_______%__+______-(______//(_____%___)))))+__*(((________/__)+___%__+_______-(________//____))**(_*(_____+_____)+_______+_________%___))+________*(((_________//__+________%__)+(_______-_))**((___+_______)+_________-(______//__)))+_______*((___+_________-(______//___-_______%__%_))**(_____+_____+_____))+__*(__+_________-(___//___-_________%_____%__))**(_________-____+_______)+(___+_______)**(________%___%__+_____+______)+(_____-__)*((____//____-_____%____%_)+_________)**(_____-(_______//_______+_________%___)+______)+(_____+(_________%_______)*__+_)**_________+_______*(((_________%_______)*__+_______-(________//________))**_______)+(________/__)*(((____-_+_______)*(______+____))**___)+___*((__+_________-_)**_____)+___*(((___+_______-______/___+__-_________%_____%__)*(___-_+________/__+_________%_____))**__)+(_//_)*(((________%___%__+_____+_____)%______)+_______-_)**___+_____*((______/(_____%___))+_______)*((_________%_______)*__+_____+_)+___//___+_________+_________/___

刚开始还以为是类似brainfuck之类的东西,但是观察到有括号和运算符,于是猜测是不是下划线的个数代表数字,验证了一下结果是正确的。运算之后得到了一串数字,应该为key。

# 似乎不能直接u'_'会验证不出来,因此用dt[0]代替
ul = dt[0]
cnt = 0
ns = ''
for i in dt:
    if i is ul:
        cnt += 1
    else:
        if cnt != 0:
            ns += str(cnt)
            cnt = 0
            ns += i
        else:
            ns += i
# add
ns += str(cnt)
ns = ns.replace('//', '/')
print ns
key = eval(ns)
print key
# 5287002131074331513 key

第二步是难点,看marshal.loads()应该处理之后的是编译后的二进制流,查阅资料 http://www.cnblogs.com/rainduck/p/3524557.html 找到反编译的方法,因此用uncompyle2是最好不过的了,反编译得出py文件。

import marshal, zlib, base64

bs = marshal.loads(zlib.decompress(base64.b64decode('eJxtVP9r21YQvyd/ieWm66Cd03QM1B8C3pggUuzYCSWstHSFQijyoJBhhGq9OXJl2ZFeqAMOK6Q/94f9Ofvn1s+d7Lgtk/3O997du/vc584a0eqpYP2GVfwDEeOrKCU6g2LRRyiK4oooFsVVUSqkqxTX6J1F+SfSNYrrdKPorC76luhbpOEGCZNFZw2KG3Rmk26QtuXi3xTb7ND6/aVu0g2RuvhEcZNut5lAGbTvAFbyH57TkYLKy8J6xpDvQxiiiaIlcdqJxVcHbXY6bXNlZgviPCrO0+StqfKd88gzNh/qRZyMdWHE29TZZvIkG7eZFRGGRcBmsXJaUoKCQ9fWKHwSqNeKFnsM5PnwJ7q2aKk4AFhcWtQCh+ChB5+Lu/RmyYUxmtOEYxas7i/2iuR7Ti14OEOSmU0RADd4+dQzbM1FJhukAUeQ+kZROuLyioagrau76kc1slY1NNaY/y3LAxDQBrAICJisV2hMdF2lxQcyFuMoqcX3+TCl6xotqzSpkqmxYVmjXVjAXiwBsEfBrd1VvTvLCj2EXRnhoryAKdpxcIgJcowUB68yAx/tlCAuPHqDuZo0CN3CUGHwkPhGMA7aXMfphjbmQLhLhJcHa0a+mpgB191c1U1lnHJQbgkHx+WGxeJbejnpkzSavo2jkxZ7i725npGAaTc8FXmUjbUETHUmkxXN5zqL5WiWxwE7Bc11yyYzNJpN02jerq+DzNNodfxOX8kE4FcmYKscDdYD1oPGGucXYNmgs1F+NTf3GOt3Mg7b+NTVruqoQyX1hOEUacKw+AGbP38ZOq9THRXaSbL5pXGQ8bho/Z/lrzQaHxdoCrlev+t6nZ7re57r+57rHXag93Deh37k+vuw9zorO/Qj/B50cAf2oyOsvut3D+ADWxdxfN/1Drqu39mHzvcRswv/Hvz7sHeg9w8Qzy99DzuFwxhPhs6zWTbOI3OZRiaZZcVj5wVwOklx7OwVxR47PR46r/SVM8ulBJic9zku/eqY/MqJxiDj+Gd55wS3f35pbLCzHoEwzKKpDkN5i+TR+1AYCWTo5IV0Z0P9H3phDDd6lMzPdS5bbo9eJGbTsW9nbDqLL1N9Iq+rRxDbll2x67a9Lf27hw5uK1s1rZr6DOPF+FI=')))

import uncompyle2
with open('f.py', 'w') as f:
    uncompyle2.uncompyle('2.7', bs, f)
看encrypt函数,加密方法如下:
def encrypt(plain, key):
    keySHA1 = sha1(key)
    intSHA1 = calc(keySHA1)
    r = []
    for i in range(len(plain)):
        r.append(ord(plain[i]) + int('0x%s' % keySHA1[i % 40], 16) - intSHA1)
        intSHA1 = calc(sha1(plain[:i + 1])[:20] + sha1(str(intSHA1))[:20])

    return ''.join(map(lambda x: str(x), r))

对key进行sha1加密为16进制串,再处理为10进制串保存到intSHA1;
对plain里面的每一位ascii编码与keySHA1的某位进行相加后减去intSHA1,放入r中;
intSHA1变化为plain前i位的sha1前二十位与intSHA1的SHA1前二十位相加的十进制值;
将r中的元素转化为字符串得到加密后的字符串,因此,该加密方法是前后字符关联的,第n位的加密都与前n-1位有关。
了解了加密方法后,接下来尝试用第一步爆出的key爆破,爆破代码如下:

...
if __name__ == '__main__'
    cipherText = '-185-147-211-221-164-217-188-169-205-174-211-225-191-234-148-199-198-253-175-157-222-135-240-229-201-154-178-187-244-183-212-222-164'

    key = '5287002131074331513'
    flag =''
    for i in range(len(cipherText)/4):
        for j in range(31, 128):   # 一般31-128才是常用字符
            if encrypt(flag+chr(j), key) == cipherText[0:i*4+4]:
                print j,
                flag += chr(j)
                break
    print flag
发现爆破出的都是无意义字符串,猜想是否key有问题,尝试将key转换为16进制、16进制转字符串发现有意义字符串I_4m-k3y
hk = hex(key)[2:]
print hk
kk = ''
for i in range(len(hk)/2):
    kk += chr(int('0x' + hk[i*2:i*2+2], 16))
print kk

将key代入爆破代码即可解出flag。