Random Password
Random Password
Platform: tamuctf | Category: Misc | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-03-21 | Status: Solved Techniques: deterministic_simulation, forward_dp_search, prng_consumption_tracking, prng_state_prediction
Summary
Task: Password verification using deterministic PRNG with fixed seed, fake sleep consuming random values. Solution: Forward DP tracking reachable PRNG indices to find bit string matching target final value.
Random Password — TAMUctf 2026
Description
Password verification by sleepy counting
The challenge provides a password verification server that uses a deterministic PRNG. The goal is to find a 64-character hex password that passes verification.
- Files:
server.py,solver-template.py - Flag format:
gigem{...}
Analysis
Server Code (Key Parts)
python read-only • copy allowedfrom functools import partial import random random.seed(121728) # Fixed seed - deterministic! def random_sleep(timeout: float) -> None: time_elapsed = 0 while time_elapsed < timeout: time_elapsed += random.random() # Consumes PRNG values handle_zero = partial(random_sleep, 5) # ~10 calls on average handle_one = partial(random_sleep, 17) # ~34 calls on average def verify(password: str, correct_value: float) -> bool: if len(password) != 64: return False bit_str = bin(int(password, 16))[2:].zfill(256) for bit in bit_str: match bit: case '0': handle_zero() case '1': handle_one() return random.random() == correct_value # Must match exactly!
Key Insight
The "sleepy counting" hint is crucial: random_sleep() does NOT call time.sleep(). Instead, it consumes random numbers from a deterministic PRNG (seeded with 121728) by summing random.random() calls until the sum exceeds a threshold (5 for bit '0', 17 for bit '1').
Since the seed is fixed, all random values are fully predictable. The challenge becomes: find a 256-bit string such that after processing all bits, the next random.random() equals exactly 0.9992610559813815.
Problem Structure
- PRNG seed is fixed:
random.seed(121728)makes all values deterministic - Password: 64 hex chars = 256 bits
- Each bit: Consumes a variable number of PRNG calls (depends on actual random values)
- Target: After 256 bits,
random.random() == 0.9992610559813815 - State space: The PRNG index after processing determines the final value
Solution Approach: Forward Dynamic Programming
Since two different bit prefixes reaching the same PRNG index are equivalent for all future choices, we can use forward DP:
- Pre-generate all random values we might need (~20000)
- Find where the target value
0.9992610559813815appears in the sequence - Track reachable PRNG indices at each bit position
- For each state, try both bit choices (0 and 1) and track resulting indices
- After 256 bits, check if any final state matches the target index
Solution
python read-only • copy allowedimport random random.seed(121728) MAX_VALS = 20000 values = [random.random() for _ in range(MAX_VALS)] def simulate_sleep(timeout, start_idx): """Returns the number of random values consumed""" time_elapsed = 0 idx = start_idx while time_elapsed < timeout: time_elapsed += values[idx] idx += 1 return idx - start_idx target = 0.9992610559813815 # Find the target value's position in the pre-generated array target_indices = [i for i in range(MAX_VALS) if values[i] == target] print(f"Target indices: {target_indices}") # [5719] # Forward BFS/DP: track reachable PRNG indices at each bit position current_states = {0: ""} # index -> bit_string_prefix for bit_pos in range(256): next_states = {} for idx, bits in current_states.items(): # Try bit = 0 (timeout = 5) consumed_0 = simulate_sleep(5, idx) new_idx_0 = idx + consumed_0 if new_idx_0 not in next_states: next_states[new_idx_0] = bits + "0" # Try bit = 1 (timeout = 17) consumed_1 = simulate_sleep(17, idx) new_idx_1 = idx + consumed_1 if new_idx_1 not in next_states: next_states[new_idx_1] = bits + "1" current_states = next_states # Check which final states match a target index for target_idx in target_indices: if target_idx in current_states: bit_str = current_states[target_idx] password = hex(int(bit_str, 2))[2:].zfill(64) print(f"Password (hex): {password}")
Results
- Target value
0.9992610559813815found at PRNG index 5719 - Forward DP explored ~4500 reachable states per bit position (manageable)
- Found password:
00000000000000000000000000000000ffffffffefffffffffffffffffffff7f
Submission
python read-only • copy allowedfrom pwn import * io = remote("streams.tamuctf.com", 443, ssl=True, sni="random-password") io.recvuntil(b"Enter the password in hex: ") io.sendline(b"00000000000000000000000000000000ffffffffefffffffffffffffffffff7f") response = io.recvall(timeout=10) # Here's the flag gigem{REDACTED}