247CTF - The Flag Lottery

posted
read
182 words · 1 min
contents

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:

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), specifically for Python 2 syntax; it is not Python 3 source. The server-side generator and Python 2 runtime are unavailable, so neither prediction equality nor the timing window has been reproduced:

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.

The returned flag belongs to the launched service instance; no cross-instance invariance is established. Record the generation/retrieval method and use the current instance response instead of a fixed-answer spoiler.