ByteCTF 2021 Crypto

easyxor shift函数是个常见的移位异或操作,convert是对一个数字使用不同的 key 和 mask 进行 4 次移位异或,这个函数在已知 key 的情况下是可逆的。 encrypt函数是对明文块进行两种模式(CBC和OFB)的块加密,块长度为 8,对于每一块的加密使用的就是上面的convert函数。 首先通过密文的长度可以得知一共被分成了 6 块;前 3 块明文使用 OFB 模式,后三块明文使用 CBC 模式;keys 是一个长度为 4 的列表,列表中每个值的范围是(-32, 32),$64^4$ 爆破也是可以接受的。 读完题目代码之后可以想到其实我们已经知道第一块明文了,就是 flag 的格式ByteCTF{,而OFB模式实际上是加密的key,最终结果和明文块异或,所以第一个明文块异或第一个密文块就可以知道第一个 key 加密的结果,也就是cur_c = convert(last, k)的cur_c,这样就可以得到第二块的 last。 现在对于第二块,已知 IV(last),未知 keys,已知明文是可显示字符,所以可以爆破 keys 了,把能解出可显示字符明文的 keys 都保留出来,发现有 4836 个 keys 是满足的,那么我们还要借助第三块再筛一次,最终只得到一组 keys。 from itertools import product from tqdm import tqdm from Crypto.Util.number import bytes_to_long, long_to_bytes def check(s): return min([((i < 129) and (i > 31)) for i in s]) c = "89b8aca257ee2748f030e7f6599cbe0cbb5db25db6d3990d3b752eda9689e30fa2b03ee748e0da3c989da2bba657b912" c_list = [int(c[i*16:i*16+16], 16) for i in range(len(c) // 16)] known_m = bytes_to_long(b'ByteCTF{') range64 = list(range(-32, 33)) cur_c = known_m ^ c_list[0] print(cur_c) k_cnt = 0 for a,b,c,d in tqdm(product(range64, range64, range64, range64)): last = cur_c k = [a, b, c, d] try_cur_c = convert(last, k) m1 = long_to_bytes(try_cur_c ^ c_list[1]) if check(m1): # 只筛选这第一轮的话,4836个k是满足条件的,所以得筛第二轮 last = try_cur_c try_cur_c = convert(last, k) m2 = long_to_bytes(try_cur_c ^ c_list[2]) if check(m2): k_cnt += 1 try: print(m1....

October 29, 2021 · 8 min · Slightwind