Здесь ты можешь шифровать в режиме CBC, но расшифровывать только в режиме ECB. Казалось бы, это не должно быть уязвимостью, ведь это разные режимы работы… правда?
Исходный код
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}
Зашифрованный флаг:
{"ciphertext":"45a0b258713724604831c4011bab47c6d394938e19fa93b5cb54544cce8ba6b361ed3cb7e39518a514ba86f591247f9e"}
Режим сцепления блоков шифротекста (CBC)
Сцепление оснащает блочный шифротекст механизмом обратной связи: шифрование последующего блока зависит от значения предыдущего. К предыдущему зашифрованному блоку применяется XOR с текущим блоком, а затем — алгоритм шифрования. Выглядит это так:

Для первого блока используется вектор инициализации. Он задаётся вручную или генерируется.
Для дешифровки алгоритм применяется в обратном порядке. Блоки мы берём от конца к началу. Каждый блок шифротекста расшифровывается с помощью ключа, затем происходит XOR с предшествующим блоком.
Решение
Режим работы ECB позволяет нам дешифровывать блоки независимо. Значит, нужно взять последний блок, дешифровать его, проксорить с предыдущим. Вот мы и получили открытый текст для этого блока.
Буду писать код на Python:
import requests
def encrypt():
url = 'http://aes.cryptohack.org/ecbcbcwtf/encrypt_flag/'
response = requests.get(url)
if response.status_code == 200:
return response.json()['ciphertext']
return None
def decrypt(ciphertext):
url = f'http://aes.cryptohack.org/ecbcbcwtf/decrypt/{ciphertext}/'
response = requests.get(url)
if response.status_code == 200:
return response.json()['plaintext']
return None
def main():
flag = []
ciphertext = encrypt()
blocks = [ciphertext[i:i+32] for i in range(0, len(ciphertext), 32)]
for i in range(len(blocks)-1, 0, -1):
xored = bytes.fromhex(decrypt(blocks[i]))
prev_block = bytes.fromhex(blocks[i-1])
plaintext = bytes(a ^ b for a, b in zip(xored, prev_block)).decode()
flag.append(''.join(plaintext))
print(''.join(flag[::-1]))
return 1
if __name__ == '__main__':
main()
iv для первого блока мне не известен. Угадать его будет сложно. Поэтому попробую посмотреть ту часть текста, что я точно могу расшифровать.
crypto{3cb_5uck5_4v01d_17_!!!!!}
Это и оказался флаг.