Roulette Conspiracy

0xFun CTFby smothy

The Roulette Conspiracy - Crypto

Points: 250 | Flag: 0xfun{m3rs3nn3_tw1st3r_unr4v3l3d} | Solved by: Smothy @ 0xN1umb

hacker vibes

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):

python
def spin(self):
    raw = self.mt.getrandbits(32)
    obfuscated = raw ^ 0xCAFEBABE
    return obfuscated

lmao 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:

  1. outputs are XOR'd with 0xCAFEBABE
  2. XOR back to get raw MT values
  3. untemper each value to recover internal state
  4. clone the PRNG
  5. predict next 10 values

key part - the untemper function reverses MT's tempering:

python
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 y

then just:

python
# 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 ✌️