$ cat writeup.md…
$ cat writeup.md…
UIUCTF 2026
Task: A stripped x86-64 PIE serializes input into lambda-calculus and Scott-encoded structures, then compares it with a fixed target. Solution: Factor the checker, match the length-only tree shape, and recover shifted input pairs with equal-length differential probes.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Strange glyphs inscribed on the wall rearrange themselves chaotically. Will they respond if you call out to them?
The program accepts one command-line argument and prints either a rejection or an acceptance word. The goal is to recover an argument in the public uiuctf{...} format that reaches the accepting result.
Initial triage identifies a stripped x86-64 PIE. Its large generated data and randomized-looking execution obscure a much smaller semantic model:
The last property is decisive. It means content must be changed without changing length. Deleting a cell changes the schedule and globally rewrites the tree, so deletion-based peeling is not a valid local inverse.
The constructed source graph contains a fixed checker at stable heap offsets:
| Heap offset | Meaning |
|---|---|
0x480bb20 | standard call-by-name Y combinator |
0x480bb40 | generic recursive comparison function F |
0x480bba0 | fixed encoded TARGET |
0x480bbe0 | App(App(Y, F), TARGET) |
Thus the 61,719-node checker is not one bespoke transducer. It is a generic recursive equality routine specialized with one constant:
CHECKER = ((Y F) TARGET)
The surrounding final expression applies this predicate to the serialized input and then to the rejection and acceptance results. inspect_checker_highlevel.py and the heap AST helpers reproduce the factorization and administrative reductions.
...
$ grep --similar