SlotWhisperer

0xFun CTFby smothy

The Slot Whisperer - Crypto

Points: 50 | Flag: 0xfun{sl0t_wh1sp3r3r_lcg_cr4ck3d} | Solved by: Smothy @ 0xN1umb

jackpot

what we got

nc connection and a python file showing a slot machine. classic LCG vibes:

python
class SlotMachineLCG:
    def __init__(self, seed=None):
        self.M = 2147483647  # 2^31 - 1
        self.A = 48271
        self.C = 12345
        self.state = seed if seed is not None else 1

    def next(self):
        self.state = (self.A * self.state + self.C) % self.M
        return self.state

    def spin(self):
        return self.next() % 100

server gives us 10 spins and wants us to predict the next 5. ez.

the solve

LCG = Linear Congruential Generator. literally the most predictable "random" ever lmao

the math is just: state = (A * state + C) % M

we only see state % 100 tho, so we gotta recover the full internal state. but like... we know all the parameters (A, C, M) so we can just brute force it

for each spin value (0-99), there's about 21 million possible internal states that could produce it. sounds like a lot but nah - we check if a candidate state produces ALL 10 observed outputs. most fail immediately on the 2nd check so its fast af

python
from pwn import *

M = 2147483647
A = 48271
C = 12345

def crack_lcg(outputs):
    o0 = outputs[0]
    for k in range(M // 100 + 1):
        state = 100 * k + o0
        if state >= M:
            break

        # check if this state produces all outputs
        valid = True
        test_state = state
        for expected in outputs:
            if test_state % 100 != expected:
                valid = False
                break
            test_state = (A * test_state + C) % M

        if valid:
            return test_state  # return state AFTER observations
    return None

def predict_next(state, n=5):
    predictions = []
    for _ in range(n):
        predictions.append(state % 100)
        state = (A * state + C) % M
    return predictions

r = remote('chall.0xfun.org', 48289)

# grab 10 spins
outputs = [int(r.recvline().decode().strip()) for _ in range(10)]
print(f"Observed: {outputs}")

# crack it
next_state = crack_lcg(outputs)
predictions = predict_next(next_state, 5)
print(f"Predictions: {predictions}")

# send it
r.recvuntil(b"Predict the next 5 spins (space-separated):")
r.sendline(' '.join(map(str, predictions)).encode())
print(r.recvall(timeout=3).decode())

run it:

Observed spins: [79, 94, 26, 3, 2, 76, 37, 5, 97, 41] Cracking LCG state... Found next state: 986752626 Predictions: [26, 31, 60, 42, 28] JACKPOT! You've mastered the slot machine! 0xfun{sl0t_wh1sp3r3r_lcg_cr4ck3d}

flag

0xfun{sl0t_wh1sp3r3r_lcg_cr4ck3d}

ngl this is why you never use LCG for anything security related. took like 2 seconds to crack lol


smothy out ✌️