I dropped my message into the bin of Legos. It’s all scrambled up now. Please help.
Initial Analysis
The challenge provides a Python script encoder.py and its corresponding output output.txt. The goal is to recover a flag that has had its bits scrambled based on a seed derived from a hardcoded string.
Seed Generation
The script uses RSA to “encrypt” the string "I_LOVE_RNG":
text = "I_LOVE_RNG"n, seed = RSA_enc(text)# ...enc_seed = pow(seed, e, n)The RSA_enc function calculates seed = pow(plain_num, e, n). In the main function, it then calculates enc_seed = pow(seed, e, n). Since is very small and the plaintext "I_LOVE_RNG" is short, the value (where is the integer representation of the text) is much smaller than the 4096-bit RSA modulus . This means pow(m, e, n) is simply without any modular reduction.
Bit Shuffling
flag_bits = get_flag_bits(flag)for i in range(10): random.seed(seed*(i+1)) random.shuffle(flag_bits)The flag is converted into a list of bits and shuffled 10 times using Python’s random.shuffle. Each shuffle is seeded with a value derived from the seed identified above.
Solution
To recover the flag, we follow these steps:
- Recover the Seed: Calculate the integer value of
"I_LOVE_RNG"and raise it to the 7th power to get theseed. We can verify this againstenc_seedin the output file by checking ifseed**7 == enc_seed. - Reverse the Shuffle: Since
random.shuffleis deterministic when the seed is known, we can track how the positions change. Instead of shuffling the bits directly, we shuffle a list of indices[0, 1, 2, ..., len(bits)-1]. - Reconstruct the Flag: After 10 shuffles, the index list tells us where each original bit ended up. We map the bits from the encoded flag back to their original positions and convert the bit array back into a string.
Solution Script
import random
def get_flag_bits(flag_hex): flag_bytes = bytes.fromhex(flag_hex) bits = [] for b in flag_bytes: bits.extend(list(bin(b)[2:].zfill(8))) return bits
def bit_arr_to_str(bit_arr): byte_arr = [] for i in range(0, len(bit_arr), 8): byte = bit_arr[i:i+8] char = int(''.join(byte), 2) byte_arr.append(char) return bytes(byte_arr)
# Values from output.txtenc_flag_hex = "a9fa3c5e51d4cea498554399848ad14aa0764e15a6a2110b6613f5dc87fa70f17fafbba7eb5a2a5179"text = "I_LOVE_RNG"plain_num = int.from_bytes(text.encode(), "big")seed = plain_num ** 7
flag_bits_enc = get_flag_bits(enc_flag_hex)num_bits = len(flag_bits_enc)indices = list(range(num_bits))
# Replay the shuffle on the indicesfor i in range(10): random.seed(seed * (i + 1)) random.shuffle(indices)
# Map scrambled bits back to original positionsflag_bits_orig = [None] * num_bitsfor j in range(num_bits): flag_bits_orig[indices[j]] = flag_bits_enc[j]
print(bit_arr_to_str(flag_bits_orig).decode())Flag
UMASS{tH4Nk5_f0R_uN5CR4m8L1nG_mY_M3554g3}