-
[Crypto][CTF]YYYES - AES CBC AttackCrypto 2024. 10. 13. 22:59
Description
- YYY가 AES 플래그 서버를 운영하고 있어요. 무엇이든 암호화하고 복호화 할 수 있다던데… 플래그를 획득해주세요!
- prob.py
#!/usr/bin/env python3 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import os BLOCK_SIZE = 16 flag = open("flag", "rb").read() key = os.urandom(BLOCK_SIZE) # perfectly protected! iv = key print(iv) for _ in range(5): iv = AES.new(key, AES.MODE_CBC, b'\x00' * 16).encrypt(iv) print(iv) encrypt = lambda pt: AES.new(key, AES.MODE_CBC, iv).encrypt(pad(pt, BLOCK_SIZE)) decrypt = lambda ct: AES.new(key, AES.MODE_CBC, iv).decrypt(ct) print("Welcome to YYY's AES server") while True: print("[1] Encrypt") print("[2] Decrypt") print("[3] Get Flag") choice = input() if choice == "1": print("Input plaintext (hex): ", end="") pt = bytes.fromhex(input()) print(encrypt(pt).hex()) elif choice == "2": print("Input ciphertext (hex): ", end="") ct = bytes.fromhex(input()) print(decrypt(ct).hex()) elif choice == "3": print(f"flag = {encrypt(flag).hex()}") exit() else: print("Nope")
- 사실 이 형태와 비슷한 워게임 문제가 이미 화이트햇 스쿨 사이트에 ‘Exercise-CBC’ 문제로 있다. 똑같이 AES CBC 모드로 인해 암호화된 flag를 평문-암호문 쌍을 이용해 복호화시켜 답을 알아내는 문제였다.
- 먼저 CBC 모드에 대해 간단히 개념을 보자.
https://dreamhack.io/lecture/courses/552
더보기CBC 모드
CBC(Cipher Block Chaining) 모드는 어떤 블록을 암호화하기 전에, 이 블록을 직전 블록의 암호문과 XOR합니다. 평문의 첫 번째 블록은 이전 블록이 존재하지 않으므로, **초기 벡터(Initialization Vector, IV)**라고 불리는 임의의 데이터와 XOR합니다. 이 모드를 사용하면 각 블록이 서로의 암호화에 영향을 주므로 같은 블록도 전체 평문 및 IV에 따라 암호화 결과가 달라집니다.
송신자는 초기 벡터를 암호문의 0번째 블록 C0로 전송합니다. CBC 모드의 암, 복호화를 수학적으로 표현하면 다음과 같습니다.
암호화 : $C_0=IV,C_i=enc_K(P_i⊕C_{i−1}),i≥1$
복호화 : $C_0=IV,P_i=dec_K(C_i)⊕C_{i−1},i≥1$
- 이 문제도 동일하게 CBC 모드의 평문-암호문 쌍을 이용하여 key를 알아내어 암호화된 flag를 복호화해야하는데, Exercise-CBC 문제와의 차이점은 key를 IV값을 0으로 하여 5번 암호화한 값을 새로운 IV값으로 설정하여 flag를 암호화했다는 것이다. 이전 문제에서는 IV값을 알아내면 그것이 곧 key였는데, 이 문제에서는 IV값을 알아내는 것에서 끝나지 않는다. 이를 해결하기 위해, 새로운 IV값이 설정된 과정과, 그 IV값을 알아내기 위한 수식을 적어보았다.
Key를 이용하여 IV가 암호화된 과정
$평문 : K, 암호문 : R_1, R_2, …$
$R_1=enc_k(K⊕0)=enc_k(K)$
$R_2=enc_k(R_1⊕0)=enc_k(R_1)$
$…$
$R_5=enc_k(R_4⊕0)=enc_k(R_4)=IV$
평문-암호문 쌍을 이용하여 IV를 얻어내는 과정
$\text{if } P_1=0, C_1=enc_k(0⊕C_0)=enc_k(IV)$
$\text{if }C_1=0, C_2=enc_k(IV),$
$P_1=dec_k(0)⊕IV, P_2=dec_k(enc_k(IV))=IV$
- 여기서 key를 알아내기 위해 어떤 평문-암호문 쌍을 이용할지를 고민하다가, xor연산을 이용하기 위해 암호문을 IV값( $R_5$ )으로 넣고 복호화를 시킨 식을 변형하면 key를 4번 암호화시킨 $R_4$가 나온다는 사실을 알아냈다.
- 마찬가지로 암호문을 $R_4$로 넣고 복호화한 식을 변형하면 $R_3$가 나오고, 이를 반복하면 key값이 나온다.
평문-암호문 쌍을 이용하여 key를 얻어내는 과정
$\text{if }C=IV, P=dec_k(IV)⊕IV, P⊕IV=dec_k(IV)=dec_k(R_5)=R_4$
$\text{if }C=R_4, P=dec_k(R_4)⊕IV, P⊕IV=dec_k(R_4)=R_3$
$…$
$\text{if }C=R_1, P=dec_k(R_1)⊕IV, P⊕IV=dec_k(R_1)=dec_k(enc_k(K))=K$
- 얻어낸 key를 가지고 암호화된 flag를 복호화시키면 성공이다
- sol.py
from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES from pwn import * r = remote("srv1.kitriwhs.kr", 8813) r.sendlineafter("[3] Get Flag\n", "1") r.sendlineafter("(hex): ", "00" * 16) blk = bytes.fromhex(r.recvline(keepends=False).decode()) r.sendlineafter("[3] Get Flag\n", "2") r.sendlineafter("(hex): ", "00" * 16 + blk.hex()) iv = bytes.fromhex(r.recvline(keepends=False).decode())[16:32] dec_iv = iv for _ in range(5): r.sendlineafter("[3] Get Flag\n", "2") r.sendlineafter("(hex): ", dec_iv.hex()) dec_iv = xor(bytes.fromhex(r.recvline()[:-1].decode())[:16],iv) key = dec_iv r.sendlineafter("[3] Get Flag\n", "3") r.recvuntil("flag = ") ct = bytes.fromhex(r.recvline(keepends=False).decode()) flag = AES.new(key, AES.MODE_CBC, iv).decrypt(ct) print(flag.decode())
'Crypto' 카테고리의 다른 글
[Crypto][CTF]hashbrown - Length Extension Attack (4) 2024.11.13 [Crypto][CTF]Bigger is Better - RSA Wiener Attack (1) 2024.10.29