archlinux / r3t0x@portfolio
ALL SYSTEMS NOMINAL /
← Back to archive · Misc

Securinets Friendly CTF 2026 / Misc WRITEUP

UNIX

Two codes are already on the screen. Predict the next one, Professor Probability. Go on. The machine could use a laugh.

EVENTSecurinets Friendly CTF 2026CATEGORYMiscDIFFICULTYeasyPOINTS200
misc

Walkthrough

Player-facing content

The player receives only the remote endpoint. There are no downloadable files, technical clues, or hints. The service prints two six-digit verification codes and accepts one prediction for the next code.

The public description is intentionally sarcastic:

Two codes are already on the screen. Predict the next one, Professor Probability.

>

Go on. The machine could use a laugh.

Construction

Each connection creates an independent Python random.Random instance seeded with int(time.time()). It draws exactly three values using randint(100000, 999999), discloses the first two, and checks the player's input against the third.

The seed has one-second granularity. A solver records its local time when it connects and tests nearby timestamps. For each candidate it reproduces the first two calls. Matching both visible codes identifies the seed with overwhelming confidence, after which the third call predicts the required answer.

The weakness is the predictable seed, not a statistical defect in the displayed six-digit values. Two outputs are exposed so the correct nearby timestamp can be identified without ambiguity.

Intended solution

  1. Connect and record the two displayed codes.
  2. Enumerate Unix timestamps near the connection time.
  3. For each timestamp, create random.Random(timestamp).
  4. Reproduce two randint(100000, 999999) calls.
  5. When both values match, generate the third value and submit it.

The included organizer solver uses a ten-minute window to tolerate clock skew and manual delays.

Reference solver

The reference solver supplied with the writeup:

#!/usr/bin/env python3

import random
import re
import socket
import sys
import time


host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
port = int(sys.argv[2]) if len(sys.argv) > 2 else 20046

connection_time = int(time.time())
connection = socket.create_connection((host, port), timeout=10)

banner = b""
while b"> " not in banner:
    chunk = connection.recv(4096)
    if not chunk:
        raise SystemExit("The service disconnected before sending its codes.")
    banner += chunk

visible_codes = re.findall(rb"(?m)^(\d{6})\r?$", banner)
if len(visible_codes) != 2:
    raise SystemExit("Could not find the two verification codes.")

first_code = int(visible_codes[0])
second_code = int(visible_codes[1])
current_time = int(time.time())
prediction = None
recovered_seed = None

for seed in range(connection_time - 600, current_time + 6):
    rng = random.Random(seed)
    if rng.randint(100000, 999999) != first_code:
        continue
    if rng.randint(100000, 999999) != second_code:
        continue
    recovered_seed = seed
    prediction = rng.randint(100000, 999999)
    break

if prediction is None:
    raise SystemExit("No matching seed was found.")

print(f"seed: {recovered_seed}")
print(f"next code: {prediction:06d}")
connection.sendall(f"{prediction:06d}\n".encode())

response = b""
while True:
    chunk = connection.recv(4096)
    if not chunk:
        break
    response += chunk

connection.close()
print(response.decode(), end="")

Flag

Securinets{y0u_c4nt_h1d3_th3_s33d_fr0m_t1m3}
Published 2026-09-28 · by r3t0x← Back to archive · Misc
0xSEARCH_INDEX

Trace a memory.

INDEX ONLINE0 addresses
↑↓ TRACEENTER OPENESC DISMISS