The Roulette Conspiracy - Crypto
Points: 250 | Flag: 0xfun{m3rs3nn3_tw1st3r_unr4v3l3d} | Solved by: Smothy @ 0xN1umb

what we got
nc service with "spin" and "predict" options. description hints at:
- "Mersenne Oracle with 624 internal spirits" = MT19937 PRNG with 624-state
- "secret is 0xCAFEBABE" = XOR key for obfuscation
source code (roulette.py):
def spin(self):
raw = self.mt.getrandbits(32)
obfuscated = raw ^ 0xCAFEBABE
return obfuscatedlmao they literally XOR the output with a known constant. classic.
the solve
MT19937 state recovery is a well-known attack. if you can observe 624 consecutive 32-bit outputs, you can fully reconstruct the internal state and predict all future outputs.
the trick here:
- outputs are XOR'd with
0xCAFEBABE - XOR back to get raw MT values
- untemper each value to recover internal state
- clone the PRNG
- predict next 10 values
key part - the untemper function reverses MT's tempering:
def untemper(y):
y = y ^ (y >> 18)
y = y ^ ((y << 15) & 0xEFC60000)
t = y
for _ in range(4):
t = y ^ ((t << 7) & 0x9D2C5680)
y = t
y = y ^ (y >> 11) ^ (y >> 22)
return ythen just:
# collect 624 spins, XOR with 0xCAFEBABE
outputs = []
for i in range(624):
recv_until(b'> ')
s.sendall(b'spin\n')
result = recv_line()
raw = int(result) ^ 0xCAFEBABE
outputs.append(raw)
# reconstruct state
state = [untemper(o) for o in outputs]
mt = random.Random()
mt.setstate((3, tuple(state) + (624,), None))
# predict next 10
predictions = [mt.getrandbits(32) for _ in range(10)]server wants 10 RAW values (not XOR'd), send em space-separated, ez clap.
flag
0xfun{m3rs3nn3_tw1st3r_unr4v3l3d}
ngl this one was straightforward if you know MT19937 state recovery. 624 outputs = full state reconstruction. the XOR obfuscation was just a red herring, doesn't add any security lol
smothy out ✌️