#!/usr/bin/env python3 # exploit.py # Try date variants and Unit options for the provided OPERATION REPORT template, # derive key prefix from c1 and decrypt c2. Look for CTF{...}. import binascii import re # Ciphertexts (exact hex strings provided earlier in this conversation) C1_HEX = "7a4ca3bb8c3a8b4e2f5b399a1ff9b911691c07ed68e0636c0ca1d10c4786c3ad1880e2675a2c9e260072080afdfc80e93141556487fbf8099c54b971b8f6a5ed62954984d943761f6591888032782a8c1a63826a398200ddebf50ac02dbf6287ef744b1b767e95581f9baa8771626496392757be7df4eb9e633630bcb0b4" C2_HEX = "7653a8af842a874f35322a936ffbae16301921dc078d0a097bd8a646048785ac5fd5c53a462b975971704b3de8c7beaf0c5b0463acaaeb228000b01286e1eafd739159d1dd0a770721958483515061f2296f897871c6329d8d9310dc32af27abfb314a1f19" c1 = binascii.unhexlify(C1_HEX) c2 = binascii.unhexlify(C2_HEX) # Template and choices date_variants = ["14-11-25", "14-11-2025"] # try both dd-mm-yy and dd-mm-yyyy units = ["Alpha", "Beta", "Delta"] def make_p1(date_str: str, unit: str) -> bytes: """Construct plaintext candidate for message 1 using the template exactly.""" s = ( "OPERATION REPORT\n" f"Date: {date_str}\n" f"Unit: {unit}\n" "Subject: Routine status update\n" "Details: All systems operational.\n" "End of report.\n" ) return s.encode("utf-8") def derive_key_prefix_from_p1(c1_bytes: bytes, p1_bytes: bytes) -> bytes: """Derive the key prefix by XORing c1 and the candidate p1 bytes where p1 exists.""" L = min(len(c1_bytes), len(p1_bytes)) return bytes([c1_bytes[i] ^ p1_bytes[i] for i in range(L)]) def decrypt_with_key_prefix(c2_bytes: bytes, key_prefix: bytes) -> str: """Decrypt the prefix of c2 using the derived key prefix, leave rest unknown as '?'.""" out_chars = [] for i in range(len(c2_bytes)): if i < len(key_prefix): out_chars.append(chr(c2_bytes[i] ^ key_prefix[i])) else: out_chars.append("?") return "".join(out_chars) flag_re = re.compile(r"WITCTF\{[A-Za-z0-9_!@#\$%\^&*\-:.?+=/\\]+\}") found = False results = [] for date_str in date_variants: for unit in units: p1 = make_p1(date_str, unit) # if p1 longer than c1, we can still derive prefix (we will take min inside function) key_pref = derive_key_prefix_from_p1(c1, p1) dec = decrypt_with_key_prefix(c2, key_pref) # Search for FLAG m = flag_re.search(dec) info = { "date": date_str, "unit": unit, "derived_key_len": len(key_pref), "decrypted_preview": dec[:300] } results.append(info) print("-" * 72) print(f"Trying Date='{date_str}' Unit='{unit}' (derived key bytes: {len(key_pref)})") print("Decrypted preview (unknowns shown as '?'):\n") print(dec[:300]) if m: print("\n>>> FLAG FOUND:", m.group(0)) found = True if not found: print("\nNo flag found with the tried date/unit combinations.") else: print("\nFinished.")