I am trying to solve another CTF challenge. The challenge consists in trying to exploit an oracle that decrypts any hex text we send (see code below). I am kind of stuck on this one as this is not a classical challenge, since the decrypted text is not displayed. My guess would be one should exploit the fact that exceptions are printed to find some detail regarding the key, and once the key is found, decrypt the flag sent at first, but this is just a random guess from my side.
Do you have any idea on how can I approach the problem or some hints?
Code:
#!/usr/bin/env python3
import signal
import os
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
TIMEOUT = 300
BLOCK_SIZE = 16
assert("FLAG" in os.environ)
flag = os.environ["FLAG"]
assert(flag.startswith("CCIT{"))
assert(flag.endswith("}"))
key = os.urandom(BLOCK_SIZE)
iv = os.urandom(BLOCK_SIZE)
print("Hello! Here's an encrypted flag")
cipher = AES.new(key, AES.MODE_CBC, iv)
print(iv.hex()+cipher.encrypt(pad(flag.encode(), BLOCK_SIZE)).hex())
def handle():
while True:
try:
dec = bytes.fromhex(
input("What do you want to decrypt (in hex)? ").strip())
cipher = AES.new(key, AES.MODE_CBC, dec[:BLOCK_SIZE])
decrypted = cipher.decrypt(dec[BLOCK_SIZE:])
decrypted_and_unpadded = unpad(decrypted, BLOCK_SIZE)
print("Wow you are so strong at decrypting!")
except Exception as e:
print(e)
if __name__ == "__main__":
signal.alarm(TIMEOUT)
handle()
My current approach (to bruteforce the first 2 bytes):
from pwn import *
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
import os
r = remote('padding.challs.cyberchallenge.it', 9033)
from Crypto.Util.Padding import pad, unpad
r.recvline()
ciphertext = bytes.fromhex(r.recvline().strip().decode())
BLOCK_SIZE = 16
BLOCK_N = len(ciphertext) // BLOCK_SIZE
iv = ciphertext[:BLOCK_SIZE]
encrypted_flag = ciphertext[BLOCK_SIZE:]
FIRST_BYTE = 47
def bruteforceByte(currentByte, ciphertext, lastDecryptedValue):
if (currentByte == FIRST_BYTE):
lastDecryptedValue = 1
for guess in range(256):
craftedblock = ciphertext[:currentByte]
craftedblock += chr(guess).encode()
craftedblock += ciphertext[currentByte+1:]
r.recvuntil(b'(in hex)?')
r.sendline(craftedblock.hex())
output = r.recvline().strip()
if (output.startswith(b'Wow you')):
paddingValue = FIRST_BYTE-currentByte+1
d = bytes([guess ^ paddingValue])
p = bytes([ciphertext[currentByte] ^ d[0]])
newCipher = ciphertext[:currentByte]+bytes(ciphertext[currentByte] ^ lastDecryptedValue)+ciphertext[currentByte+1:]
print(f"Plaintext: {p}")
return newCipher, d
return None, None
lastDecryptedValue = 1
for currentByte in range(FIRST_BYTE, -1, -1):
print(f"Working on byte {currentByte}")
ciphertext , lastDecryptedValue = bruteforceByte(currentByte, ciphertext, lastDecryptedValue)
```