throughthewall
b01lersc
Task: exploit a Linux kernel firewall module in a QEMU guest with PTI, KASLR, SMEP, and SMAP enabled. Solution: turn a stale kmalloc-1024 rule object into a pipe_buffer KASLR leak, then use msg_msg corruption for arbitrary read and unlink-based writes to swap current->cred pointers to init_cred.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
throughthewall — b01lersc (BCTF 2026)
Description
Goal: Exploit the firewall module!
We are given a kernel-pwn challenge with three files: bzImage, initramfs.cpio.gz, and start.sh. The guest boots with pti=on, kaslr, SMEP, SMAP, and -smp 2, and the goal is to gain root inside the VM and read /flag.txt.
Files and environment
The provided launcher is:
qemu-system-x86_64 \ -m 256M \ -nographic \ -kernel ./bzImage \ -append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on kaslr" \ -no-reboot \ -cpu qemu64,+smep,+smap \ -smp 2 \ -initrd ./initramfs.cpio.gz \ -monitor /dev/null \ -s
Important initramfs observations:
/dev/firewallis world-writable./dev/ptmxexists, butdevptsis not mounted, so the usualtty_structptmx spray path is not practical.- SysV message queues are available.
- Pipes are available.
dmesgis readable viaklogctl, which turns kernel log output into a very strong infoleak source.
Vulnerable interface
The challenge module is firewall.ko, exposing four ioctls:
#define FW_ADD_RULE 0x41004601 #define FW_DEL_RULE 0x40044602 #define FW_EDIT_RULE 0x44184603 #define FW_SHOW_RULE 0x84184604
Each firewall rule is a 0x400-byte object, so the interesting allocations live in kmalloc-1024.
The core bug is in delete: the module does kfree(rules[idx]) but does not clear rules[idx]. After that, both edit and show still operate on the stale pointer. That gives a stable use-after-free on a very sprayable cache size.
Analysis
1. Strong heap pointer leak from kernel logs
The module logs raw heap pointers with %px in fw_del_rule and fw_show_rule. Because dmesg is readable, every delete gives us the exact address of the freed firewall object.
That matters a lot: instead of guessing which allocation reclaimed the freed slot, we can free a rule, read the log, and then inspect the stale object with FW_SHOW_RULE until we recognize the replacement structure.
...
$ grep --similar
Similar writeups
- [pwn][Pro]lasOS— volgactf2026
- [pwn][Pro]Astralogy— kalmarctf2026
- [reverse][free]SEPC (Secure Enclave)— HackTheBox
- [reverse][Pro]Device Has Been Modified— miptctf
- [pwn][Pro]read_me_not — sendfile bypass— spbctf