Recovering secrets is hard, but there is always some easy parts!
Files given:
def encrypt(msg, iv, key):
aes = AES.new(key, AES.MODE_CBC, iv)
return aes.encrypt(msg)
enc = encrypt(msg_inp, iv, key).hex()
r = random.randint(0, 4)
s = 4 - r
mask_key = key[:-2].hex() + '*' * 4
mask_enc = enc[:r] + '*' * 28 + enc[32-s:]
pr("| enc =", mask_enc)
pr("| key =", mask_key)
This challenge uses a fixed key and iv to encrypt all messages and allows us to see the encrypted flag. It also allows us to encrypt arbitrary messages, but it hides away 28 out of the 32 hex characters from the first chunk of our ciphertext as well as the last 2 bytes of the key.
| Options:
| [G]et the encrypted flag
| [T]est the encryption
| [Q]uit
G
| encrypt(flag) = 14afc2061c4275bd57c713fa89773f41feacf2116e15baeb48cb2e783a629820
| Options:
| [G]et the encrypted flag
| [T]est the encryption
| [Q]uit
T
| Please send your 32 bytes message to encrypt:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
| enc = 3b61****************************20b259e703858ebb7147c085ec3d8088
| key = 103b7d56ee9b4d763a2d61a9f747****
Evidently the key is bruteforcible, but how do we know the iv? Since this uses CBC mode, the IV for the second block is actually the ciphertext for the first block and the iv and plaintext are simply XORed together before getting encrypted. Hence we can brute force the key and checking using the information about the first block if the guess is correct. Once we have a guess for the key, we can easily recover the first block by a simple XOR operation and with that, recover the IV with a decrypt then XOR.
Solution at solve.py
Flag:
CCTF{h0W_R3cOVER_7He_5eCrET_1V?}