DF-0436 / trace_analysis.c
/* * DF-0436 — Static analysis trace for ng_btsocket_hci_raw_filter() * heap OOB read + security-filter bypass * * STATUS: NOT LIVE-REPRODUCED on this guest. The ng_btsocket_hci_raw module * is NOT shipped in /boot/kernel/ on the master DEV ISO (only USB-HCI drivers * like uhci/ehci/xhci/sdhci are shipped, NOT netgraph7 Bluetooth). kldload * of ng_btsocket_hci_raw / ng_hci / ng_ubt all fail with "No such file or * directory". The bug is confirmed by line-by-line source trace below; it * would be live-reproducible on any system with the netgraph7 Bluetooth * stack compiled and loaded AND a Bluetooth adapter (real or emulated). * * THE BUG (sys/netgraph7/bluetooth/socket/ng_btsocket_hci_raw.c:669-718): * * static int * ng_btsocket_hci_raw_filter(ng_btsocket_hci_raw_pcb_p pcb, * struct mbuf *m, int d) * { * int type, event, opcode; * switch ((type = *mtod(m, u_int8_t *))) { * case NG_HCI_CMD_PKT: * if (!(pcb->flags & NG_BTSOCKET_HCI_RAW_PRIVILEGED)) { * opcode = le16toh(mtod(m, ng_hci_cmd_pkt_t *)->opcode); * if (!bit_test( * ng_btsocket_hci_raw_sec_filter->commands[NG_HCI_OGF(opcode) - 1], <-- [-1] * NG_HCI_OCF(opcode) - 1)) <-- [-1] * return (EPERM); * } * ... * case NG_HCI_EVENT_PKT: * if (!d) return (EINVAL); * event = mtod(m, ng_hci_event_pkt_t *)->event - 1; <-- underflow * if (!(pcb->flags & NG_BTSOCKET_HCI_RAW_PRIVILEGED)) * if (!bit_test(ng_btsocket_hci_raw_sec_filter->events, event)) * return (EPERM); * * From ng_hci.h: * #define NG_HCI_OCF(op) ((op) & 0x3ff) // 0..1023 * #define NG_HCI_OGF(op) (((op) >> 10) & 0x3f) // 0..63 * * Three distinct OOB reads when attacker-controlled opcode/event == 0: * * (1) OGF==0: commands[OGF(opcode)-1] = commands[-1] * struct ng_btsocket_hci_raw_sec_filter { * bitstr_t events[0xff / 8]; // 32 bytes @ offset 0 * bitstr_t commands[0x3f][0x3ff / 8]; // 63*128 = 8064 bytes @ offset 32 * }; * commands[-1] is 128 bytes before commands[0] = at offset -96 from * the struct start = 96 bytes BEFORE the struct's kmalloc allocation. * Heap OOB read of 128 bytes; bit_test returns whatever bit happens * to be set there, gating EPERM. * * (2) OCF==0: bit_test(commands[X], OCF-1) = bit_test(commands[X], -1) * bit_test macro: name[(bit) >> 3] = name[(-1) >> 3] = name[-1] * Reads 1 byte before commands[X]. Heap OOB read; the bit value * drives EPERM. * * (3) event==0: event = ...->event - 1 = -1 * bit_test(events, -1) = events[-1] = 1 byte before events[0] = * 1 byte before the struct allocation. Heap OOB read of 1 byte. * * SECURITY BYPASS: all three OOB reads drive a security decision * (return EPERM or allow). With OOB bytes attacker-influenced or lucky, * an unprivileged Bluetooth socket can send normally-restricted HCI * commands/events through the filter, including potentially dangerous * ones (e.g. NG_HCI_OGF_LINK_CONTROL commands). This is both an OOB * read and a security-policy bypass. * * TRIGGER PRECONDITIONS (none of which hold on this guest): * - netgraph7 Bluetooth stack built + loaded (ng_btsocket_hci_raw, * ng_hci, ng_ubt or another HCI driver). Not shipped on the master ISO. * - A Bluetooth adapter (USB BT dongle or emulated). vm.sh exposes none. * - An open PF_BLUETOOTH HCI raw socket from an unprivileged user. * * REPRODUCIBILITY on a BT-equipped system: * 1. kldload ng_ubt <or other BT driver> && kldload ng_hci && * kldload ng_btsocket_hci_raw * 2. As unprivileged user: socket(PF_BLUETOOTH, SOCK_RAW, ...) / * bind to HCI node. * 3. sendto() a 1-byte NG_HCI_CMD_PKT followed by opcode=0x0000. * Result: filter reads commands[-1] off the heap; OOB read + EPERM * decision driven by uninitialized heap contents. * * This file is a trace artifact, NOT a runnable PoC. fix.diff applies * the OGF/OCF/event==0 guards. */ int main(void) { return 0; } |