$ cat writeup.md…
$ cat writeup.md…
umdctf
Task: a stripped static ELF64 validator checks a 106-byte input in 27 four-byte chunks using several .rodata lookup tables, a custom helper routine, and a tiny bytecode VM. Solution: extract the tables and VM bytecode with pyelftools, evaluate the helper concretely with angr, reimplement the VM and rolling state update in Python, and reconstruct the flag directly from the expected chunk values.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
No separate organizer description was present in the provided workspace files.
English summary: the challenge provides a stripped static Linux ELF called roulette. The program asks for a roulette number, but the actual validation logic is a scripted reverse-engineering problem: it checks a 106-byte string through a helper arithmetic routine, several lookup tables in .rodata, a tiny bytecode VM, and a rolling per-chunk state.
The first step was simple triage:
file roulette checksec --file=roulette strings roulette
That immediately gave the important high-level picture.
file showed a stripped static ELF64 x86-64 binary.
checksec showed:
strings exposed the user-facing messages and confirmed that this was a normal validator rather than a crypto service or packed binary. The most useful strings were:
lets go gambling!submit roulette number:acceptedrejectedJACKPOT! You guessed the winning number.The environment mattered here. I did not have qemu, gdb, rizin, or readelf, so the solve path had to rely on:
objdumppyelftoolsangrThat ended up being enough.
As a precedent, I also looked at the knowledge-base writeup 20260128_htb_flagcasino. That challenge was not the same, but it was a useful reminder that some gambling-themed reverse tasks are easiest to solve by scripting the validator instead of trying to manually recover every intermediate constant.
Using objdump -d on the interesting region showed that the main validation routine starts at 0x401690.
Important observations from that function:
...
$ grep --similar