0x00 题目分析

首先这是一道原题,是2020年工业互联网安全大赛CRYPTO 部分的一道题 2EM

这道题在比赛时是由我做出来的,跟网上大多数wp不一样,我没有采用爆破key的方式

而是通过公式推导解出的本次题目

题目

import random from secret import flag from Crypto.Util.number import bytes_to_long pbox1 = [22, 28, 2, 21, 3, 26, 6, 14, 7, 16, 15, 9, 17, 19, 8, 11, 10, 1, 13, 31, 23, 12, 0, 27, 4, 18, 30, 29, 24, 20, 5, 25] pbox2 = [17, 6, 7, 27, 4, 20, 11, 22, 2, 19, 9, 24, 23, 31, 15, 10, 18, 28, 5, 0, 16, 29, 25, 8, 3, 21, 30, 12, 14, 13, 1, 26] def p(data,pbox): tmp = bin(data)[2:].rjust(32,'0') out = [ tmp[x] for x in pbox ]# 010101010101001 # [22] [0] return int(''.join(out),2) def genkey(l): return random.getrandbits(l) def encrypt(key,msg): tmp1 = p(msg^key,pbox1) tmp2 = p(tmp1^key,pbox2) return tmp2^key key = genkey(32) flag = flag.ljust(44,'\x00') for i in range(len(flag)/4): pt = bytes_to_long(flag[i*4:i*4+4]) print encrypt(key,pt) for i in range(2**22): # 后面为key ,前面11组为flag pt = random.getrandbits(32) ct = encrypt(key,pt) print pt,ct

0x10 加密流程分析

可以看出来这里主要的加密函数是encrypt函数

我们来分析这个函数干了什么

首先需要传入一个msg(长整型)和一个key(长整形)
然后encrypt(key,msg)会做第一步处理
tmp1 = p(msg^key,pbox)====>
接下来我们分析p函数,p函数需要传入一个data, pbox
data 为长整型 pbox为一个列表,一个替换表
def p(data,pbox):
    tmp = bin(data)[2:].rjust(32,'0') //将data转化为二进制形式0101 并补起32位
    out = [ tmp[x] for x in pbox ] 而刚好pbox也是32的长度 其实这里只是将32位0101换了个位置
    return int(''.join(out),2) //返回密文
我们做一个输入假设:
data = msg ^ key
pbox 
第一加密结果为 pbox1(msg^key)
第二次加密结果为:pbox2(pbox1(msg^key)^key)
最终返回结果为:pbox2(pbox1(msg^key)^key)^key

划重点: (a^b)^c = a^b^c = a^c ^b
a ^ b ^ a = b

而在这里 enc = pbox2(pbox1(msg^key)^key)^key

而 在这里pbox的作用只是将密码换位比如说将0101 换成 1100 ,由于异或操作是按位异或的,相同为0不同为1
>>> 0^1
1
>>> 0^0
0
>>> 1^1
0
>>> 1^0
1
>>>
因此其实 pbox1(msg^key) = pbox1(msg) ^ pbox1(key) , 即: 先异或在换位与先换位在异或是一样的

那么在这里我们就可以得出
enc = pbox2(pbox1(msg^key)^key)^key
		= pbox2(pbox1(msg)^pbox1(key)^key)^key
		= pbox2(pbox1(msg)^pbox1(key))^pbox2(key)^key
		= pbox2(pbox1(msg)) ^ pbox2(pbox1(key))^pbox2(key)^key
由于 pbox1 与 pbox2 是我们已知的内容,因此我们的问题转化成
假设:pbox2(pbox1(key))^pbox2(key)^key = KEY
pbox2(pbox1(msg)) = enc ^ KEY

因此我们需要一组明文以及密文来恢复KEY
KEY = enc ^ pbox2(pbox1(msg))
而题目给了我们2**22组密文明文,我们随便取一组就可以获得key

获取KEY之后我们只需要利用:
pbox2(pbox1(msg)) = KEY ^ enc 就可以获取 pbox2(pbox1(msg))
然后反解pbox就可以获取msg

0x20 POC

from Crypto.Util.number import bytes_to_long, long_to_bytes pbox1 = [22, 28, 2, 21, 3, 26, 6, 14, 7, 16, 15, 9, 17, 19, 8, 11, 10, 1, 13, 31, 23, 12, 0, 27, 4, 18, 30, 29, 24, 20, 5, 25] pbox2 = [17, 6, 7, 27, 4, 20, 11, 22, 2, 19, 9, 24, 23, 31, 15, 10, 18, 28, 5, 0, 16, 29, 25, 8, 3, 21, 30, 12, 14, 13, 1, 26] def p(data,pbox): tmp = bin(data)[2:].rjust(32,'0') out = [ tmp[x] for x in pbox ] return int(''.join(out),2) def rep(data,pbox): tmp = bin(data)[2:].rjust(32,'0') out = [tmp[pbox.index(x)] for x in range(32)] return int(''.join(out),2) # KEY = enc ^ pbox2(pbox1(msg)) def getkey(msg,enc): tmp1 = p(msg,pbox1) tmp2 = p(tmp1,pbox2) return tmp2 ^ enc def decrypt(key,enc): p2p1msg = key^enc p1msg = rep(p2p1msg,pbox2) msg = rep(p1msg,pbox1) return msg if __name__ == '__main__': enc = 1172144695 msg = 460506081 key = getkey(msg,enc) flags = '''4251930966 3815817250 924996034 2002900166 863555937 388087843 795922435 1130479906 1394659491 556299079 732691239'''.split("\n") flag = "" for i in flags: msg = decrypt(key,int(i)) flag += long_to_bytes(msg) print flag ''' 4251930966 3815817250 924996034 2002900166 863555937 388087843 795922435 1130479906 1394659491 556299079 732691239 460506081 1172144695 '''