$ cat writeup.md…
$ cat writeup.md…
asisctf2026
Task: a custom arena service exposes BUF/VIEW/PATCH/JOB objects and validates opaque handles against slot metadata. Solution: use unsigned-wrap OOB access to rewrite a recycled BUF back into a forged JOB, then run the revived stale handle and read the inherited memfd flag descriptor.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
A very normal arena manager where nothing bad can happen after you press “rollback.”
We are given a custom line-based service and a remote endpoint. Despite the theme, the winning path does not require ROLLBACK at all.
The protocol supports these commands:
BUFVIEWPATCHDROPJOBSNAPROLLBACKRUNQUITThe service manages 24 fixed slots of size 0x98. Each slot has a common header at the slot base:
+0x00: active byte+0x01: type byte+0x04: generationBUF objects use raw storage at slot+0x8 with size 0x90. JOB objects also start at slot+0x8 and have this layout:
+0x00: magic 0x415379b7+0x04: selector (0x415347dc normal, 0x4153b0d8 shell)+0x0c: path length+0x10: cookie+0x18: 64-bit integrity+0x20: 32-bit integrity+0x24: pathHandles are XOR-obfuscated and encode object type, global epoch, slot generation, and slot index. Validation succeeds only if those decoded fields match the current slot header. That detail matters because stale handles become usable again once the old slot metadata is restored.
The bug is in VIEW and PATCH: offsets are effectively treated as unsigned 64-bit values. A wrapped offset such as 2^64-8 reaches eight bytes before slot+0x8, which means the header of the same slot becomes readable and writable from a BUF handle.
Another crucial property is slot reuse. After DROP, allocating a new object in the same slot does not clear the old payload. So a dropped JOB leaves its structured contents behind, and a new BUF in that slot can leak and edit them.
...
$ grep --similar