The Slot Whisperer | 0xFun CTF 2026
a writeup for "The Slot Whisperer" in 0xFun CTF 2026.
The Slot Whisperer | 0xFun CTF 2026
Overview
- Platform: 0xFun
- Challenge: The Slot Whisperer
- Categories: Crypto
- Rating: 9/10
- Difficulty: Easy
- Sovling Time: ~15 Minutes
Challenge
“The oldest slot machine in the casino runs on predictable gears. Watch it spin, learn its rhythm, and predict what comes next.”
Walkthrough
Recon
It looks like normal flawed random number generator
The Machine Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class SlotMachineLCG:
def __init__(self, seed=None):
self.M = 2147483647
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
if __name__ == "__main__":
print("Slot Machine LCG Test")
print("=" * 40)
lcg = SlotMachineLCG(seed=12345)
print("First 10 spins:")
for i in range(10):
spin = lcg.spin()
print(f"Spin {i+1:2d}: {spin:2d}")
Solution Approach
“Flag format:
0xfun{}”
The parameters provided are:
- M = 2,147,483,647 (A Mersenne Prime, 2^31-1)
- A = 48,271
- C = 12,345
- The machine doesn’t show us the full internal state
Snit only shows usspin = S_n % 100. The Vulnerability LCGs are entirely deterministic. If you know the internal stateSnat any point, you can calculate all future “random” numbers. While we are only given the last two digits of the state (the remainder when divided by 100), the modulus M is small enough for a brute-force attack.Solving Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# LCG Parameters provided in the challenge
M = 2147483647
A = 48271
C = 12345
# The sequence observed from the nc connection
observed = [29, 10, 42, 89, 33, 73, 10, 92, 19, 73]
def get_next_state(state):
return (A * state + C) % M
def solve():
print(f"[*] Brute-forcing state for sequence: {observed}")
# The first spin is 39. Therefore, state_0 = 100 * k + 39
# We iterate through all possible values of k
for k in range(M // 100 + 1):
state = 100 * k + observed[0]
if state >= M:
break
current_state = state
match = True
# Verify if this candidate state produces the rest of the sequence
for i in range(1, len(observed)):
current_state = get_next_state(current_state)
if current_state % 100 != observed[i]:
match = False
break
if match:
print(f"[+] Found internal state: {state}")
# Calculate the next 5 spins
predictions = []
temp_state = current_state # state after the 10th spin
for _ in range(5):
temp_state = get_next_state(temp_state)
predictions.append(temp_state % 100)
return predictions
return None
if __name__ == "__main__":
result = solve()
if result:
print("[!] Next 5 spins (predict these):")
print(" ".join(map(str, result)))
else:
print("[-] State not found. Double check your input sequence.")
Flag
0xfun{sl0t_wh1sp3r3r_lcg_cr4ck3d}
Hope you enjoyed the writeup, Don’t forget to leave a comment or a star on the github repo (;
This post is licensed under CC BY 4.0 by the author.



