247CTF - The Flag Lottery

Guess a random number to win the flag lottery. The server generates the winning number using a seeded PRNG.

Vulnerability

The server seeds the random number generator with the current Unix timestamp, which is predictable. Additionally, Python 2 and Python 3 have different PRNG implementations and string conversion behaviors.

Solution

The server code is vulnerable because:

  1. Predictable seed: Unix timestamp is public knowledge
  2. Time window: Even if time is slightly off, we can try nearby timestamps
  3. Version differences: Python 2's str() truncates floats, making predictions consistent

Use Python 2 to generate predictions for timestamps around the server's current time:

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
import subprocess
import time
from pwn import *

host = "13721472f3c35e88.247ctf.com"
port = 50316
context.log_level = "error"

# Try timestamps within a ±10 second window
for offset in range(0, 10):
r = remote(host, port)
prompt = r.recvline()

target_time = int(time.time()) + offset

# Generate number using Python 2 seeding
cmd = ["python2", "get_legacy_random.py", str(target_time)]
payload = subprocess.check_output(cmd).decode().strip()

print(f"[*] Predicting for time {target_time} (offset +{offset}s): {payload}")

r.sendline(payload.encode())
response = r.recvall().decode()

if "247" in response:
print(f"[SUCCESS] {response.strip()}")
r.interactive()
break
else:
print(f"[-] Failed: {response.strip()}")
r.close()

Helper script (get_legacy_random.py):

1
2
3
4
5
6
7
import random
import sys

target_time = int(sys.argv[1])
secret = random.Random()
secret.seed(target_time)
print str(secret.random())

Key Insight

PRNGs seeded with time are not cryptographically secure. Use secrets or os.urandom() for security-sensitive randomness. Additionally, always be aware of version-specific differences in language implementations when predicting values.

247CTF{3c435fe8a89cb0c65fdfcf0089669808}