对称密码题,记录一下。
题目源码:
import sys
from hashlib import sha256
from Crypto.Cipher import DES
SECRET = 0xa########e # remember to erase this later..
seed = b'secret_sauce_#9'
def keygen(s):
keys = []
for i in range(2020):
s = sha256(s).digest()
keys.append(s)
return keys
def scramble(s):
ret = "".join( [format(s & 0xfffff, '020b')]*101 )
ret += "".join( [format(s >> 20, '020b')]*101 )
return int(ret, 2)
def encrypt(keys, msg):
dk = scramble(SECRET)
for v in keys:
idx = dk & 3
dk >>= 2
k = v[idx*8:(idx+1)*8]
cp = DES.new(k, DES.MODE_CBC, bytes(8))
msg = cp.encrypt(msg)
return msg
keys = keygen(seed)
with open("flag.txt", "rb") as f:
msg = f.read()
ctxt = encrypt(keys, msg)
print(ctxt)
#Plaintext: b'Attack at DAWN!!'
#Ciphertext: b'\x15\x08T\xff<\xf4\xc4\xc0\xd2;\xd6\x8a\x824\x83\xbe'
输出flag
00000000h: 8C A9 D3 FA 58 75 3B 29 CD F6 BE 1E 7B A9 5B C9 ; .©ÓúXu;)Íö¾.{©[É
00000010h: D7 B9 15 A4 7D 51 85 7A E7 E8 F5 D6 B6 20 ED 24 ; ×¹.¤}Q.zçèõÖ¶ í$
00000020h: 0A 80 48 FA A8 9C 9E 0E ; ..Hú¨...
大概分析下题意,首先是以secret_sauce_#9为种子生成2020个随机数,生成逻辑是Sk=sha256(Sk-1)
然后进行encrypt加密
- 加密第一步是调用scramble函数对SECRET值(共40bit)进行一个处理——先将SECRET和0xfffff进行与运算(保留低20bit)保存101次至dk;再将SECRET右移20位(保留高20bit)保存101次至dk
- 然后循环2020个key,每轮取idx为dk低位2bit数值(取完idx后dk右移2位),k取当前key第idx8到idx8+8个字符,之后以k为密钥进行一次DES加密,本轮密文作为下一轮明文,初始明文为flag
- 最终返回2020次DES加密的结果
由于2020个随机数的种子是已知的,递推算法也明确,所以2020个随机数都可以直接得出。
本题的重点实际上是恢复SECRET值。
看清楚加密逻辑之后其实可以看出来,2020轮DES加密的前1010轮只和dk的低2020bit有关(也就是只和SECRET值高20bit有关),后1010轮只和dk的高2020bit有关(也就是只和SECRET值低20bit有关)。当已知明文和密文的情况下,将明文加密前1010轮得到的值应该等于将密文解密后1010轮得到的值相同。
根据SECRET = 0xa########e ,即需要枚举高位中缺失的16bit和低位中缺失的16bit,找到正确的高位值和低位值。
from Crypto.Util.number import *
from Crypto.Cipher import DES
from hashlib import sha256
from numpy import *
from tqdm import *
m=b"Attack at DAWN!!"
def keygen(s):
keys = []
for i in range(2020):
s = sha256(s).digest()
keys.append(s)
return keys
def scramble_l(x):
ret = "".join( [format(x >> 20-20, '020b')]*101 )
return int(ret,2)
def encrypt(msg,keys,sec):
for i in range(1010):
idx=(sec>>(2*i))&3
k=keys[i][idx*8:idx*8+8]
cp = DES.new(k, DES.MODE_CBC, bytes(8))
msg=cp.encrypt(msg)
return msg
keys = keygen(b'secret_sauce_#9')
cts_enc = []
for i in tqdm(range(0,65536)):
SECRET_H=scramble_l(0xa0000+i)
cts_enc.append(bytes_to_long(encrypt(m,keys,SECRET_H)))
c=b"\x15\x08\x54\xff\x3c\xf4\xc4\xc0\xd2\x3b\xd6\x8a\x82\x34\x83\xbe"
def scramble_h(x):
ret = "".join( [format(x & 0xfffff, '020b')]*101 )
return int(ret,2)
def decrypt(msg,keys,sec):
for i in range(1010):
idx=(sec>>(2018-2*i))&3
k=keys[2019-i][idx*8:idx*8+8]
cp = DES.new(k, DES.MODE_CBC, bytes(8))
msg=cp.decrypt(msg)
return msg
cts_dec = []
for i in tqdm(range(0,65536)):
SECRET_L=scramble_h(0xe+i*16)
cts_dec.append(bytes_to_long(decrypt(c,keys,SECRET_L)))
tip,xidx,yidx=intersect1d(cts_enc,cts_dec,return_indices = True)
#184442183257442470540344695923362364633
#19870
#13848
则SECRET高位为0xa0000+19870=0xa4d9e,低位为0x0000e+13848*16=0x3618e
接下来就是一个简单的逆向DES解密即可得到flag
import sys
from hashlib import sha256
from Crypto.Cipher import DES
SECRET=0xa4d9e3618e
seed = b'secret_sauce_#9'
def keygen(s):
keys = []
for i in range(2020):
s = sha256(s).digest()
keys.append(s)
return keys
def scramble(s):
ret = "".join( [format(s & 0xfffff, '020b')]*101 )
ret += "".join( [format(s >> 20, '020b')]*101 )
return int(ret, 2)
def decrypt(keys, msg):
dk = scramble(SECRET)
for i in range(2020):
idx = (dk>>(4038-(2*i)))&3
k = keys[2019-i][idx*8:(idx+1)*8]
cp = DES.new(k, DES.MODE_CBC, bytes(8))
msg = cp.decrypt(msg)
return msg
keys = keygen(seed)
msg=b'\x8c\xa9\xd3\xfaXu;)\xcd\xf6\xbe\x1e{\xa9[\xc9\xd7\xb9\x15\xa4}Q\x85z\xe7\xe8\xf5\xd6\xb6 \xed$\n\x80H\xfa\xa8\x9c\x9e\x0e'
ctxt = decrypt(keys, msg)
print(ctxt)