DF-5741 / verdict.json
{ "finding_id": "DF-5741", "status": "reproduced", "reproduced": true, "verdict": "hci_sattach (hci_socket.c:644) grants HCI_PRIVILEGED to EVERY raw HCI socket: the handler runs as a netmsg on netisr_cpuport(0) where curproc is NULL and caps_priv_check_self() evaluates the lwp-less netisr thread (caps_priv_check_td returns 0=privileged for td_lwp==NULL, kern_caps.c:348) - both arms true for any caller. Deterministically verified: uid 1001 queued privileged-only opcode HCI_CMD_RESET with errno=0 (kernel cmdwait confirmed). The whole unprivileged opcode/event/ACL filter (hci_socket.c:510-514, 961-980) is dead code on DragonFly; the five caps_priv_check_self gates in hci_ioctl.c:224-287 (via this files hci_scontrol) are equally dead. Unprivileged users get full controller takeover (RESET, WRITE_SCAN_ENABLE, link-key ops) and tap of link-key/passkey disclosure events.", "impact": "privesc", "confidence": "certain", "exploit_chain": "uid!=0 on any machine with a BT controller: socket(AF_BLUETOOTH,SOCK_RAW,BTPROTO_HCI) -> socket is wrongly HCI_PRIVILEGED -> send(2) any controller command (RESET = controller DoS; WRITE_SCAN_ENABLE/WRITE_AUTH_ENABLE/WRITE_STORED_LINK_KEY = pairing/auth manipulation) and setsockopt(SO_HCI_EVT_FILTER) to receive HCI_EVENT_RETURN_LINK_KEYS / LINK_KEY_NOTIFICATION / USER_PASSKEY_NOTIFICATION = disclosure of stored link keys and passkeys from the kernels controller image.", "evidence": "run.log (baseline: uid1001 RESET QUEUED-OK + kernel queue growth; fixed: EPERM for uid1001, root unchanged); bypass.c (the 30-line determinstic test); fix.diff (so_cred-based check)", "kernel_refs": [ "sys/netbt/hci_socket.c:644", "sys/netbt/hci_socket.c:510", "sys/netbt/hci_socket.c:961", "sys/netbt/hci_socket.c:608", "sys/netbt/hci_ioctl.c:224", "sys/kern/kern_caps.c:344", "sys/kern/uipc_socket.c:259" ], "poc_changes": "no seed; wrote the minimal deterministic userland proof (bypass.c) and ran it as uid 1001 through the DF-5740 rigs verbatim production socket layer", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 120, "guest_dirty": 0, "build_cmd": "cc -O -Wall -o bypass bypass.c (plus DF-5740 rig loaded for a live unit)", "run_cmd": "su maxx -c ./bypass (baseline: PRIVILEGE BYPASS banner; fixed: EPERM)", "code_hash": "5bafd8a39f0146fb5085ec2501e13d65a912b158063fe5f05ae94a4b6bab4372", "notes": "deterministic, 100% repeatable, no race. Same class should be audited in sco_socket.c/l2cap_socket.c/rfcomm_socket.c privilege paths. fix validated on rig (module rebuilt against patched hci_socket.c).", "recommended_fix": "evaluate the callers credentials captured at socket creation: caps_priv_check(so->so_cred, SYSCAP_RESTRICTEDROOT) instead of curproc/caps_priv_check_self (see fix.diff); apply the same conversion to hci_ioctl.c:224,249,259,273,287.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "rebuilt rig against patched hci_socket.c: uid1001 RESET -> EPERM (bypass gone), root -> QUEUED-OK (privilege preserved). run.log holds both transcripts.", "fix_evidence": "run.log (fixed section); fix.diff" } |