#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Util.strxor import strxor from os import urandom flag = open('./flag.txt', 'rb').read().strip() KEY = urandom(16) IV = urandom(16) def encrypt(msg, key, iv): msg = pad(msg, 16) blocks = [msg[i:i+16] for i in range(0, len(msg), 16)] out = b'' for i, block in enumerate(blocks): cipher = AES.new(key, AES.MODE_ECB) enc = cipher.encrypt(block) if i > 0: enc = strxor(enc, out[-16:]) out += enc return strxor(out, iv*(i+1)) def decrypt(ct, key, iv): blocks = [ct[i:i+16] for i in range(0, len(ct), 16)] out = b'' for i, block in enumerate(blocks): dec = strxor(block, iv) if i > 0: dec = strxor(dec, ct[(i-1)*16:i*16]) cipher = AES.new(key, AES.MODE_ECB) dec = cipher.decrypt(dec) out += dec return out flag_enc = encrypt(flag, KEY, IV).hex() print('Welcome! You get 1 block of encryption and 1 block of decryption.') print('Here is the ciphertext for some message you might like to read:', flag_enc) try: pt = bytes.fromhex(input('Enter plaintext to encrypt (hex): ')) pt = pt[:16] # only allow one block of encryption enc = encrypt(pt, KEY, IV) print(enc.hex()) except: print('Invalid plaintext! :(') exit() try: ct = bytes.fromhex(input('Enter ciphertext to decrypt (hex): ')) ct = ct[:16] # only allow one block of decryption dec = decrypt(ct, KEY, IV) print(dec.hex()) except: print('Invalid ciphertext! :(') exit() print('Goodbye! :)')
AES 。なにやら独自の暗号化モードを実装している。
encryptは
decryptは
暗号化、復号は1ブロックのみ、1度ずつ許可されている状態で、暗号化したフラグが渡されるのでどうにかしようという問題。KEY
, IV
が接続毎に変わるので大変だぁ
ここで暗号化時にPKCS#7パディングが用いられていることを利用して\x10
* 16を暗号化してみる
すると暗号化される平文は(の部分はパディングで追加された \x00
*16)になるので
としてを求めることができる
がわかっているならフラグの暗号文のは簡単に復号できるし、を送ればが復号できる
from ptrlib import Socket, xor cnt = 0 flag = b"" while True: sock = Socket("chal.duc.tf", 30201) ciphertext = bytes.fromhex(sock.recvlineafter(":").decode()) sock.sendlineafter(": ", (b"\x10"*16).hex()) iv = bytes.fromhex(sock.recvline().decode())[16:] if cnt == 0: c = ciphertext[:16] else: c = xor(ciphertext[cnt*16:(cnt+1)*16], ciphertext[(cnt-1)*16:cnt*16]) c = xor(c, iv) sock.sendlineafter(": ", c.hex()) flag += bytes.fromhex(sock.recvline().decode()) print(flag) if 16*(cnt+1) == len(ciphertext): break cnt+=1 sock.close()