$ cat writeup.md…
$ cat writeup.md…
UIUC CTF 2026
Task: Lean 4 jail requiring entry : Nat -> Nat that passes a recursive dependency checker blocking IO/System roots; binary runs as runner who can read /flag.txt. Solution: axiom-based Eq.mp type confusion leaks closure code pointers for PIE base, then reset/reuse optimization rewrites closure m_fun to redirect calls to l_IO_FS_readFile and lean_get_stdout for flag exfiltration.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
State and prove the Riemann hypothesis in Lean.
Submit your proof as a single base64-encoded line, then press Enter.
The challenge provides a Lean 4 jail (v4.26.0-rc2) that accepts a base64-encoded Lean source file. The submission must export a safe declaration entry : Nat → Nat from module Submission. A post-compilation checker (Check.lean) recursively traces all dependencies and rejects anything rooted at IO, EIO, BaseIO, ST, EST, System, Task, Runtime, or Lean, plus specific names unsafeCast, withPtrEq, sorryAx. Unsafe/partial declarations and compiler attributes (extern, implemented_by, export, init, csimp) are also rejected. A regex prefilter blocks source-level keywords like #eval, run_cmd, unsafe, partial, opaque, sorry, macro, elab, etc.
The generated C is compiled with leanc -O2 (statically linking the Lean runtime) and executed as user runner who has group read access to /flag.txt. The service returns stdout.
The checker validates declaration safety and traces dependencies through kernel expression trees but does not reject local axiom declarations. An axiom like:
axiom allEq (a b : Type) : a = b
declares a proof that any two types are equal. While logically inconsistent, this is perfectly valid Lean syntax that passes both the regex prefilter and the dependency checker — allEq has no dependencies on forbidden roots.
...
$ grep --similar