multifiles
b01lersc
Task: Linux kernel module with custom slab cache and OOB read/write via incomplete bounds check. Solution: SLUB freelist poisoning with safe-linking bypass, tail object technique for cross-page access, struct file spray to leak cred pointer, then cred corruption for privilege escalation.
$ 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.
multifiles - b01lersc CTF
Description
build artifacts in
build_out/rebuild withpwn_build.shrun challenge withdev.sh
A Linux kernel module called "multifiles" implements a custom slab cache for managing file-like objects. The challenge runs on Linux 6.12.81 with full kernel hardening enabled:
- KASLR, SMEP, SMAP, PTI
- CONFIG_SLAB_FREELIST_HARDENED=y
- CONFIG_HARDENED_USERCOPY=y
- CONFIG_STATIC_USERMODEHELPER is not set
User runs as ctf (uid=1000), flag is in /root/flag.txt.
Analysis
Kernel Module Structure
The module creates a custom slab cache for MultiFile objects:
typedef struct { u64 type; // offset 0 u64 flags; // offset 8 char name[16]; // offset 16 u64 data[16]; // offset 32 (128 bytes) } MultiFile; // Total: 160 bytes
The cache is created with SLAB_NO_MERGE to prevent merging with other caches:
multifiles_cache = kmem_cache_create_usercopy( MODULE_NAME "_cache", sizeof(MultiFile), // 160 bytes 0, SLAB_NO_MERGE, offsetof(MultiFile, name), // 16 USERCOPY_SIZE, // 144 (name + data) NULL );
With 160-byte objects, each 4096-byte slab page holds 25 objects (25 x 160 = 4000 bytes), leaving 96 bytes unused at the end of each page.
Vulnerability: Incomplete Bounds Check
The read/write operations have a flawed bounds check:
// check read/write bounds if ( count > MAX_RW_SIZE // count <= 64 || (count % sizeof(u64)) != 0 // count % 8 == 0 || *offset >= sizeof(MultiFile) // offset < 160 || *offset < 0 ) { ret = -EINVAL; goto out_unlock; }
Bug: The check verifies offset < sizeof(MultiFile) but NOT offset + count <= sizeof(MultiFile).
With offset = 152 and count = 64, we can read/write bytes 152-215, which extends 56 bytes past the object boundary into adjacent memory (or the next object's freelist pointer when freed).
SLUB Safe-Linking
...
$ grep --similar
Similar writeups
- [pwn][Pro]Common Offset— srdnlen
- [pwn][Pro]pwn8_logger — logger_easy!(not) — UAF/alias + tcache poison— spbctf
- [pwn][free]throughthewall— b01lersc
- [pwn][Pro]Taste— grodno_new_year_2026
- [pwn][free]Ox78— tjctf