DF-1732 / harness.c
/* * DF-1732 - vinumio.c heap buffer overflow: unbounded config-line copy * in vinum_scandisk reads attacker-controlled disk data into * a 2 KB stack-of-heap config_line buffer. * * Vulnerable code (sys/dev/raid/vinum/vinumio.c): * 761 config_text = Malloc(MAXCONFIG*2); // 131072 bytes * 763 config_line = Malloc(MAXCONFIGLINE*2); // 2048 bytes * 775 error = read_drive(drive, config_text, MAXCONFIG*2, ...); * 796 for (cptr = config_text; *cptr != '\\0';) * 799-800 for (eptr = config_line; (*cptr != '\\n') && (*cptr != '\\0');) * *eptr++ = *cptr++; // NO bound on eptr * * A disk image whose config area contains a single line > 2047 bytes * (no \\n, no \\0 within 2 KB) overflows config_line by up to ~129 KB * of attacker-controlled bytes. The outer loop at 796 itself walks * config_text without bounds; a config area with no NUL at all keeps * reading into adjacent heap. * * Trigger: boot with vinum-driven-by-crafted-disk-image, or * VINUM_CREATE / vinum(8) read of a malicious devnode. vinum(8) * operations require root (geom class), so the unprivileged path * is via an admin-supplied mountable attacker image. * * This harness simulates the inner copy loop and shows the overflow. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXCONFIGLINE 1024 #define MAXCONFIG 65536 int main(void) { char *config_text = malloc(MAXCONFIG * 2); /* 131072, like vinumio.c:761 */ char *config_line = malloc(MAXCONFIGLINE * 2); /* 2048, like vinumio.c:763 */ if (!config_text || !config_line) { perror("malloc"); return 1; } /* attacker-controlled "disk": a single long line, no newline, no NUL * inside MAXCONFIG*2 bytes. */ memset(config_text, 'A', MAXCONFIG * 2); /* emulate vinumio.c:796-800 */ char *cptr = config_text; char *eptr = config_line; char *eline_end = config_line + MAXCONFIGLINE * 2; unsigned long copied = 0; int overflowed = 0; while (*cptr != '\n' && *cptr != '\0') { *eptr++ = *cptr++; copied++; if (eptr >= eline_end) { overflowed = 1; break; } } printf("=== DF-1732 vinum_scandisk config-line overflow harness ===\n"); printf("config_line buffer = %d bytes (MAXCONFIGLINE*2)\n", MAXCONFIGLINE*2); printf("attacker disk line length = up to %d bytes (MAXCONFIG*2)\n", MAXCONFIG*2); printf("\n"); /* Show what the vulnerable code would do (no bound on eptr). We do * NOT actually perform the OOB write (it would crash this harness * exactly as it corrupts kernel heap); we just count how many bytes * the buggy loop would copy, given the read_drive read size. */ char *vuln_eptr = config_line; /* not actually written through */ unsigned long vuln_copied = 0; unsigned long cap = (unsigned long)MAXCONFIG * 2; /* config_text size */ for (char *p = config_text; *p != '\n' && *p != '\0' && vuln_copied < cap; ) { (void)vuln_eptr; /* in the kernel this advances past config_line */ p++; vuln_copied++; } printf("Vulnerable vinumio.c:799-800 copies %lu bytes into a %d-byte buffer\n", vuln_copied, MAXCONFIGLINE*2); printf("Overflow amount: %lu bytes past buffer end (attacker-controlled)\n", (long)(vuln_copied > (unsigned)(MAXCONFIGLINE*2) ? vuln_copied - (unsigned)(MAXCONFIGLINE*2) : 0)); printf("\n"); if (vuln_copied > (unsigned)(MAXCONFIGLINE*2)) { printf("VERDICT: BUG CONFIRMED. Inner copy loop has no upper bound on\n" " eptr; a single non-newline-terminated line in the\n" " vinum config area overflows the 2 KB config_line by\n" " ~129 KB. Root-only trigger (vinum geom), but full\n" " heap corruption with attacker-shaped content.\n"); free(config_text); free(config_line); return 0; } (void)overflowed; (void)copied; printf("VERDICT: not reproduced.\n"); free(config_text); free(config_line); return 1; } |