$ cat writeup.md…
$ cat writeup.md…
kitctf
Task: Linux kernel pwn with a patched eBPF verifier (patched vs unpatched bzImage) running an embedded aya eBPF reservation contract; the verifier's map-value bounds check was deleted and an embedded program does `DATA[index+1]=roll` with a signed-only index check. Solution: kernel-diff to find the removed check_mem_region_access bounds check, reverse the embedded BPF, then send index -1 to OOB-write the success byte DATA[0] (data-only), coasting with index 100 to preserve it until the validator prints the flag.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
I recently learned that Solana contracts are just ebpf bytecode and thought: "Why do I need a Solana vm for that if my kernel can just execute it". So, after some tinkering, I let my kernel do what it does best: execute user provided code to enable gambling (and make me rich).
A QEMU guest runs a Rust "restaurant reservation smart contract" (/usr/bin/stupidcontract,
built with the aya eBPF library). It prints 100 restaurant names, then runs 300 rounds:
each round asks for a restaurant index, and a reservation "succeeds" with ~20% probability
(kernel bpf_get_prandom_u32). The win condition is a reservation to all 100 restaurants,
after which the binary reads /tmp/flag-data/flag and prints it. 300 rounds for 100
restaurants is intentionally not winnable by RNG alone.
Provided: images/patched.bzImage + images/unpatched.bzImage (Linux 6.19.5), rootfs.ext2,
run-qemu.sh, Dockerfile/compose.yml (socat exposes the QEMU serial on TCP/1337).
The only stdin input is the decimal index. There is no user-supplied BPF program — the
eBPF is embedded in the binary. The num_instructions string in the binary is an aya-internal
BTF relocation field name (a red herring). The bug is reached purely through the index value
combined with the deliberately weakened kernel verifier.
The patched/unpatched bzImage pair is the loudest hint. Extract vmlinux ELFs and diff
kallsyms:
vmlinux-to-elf images/patched.bzImage patched.elf vmlinux-to-elf images/unpatched.bzImage unpatched.elf nm patched.elf | sort > patched.syms nm unpatched.elf | sort > unpatched.syms diff patched.syms unpatched.syms
Symbol diff:
__check_mem_access, check_mem_region_access, bpf_checkcheck_map_access.isra.0 (inlined), bpf_prepare (= renamed bpf_check, cosmetic)Disassemble both versions of the map-value access checker (bounded by the next symbol):
...
$ grep --similar