https://app.hackthebox.com/challenges/415

Description

In a parallel universe, "trick-or-treat" is played by different rules. As technologies became more advanced and the demand for security researchers increased, the government decided to incorporate security concepts into every game and tradition. Instead of candy, kids have the choice of selecting a AES mode and encrypting their plaintext. If they somehow manage to find the FLAG, they get candy. Can you solve this basic problem for the toddlers of this universe?

Exploitation

#!/usr/bin/env python3
from pwn import *
import json

def get_process():
    if len(sys.argv) > 1:
        ip, port = sys.argv[1].split(':')
        return remote(ip, int(port))
    print(f"Usage: {sys.argv[0]} <ip:port>")
    sys.exit(1)

def send_json(s, data):
    s.sendline(json.dumps(data).encode())

def get_ctr_mode():
    while True:
        r = get_process()
        initial = r.recvuntil(b'>').decode()
        if 'CTR' in initial:
            log.success("Got CTR mode!")
            return r
        r.close()
        log.info("Retrying for CTR mode...")

def perform_attack():
    r = get_ctr_mode()
    send_json(r, {"option": "1"})
    r.recvuntil(b'{"response": "encrypted", "ciphertext": "')
    flag_ct = r.recvuntil(b'"').decode().strip('"')
    log.info(f"Flag ciphertext: {flag_ct}")
    pt_len = len(flag_ct) // 2
    known_pt = "A" * pt_len
    send_json(r, {"option": "2"})
    r.recvuntil(b'Enter plaintext:')
    send_json(r, {"plaintext": known_pt})
    r.recvuntil(b'{"response": "encrypted", "ciphertext": "')
    known_ct = r.recvuntil(b'"').decode().strip('"')
    log.info(f"Known plaintext ciphertext: {known_ct}")
    flag_bytes = xor(
        bytes.fromhex(flag_ct),
        known_pt.encode(),
        bytes.fromhex(known_ct)
    )
    try:
        flag_text = ""
        for b in flag_bytes:
            if b < 32 or b > 126:
                break
            flag_text += chr(b)
        if "}" in flag_text:
            flag = flag_text[:flag_text.index("}") + 1]
            log.success(f"Flag: {flag}")
        else:
            log.error("Could not find end of flag")
            log.info(f"Raw bytes: {flag_bytes.hex()}")         
    except Exception as e:
        log.error(f"Error processing flag: {e}")
        log.info(f"Raw bytes: {flag_bytes.hex()}")
    
    r.close()

if __name__ == "__main__":
    perform_attack()

Summary

Whole Lotta Candy: abuse the AES misuse, derive the missing key material, and decrypt the flag.