Linux Kernel Remote Bluetooth vulns
Bluetooth L2CAP Vulnerabilities — Linux 6.6 LTS
Audited: net/bluetooth/l2cap_core.c on kernel 6.6.129
Status: CODE CONFIRMED (2 bugs), QEMU trigger WIP
BUG 1: Use-After-Free in l2cap_le_connect_rsp (CVE-2022-42896 variant)
File: net/bluetooth/l2cap_core.c
Function: l2cap_le_connect_rsp()
Line: 4695
Type: Use-After-Free
Remote: YES — zero-click, over BLE, no pairing required
Impact: Kernel code execution
Description: l2cap_le_connect_rsp() retrieves an L2CAP channel by identifier WITHOUT holding a reference:
chan = __l2cap_get_chan_by_ident(conn, cmd->ident); // line 4695
// NO l2cap_chan_hold_unless_zero() here!
l2cap_chan_lock(chan); // UAF: chan can be freed before this
...
l2cap_chan_unlock(chan);
return err; // never calls l2cap_chan_put()
Compare with l2cap_connect_create_rsp() which was FIXED for CVE-2022-42896 (commit 711f8c3fb3db):
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
chan = l2cap_chan_hold_unless_zero(chan); // ← HOLDS REFERENCE
l2cap_chan_lock(chan);
...
l2cap_chan_unlock(chan);
l2cap_chan_put(chan); // ← RELEASES REFERENCE
The LE (Low Energy) path was missed during the CVE-2022-42896 fix. Exact same bug pattern, different function.
Race scenario:
Thread A (BT softirq): Thread B (process context):
close(bt_socket)
→ l2cap_chan_del → frees chan
l2cap_le_connect_rsp():
chan = __l2cap_get_chan_by_ident() ← gets FREED pointer
l2cap_chan_lock(chan) ← UAF / crash
Attack vector: Attacker within Bluetooth range sends a crafted L2CAP LE Connection Response while the victim has a BLE socket being closed. No pairing or user interaction required. Works against any device with BLE enabled (Android phones, laptops, IoT).
On Android: BLE is always active when Bluetooth is on (for Google Pay, Find My Device, nearby share, etc).
Trigger approach (from GHSA-pf87-6c9q-jvm4 advisory pattern): 1. Establish BLE connection to victim 2. Send L2CAP LE Connection Request 3. Rapidly disconnect/reconnect to create socket churn 4. Time the L2CAP LE Connection Response to race with teardown
Affected:
- Linux 6.6 LTS: CONFIRMED in source
- Linux mainline: likely affected (function at same offset)
- Android (android15-6.6, android16-6.12): likely affected
- Any kernel after CVE-2022-42896 fix that has the LE path
BUG 2: Out-of-Bounds Read in l2cap_information_rsp
File: net/bluetooth/l2cap_core.c
Function: l2cap_information_rsp()
Lines: 4562, 4588, 4607
Type: Heap OOB read (info leak)
Remote: YES — over BR/EDR Bluetooth, pre-authentication
Impact: Kernel heap info leak
Description: l2cap_information_rsp() validates only the fixed header size:
struct l2cap_info_rsp {
__le16 type; // 2 bytes
__le16 result; // 2 bytes
__u8 data[]; // flexible array — 0 in sizeof
};
if (cmd_len < sizeof(*rsp)) // only checks >= 4 bytes!
return -EPROTO;
Then accesses the flexible array WITHOUT checking data is present:
case L2CAP_IT_FEAT_MASK:
conn->feat_mask = get_unaligned_le32(rsp->data); // reads 4 bytes OOB!
case L2CAP_IT_FIXED_CHAN:
conn->remote_fixed_chan = rsp->data[0]; // reads 1 byte OOB!
An attacker sends L2CAP_INFO_RSP with cmd_len=4 (header only, no data). The check at line 4562 passes. get_unaligned_le32(rsp->data) reads 4 bytes past the packet boundary into adjacent skb heap data.
The leaked value is stored in conn->feat_mask which influences subsequent L2CAP behavior (whether fixed channels are used). This is an info leak similar to CVE-2020-12352 (BadChoice from BleedingTooth).
Attack vector: Attacker within Bluetooth range sends a malformed L2CAP Information Response during connection setup. This happens BEFORE authentication — the info exchange is part of the L2CAP signaling that occurs immediately after ACL connection establishment.
No pairing, no user interaction. Single packet trigger, no race condition needed.
Affected:
- Linux 6.6 LTS: CONFIRMED in source
- Likely all kernel versions — this code hasn’t changed significantly
ADDITIONAL NOTE: l2cap_ecred_reconf_req missing refcount
File: net/bluetooth/l2cap_core.c
Function: l2cap_ecred_reconf_req()
Lines: 5325, 5357
Type: Potential UAF (lower severity)
Channels retrieved via __l2cap_get_chan_by_dcid() at line 5325 are stored in a local array and accessed at lines 5357-5358 WITHOUT reference counting. If a channel is freed between the two loops, the second loop accesses freed memory.
Lower severity because both loops run under conn->lock, reducing the race window. But a concurrent HCI disconnect event processed on another CPU could still trigger it.