CBC 密码分组链接模式(cipher block chaining Triple)。需要key,IV初始向量 (Initialization Vector) 加解密,(key、IV、c为字节类型) ,IV is as long as the block size ,IV一般会直接给出,大小和 block 等大,block 即加密时会将明文分成等长的模块(block) ,常见的分为8字节、16字节、32字节,所以很多时候不会恰好等分,此时需要填充。
defasserts(pt: bytes): num = pt[-1] iflen(pt) == 16: result = pt[::-1] count = 0 for i in result: if i == num: count += 1 else: break if count == num: returnTrue else: returnFalse else: returnFalse
defdecrypt(c): iv = c[:32] cipher = c[32:] plain_text = cbc_decrypt(binascii.unhexlify(cipher), binascii.unhexlify(iv)) if asserts(plain_text): returnTrue else: returnFalse
if hashlib.sha256((XXXX + random_str[4:].encode())).hexdigest() != str_sha256: returnFalse
returnTrue
defhandle(self): ifnot self.proof(): self.request.sendall(b'Error Hash!') return cipher = encrypt() self.request.sendall('Welcome to AES System, please choose the following options:\n1. encrypt the flag\n2. decrypt the flag\n'.encode()) n = 0 while n < 65536: options = self.request.recv(512).strip().decode() if options == '1': self.request.sendall(('This is your flag: %s\n' % cipher).encode()) elif options == '2': self.request.sendall('Please enter ciphertext:\n'.encode()) recv_cipher = self.request.recv(512).strip().decode() if decrypt(recv_cipher): self.request.sendall('True\n'.encode()) else: self.request.sendall('False\n'.encode()) else: self.request.sendall('Input wrong! Please re-enter\n'.encode()) n += 1 return
from pwn import * from hashlib import * import itertools import string from Crypto.Util.number import *
defproof(broke, Hash): # 爆破 sha256 assertlen(broke) == 16andlen(Hash) == 64 shaTable = string.ascii_letters + string.digits for ii in itertools.permutations(shaTable, 4): x = ''.join(ii) s = x + broke if sha256(s.encode()).hexdigest() == Hash: print(x) return x
io.sendlineafter(b'2. decrypt the flag',b'1') io.recvuntil(b'This is your flag: ') c = io.recv(64).decode() print(c) guess_iv = [0for _ inrange(16)] # iv 初始列表 restore_midd = [0for _ inrange(16)] # 中间值初始列表 index = 1# 填充值
for i inrange(15, -1, -1): # iv长度为16字节,先爆破最后一位,所以倒序 for j inrange(0, 256): # iv每一字节的所有可能性 io.sendline(b'2') io.recvuntil(b'Please enter ciphertext:') io.recv(1).decode() # 空格 随便接收一下 guess_iv[i] = j mess = bytes(guess_iv).hex() + c[32:] io.sendline(mess.encode()) result = io.recv(5).strip().decode() # 接收交互端的判断结果
if result == 'True': print('find') restore_midd[i] = index ^ j # 依据判断结果得到中间值 for k inrange(15, i - 1, -1): guess_iv[k] = restore_midd[k] ^ (index + 1) # 替换到iv列表里 break index += 1
m = bytes_to_long(bytes(restore_midd)) ^ int(c[:32], 16) # iv是16字节,所以只有一组,直接异或iv即得flag print(long_to_bytes(m))
if __name__ == '__main__': Solve_Oracle() # QU{0P@d4Ttk} 改为 D0g3{0P@d4Ttk}