Hearts2Hearts
Givenh2h.pycandflag.jpg.enc. Recover the AES key/IV from the compiled Python and decrypt the image.

Overview
This challenge provides two files: h2h.pyc and flag.jpg.enc. To decrypt flag.jpg.enc we
first have to understand h2h.pyc. We decompiled it with the online tool
pylingual.io, which recovered the following source:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
def lcg(seed, a, c, m, size):
key = []
for _ in range(size):
seed = (a * seed + c) % m
key.append(seed % 256)
return bytes(key)
def generate_iv(seed, rounds=4):
def round_function(data, key):
return (data << 1 ^ key) % 256
left = seed[:8]
right = seed[8:]
for _ in range(rounds):
new_left = right
new_right = bytes([left[i] ^ round_function(right[i], i) for i in range(8)])
left, right = (new_left, new_right)
return left + right
def generate_key_and_iv():
seed = b'nyomanayucarmenita'
lcg_seed = sum(seed)
key = lcg(lcg_seed, a=1664525, c=1013904223, m=4294967296, size=16)
iv = generate_iv(seed)
print(key, iv)
return (key, iv)
def encrypt_flag():
key, iv = generate_key_and_iv()
cipher = AES.new(key, AES.MODE_CBC, iv)
try:
with open('flag.jpg', 'rb') as f:
flag = f.read()
flag = pad(flag, 16)
encrypted_flag = cipher.encrypt(flag)
with open('flag.jpg.enc', 'wb') as f:
f.write(encrypted_flag)
print('Flag encrypted successfully')
except FileNotFoundError:
print('flag.jpg not found!')
def main():
encrypt_flag()
if __name__ == '__main__':
main()
Analysis
Everything needed to reproduce the key and IV is hard-coded:
- The AES key is generated by a Linear Congruential Generator (LCG) seeded with
sum(b'nyomanayucarmenita'), using the well-known Numerical Recipes constantsa = 1664525,c = 1013904223,m = 2³², producing 16 bytes. - The IV is derived from a small 4-round Feistel-like function over the constant seed
b'nyomanayucarmenita'.
Since both the key and IV are fully deterministic from constants baked into the binary, we can simply re-run the same generation functions and then decrypt in AES-CBC mode.
Solution
We reuse the original generate_key_and_iv() (call it recover_key_and_iv()) and add a
decrypt_flag():
def decrypt_flag():
"""Decrypt the encrypted flag file."""
key, iv = recover_key_and_iv()
try:
with open('flag.jpg.enc', 'rb') as f:
encrypted_flag = f.read()
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_flag = unpad(cipher.decrypt(encrypted_flag), 16)
with open('recovered_flag.jpg', 'wb') as f:
f.write(decrypted_flag)
print('Flag decrypted successfully and saved as recovered_flag.jpg')
except FileNotFoundError:
print('flag.jpg.enc not found!')
except ValueError as e:
...
Running the decryptor produces recovered_flag.jpg, which contains the flag:

Flag
CTFITB{4D333kKk_c4RM3n_k0K_lUCu_b4n93777}