crypto-Des-MMXX(b01lers ctf 2020)

对称密码题,记录一下。
题目源码:

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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值