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
'''