Randomness
Randomness
Platform: spbctf | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-06-24 | Status: Solved Techniques: docker_amd64_emulation, dynamic_analysis, prng_prediction, time_based_seed_reproduction
Summary
Task: an x86-64 C binary gates the flag behind a 7-char check where each expected char is random() % 26 + 'a' from a time-seeded glibc PRNG. Solution: reproduce srandom(time(0)) in the same second, pipe predicted chars to pass the gate, and let the binary print the flag itself instead of reversing its bytecode VM.
Randomness — spbctf (rev-kids20)
Description
Flag is the input that makes the app not say 'Wrong' :)
We are given randomness.elf, an x86-64 ELF binary. It prompts for 7 letters; if all of them match an internally generated sequence, it prints the flag instead of saying "Wrong".
Analysis
Binary overview
randomness.elf: ELF 64-bit LSB executable, x86-64, dynamically linked, not stripped, GNU/Linux.- Imports:
srandom,random,time,getchar,printf,puts. - Notable symbols:
main@0x400f2c,genflag@0x4007c6, data_1_genflag_$array@0x602080,_1_genflag_$strings@0x6026c0. - Strings in
.rodata:"Guess random english letter: ","Wrong","%s\n".
main logic (@ 0x400f2c)
c read-only • copy allowedsrandom(time(0)); for (i = 0; i <= 6; i++) { // 7 iterations long r = random(); char expected = (r % 26) + 'a'; // compiled as multiply by 0x4ec4ec4ec4ec4ec5, sar 3 (division by 26) printf("Guess random english letter: "); int guess = getchar(); // read the guess char getchar(); // consume the trailing newline if (expected != (char)guess) { puts("Wrong"); return 1; } } // only reached if all 7 chars are correct: for (i = 0; i <= 6; i++) genflag(i); // first VM pass for (i = 7; i <= 26; i++) genflag(i); // second VM pass printf("%s\n", 0x6026e0); // prints the generated flag buffer return 0;
The % 26 appears in the disassembly as a signed multiply by the magic
constant 0x4ec4ec4ec4ec4ec5 followed by sar 3 — the standard compiler
idiom for division/modulo by a constant (26).
genflag (@ 0x4007c6) — the red herring
genflag is a small stack-based bytecode VM. It reads opcodes from
_1_genflag_$array (@ 0x602080) and dispatches through a jump table at
0x4010d0:
asm read-only • copy allowedmov rax, [8*opcode + 0x4010d0] jmp rax
It maintains a value stack (sp in [rbp-0x160], base near [rbp-0x140]) and
supports push/pop/dup/swap, arithmetic (add, imul, xor, shl),
comparisons (setl, sete), conditional jumps, and load/store of various
widths. It writes characters one at a time into an output buffer at
0x6026e0. This VM is pure obfuscation: it constructs the flag string
REDACTED into 0x6026e0, which main then prints.
Key insight
The 7-character "password" gate is entirely PRNG-driven. Each expected char is
random() % 26 + 'a', and the PRNG is seeded with srandom(time(0)). Because
the seed is the current Unix time in seconds, the expected sequence is fully
predictable: reproduce the same srandom(time(0)) with the same glibc inside
the same wall-clock second, and you know all 7 answers.
You never have to reverse the VM — once the gate is passed, the program prints the real flag itself.
Solution
- Write a tiny C helper that reproduces the gate sequence using the same glibc PRNG.
- Each guess needs a char plus a newline (main calls
getchar()twice per iteration — one for the guess, one to eat the newline). - Pipe the helper's output into the binary within the same wall-clock second so the
time(0)seed matches.
solve.c:
c read-only • copy allowed#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { time_t t = time(0); srandom(t); for (int i = 0; i < 7; i++) printf("%c\n", (char)(random() % 26 + 'a')); // char + newline per guess return 0; }
Run on Linux x86-64. On Apple Silicon, force --platform linux/amd64 (Rosetta
otherwise fails with failed to open elf at /lib64/ld-linux-x86-64.so.2):
bash read-only • copy alloweddocker run --rm --platform linux/amd64 -v "$PWD":/w -w /w gcc:12 bash -c ' gcc -O0 solve.c -o solve chmod +x randomness.elf ./solve | ./randomness.elf '
Output:
Guess random english letter: Guess random english letter: ... (x7)
REDACTED
The helper and the binary share the same time-seeded glibc PRNG, so all 7 predicted characters match, the gate passes, and the binary prints the flag.