문제 설명
여기서는 CBC로 암호화할 수 있지만 ECB로만 해독할 수 있습니다. 서로 다른 모드이기 때문에 약점이 될 수는..없겠죠..?
Play at https://aes.cryptohack.org/ecbcbcwtf/
문제 풀이
from Crypto.Cipher import AES
KEY = ?
FLAG = ?
@chal.route('/ecbcbcwtf/decrypt/<ciphertext>/')
def decrypt(ciphertext):
ciphertext = bytes.fromhex(ciphertext)
cipher = AES.new(KEY, AES.MODE_ECB)
try:
decrypted = cipher.decrypt(ciphertext)
except ValueError as e:
return {"error": str(e)}
return {"plaintext": decrypted.hex()}
@chal.route('/ecbcbcwtf/encrypt_flag/')
def encrypt_flag():
iv = os.urandom(16)
cipher = AES.new(KEY, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(FLAG.encode())
ciphertext = iv.hex() + encrypted.hex()
return {"ciphertext": ciphertext}
encrypt_flag에서는 iv를 random 16byte로 설정하고 암호문을 CBC 모드로 iv사용하여 생성한다. CBC(Cipher Block Chaining)모드는 ECB(Electronic CodeBook)모드와 다르게 이전 블록의 암호화된 결과를 현재 블록의 입력으로 사용하는 체인 방식으로 동작한다.
각 블록이 이전 블록의 암호문과 XOR을 수행한 결과와 함께 암호화된다. 따라서 같은 평문이라도 암호문이 다르게 생성되며, 초기화 벡터가(IV)가 필요하다. 이는 첫 블록의 암호화에 사용되는 값이다.
위에서는 ciphertext에 iv.hex() + encrypted.hex()를 사용한다.
복호화를 수행할 때는 ECB모드로 작동한다..
이걸 가지고 문제를 어떻게 풀 수 있을까? CBC 모드를 자세히 알아보자.
원래 CBC 모드의 복호화는 CBC모드를 역으로 행하는 것으로 우리가 ECB모드를 사용해서 복호화한 것은 여기서 xor과정을 생략한 것이다. 복호화를 하려면 iv를 알아야 하는데, xor을 한 값을 역으로 알아내는 것은 쉽다.
우리는 첫 블록이 crypto{ 로 시작한다는 것을 알고 있다. 그리고 끝은 }로 끝난다.
마침 padding도 없고 딱 맞겠다, 복호화했을 때, 끝 자리가 .. 음?
ciphertext를 줄 때 iv도 준다.. 그럼 그냥 xor만 넣어서 복호화해주면 된다.
import requests
from pwn import *
url = 'https://aes.cryptohack.org/ecbcbcwtf/'
def encrypt_flag():
r = requests.get(url + 'encrypt_flag/')
js = r.json()
return bytes.fromhex(js['ciphertext'])
def decrypt(cipher):
loc_url = url + 'decrypt/'
loc_url += cipher.hex()
loc_url += '/'
r = requests.get(loc_url)
js = r.json()
return bytes.fromhex(js['plaintext'])
ciphertext = encrypt_flag()
flag = b''
for i in range(16, len(ciphertext), 16):
flag += xor(ciphertext[i-16 : i], decrypt(ciphertext[i : i+16]))
print(flag)
ciphertext 내에 iv를 넣어준 이유는 계산을 따로 해줄 필요가 없기 때문이다. 복호화 시에 AES 복호화를 거치고 그 전 암호문으로 xor하기 때문에 python슬라이싱을 이용해서 xor해준다. iv또한 ciphertext에 넣어두면 똑같이 처리하면 된다.