DF-0458 / df0458_model.c
/* * DF-0458 - ng_l2cap L2CA_Ping heap over-read via unchecked echo_size vs arglen * * Bug: sys/netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.c:1297-1358 * * 1306: if (msg->header.arglen < sizeof(*ip)) { // sizeof(*ip) = 8 * error = EMSGSIZE; goto out; * } * 1316: if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) { // 65531 * error = EMSGSIZE; goto out; * } * ... * 1357: _ng_l2cap_echo_req(cmd->aux, cmd->ident, * 1358: msg->data + sizeof(*ip), ip->echo_size); * * _ng_l2cap_echo_req (sys/netgraph7/bluetooth/l2cap/ng_l2cap_cmds.h:323-344) * calls m_copyback(m, sizeof(*c), echo_size, data) which reads `echo_size` * bytes from `data` = msg->data + sizeof(*ip). The available bytes at that * pointer are arglen - sizeof(*ip). The only length check is arglen>=8 * (line 1306) and echo_size<=65531 (line 1316). There is NO check that * arglen >= sizeof(*ip) + echo_size. * * So a message with arglen=8 but echo_size=65531 makes m_copyback read * 65531 bytes starting at msg->data+8 -- 65523 bytes past the end of the * allocated message buffer, into kernel heap. Those bytes are echoed in * the L2CAP EchoReq to the remote peer (kernel heap info leak over BT), * and if the read crosses an unmapped page the kernel panics (DoS). * * Threat model: local privileged netgraph control message sender, OR * the remote BT peer if an attacker can influence the L2CA_Ping request * sent on the host's behalf. The echo bytes are returned over the air. * * Why we cannot fire this live on the audit guest: * - The L2CA_Ping path needs an L2CAP connection (con = ng_l2cap_con_by_addr). * Without one, the function tries ng_l2cap_lp_con_req (line 1328) which * requires an attached HCI lower layer. The audit guest has no Bluetooth * hardware / virtual HCI, so no connection can be established and the * m_copyback at line 1357 is never reached. * - Confirmed by source: the OOB read is gated behind con != NULL (or a * successful lp_con_req that creates a connection). * * The bug is confirmed by source inspection. This file documents the exact * data flow and the missing check (arglen < sizeof(*ip) + echo_size). * * No buildable PoC -- the path is unreachable without a BT HCI transport. */ #include <stdint.h> #include <stdio.h> int main(void) { uint32_t arglen = 8; /* minimum that passes line 1306 */ uint32_t echo_size = 65531; /* maximum that passes line 1316 */ uint32_t avail = arglen - 8; /* bytes after the ip header */ uint32_t read_oob = echo_size - avail; printf("DF-0458 source-level analysis (no live trigger - no BT HW)\n"); printf("---\n"); printf("Attacker message: arglen=%u, echo_size=%u\n", arglen, echo_size); printf("Check at :1306: arglen(%u) < sizeof(*ip)(8)? %s -> passes\n", arglen, arglen < 8 ? "YES" : "no"); printf("Check at :1316: echo_size(%u) > 65531? %s -> passes\n", echo_size, echo_size > 65531 ? "YES" : "no"); printf("MISSING check: arglen(%u) < sizeof(*ip)+echo_size(%u)? YES\n", arglen, 8 + echo_size); printf("m_copyback reads %u bytes from msg->data+8; only %u available.\n", echo_size, avail); printf("=> %u bytes read OUT OF BOUNDS into kernel heap.\n", read_oob); printf("Those bytes are echoed in the L2CAP EchoReq response.\n"); printf("Fix: add if (arglen < sizeof(*ip) + ip->echo_size) { EMSGSIZE; goto out; }\n"); return 0; } |