bluetooth_stack开源蓝牙协议栈源码分析与漏洞挖掘
字数 1534 2025-08-24 07:48:09

开源蓝牙协议栈源码分析与漏洞挖掘教学文档

1. 项目概述

该项目是一个开源的蓝牙协议栈实现,支持STM32等嵌入式芯片,主要实现了HCI层以上的协议(如L2CAP、ATT等),而HCI下层协议(如LL)则使用CSR8311芯片自带的协议栈。

项目地址:https://github.com/sj15712795029/bluetooth_stack

2. 核心数据结构

2.1 bt_pbuf_t 结构

struct bt_pbuf_t{
    /** 单链表中的下一个pbuf节点 */
    struct bt_pbuf_t *next;
    /** pbuf节点payload数据指针 */
    void *payload;
    /** pbuf单链表中本节点以及后续节点的数据总和 */
    uint16_t tot_len;
    /** 本pbuf节点的payload数据长度 */
    uint16_t len;
    /** pbuf类型 */
    uint8_t type;
    /** pbuf标志 */
    uint8_t flags;
    /** pbuf引用次数 */
    uint16_t ref;
};

特点:

  • 采用链表结构存储数据包
  • payload指向当前pbuf的数据存放地址
  • len表示当前pbuf的payload长度
  • tot_len表示整个链表所有pbuf的总长度
  • 便于数据包重组

3. 数据包处理流程

3.1 入口函数:hci_acl_input

void hci_acl_input(struct bt_pbuf_t *p) {
    if(link != NULL) {
        if(aclhdr->len) {
            l2cap_acl_input(p, &(link->bdaddr));
        }
    }
}

3.2 L2CAP处理:l2cap_acl_input

void l2cap_acl_input(struct bt_pbuf_t *p, struct bd_addr_t *bdaddr) {
    l2cap_seg_t *inseg = l2cap_reassembly_data(p,bdaddr,&can_contiue);
    if(!can_contiue) return;
    
    switch(inseg->l2caphdr->cid) {
        case L2CAP_NULL_CID:
            _l2cap_null_cid_process(inseg->p,bdaddr); break;
        case L2CAP_SIG_CID:
            _l2cap_classical_sig_cid_process(inseg->p,inseg->l2caphdr,bdaddr); break;
        case L2CAP_CONNLESS_CID:
            _l2cap_connless_cid_process(inseg->p,bdaddr); break;
        case L2CAP_ATT_CID:
            _l2cap_fixed_cid_process(L2CAP_ATT_CID,p,bdaddr); break;
        default:
            _l2cap_dynamic_cid_process(inseg->pcb,inseg->p,inseg->l2caphdr,bdaddr); break;
    }
    bt_memp_free(MEMP_L2CAP_SEG, inseg);
}

处理流程:

  1. 调用l2cap_reassembly_data处理L2CAP分片
  2. 根据L2CAP头部的CID字段选择相应的处理函数
  3. 释放内存

4. 漏洞分析

4.1 ATT报文处理中的3处栈溢出漏洞

4.1.1 漏洞函数调用链

_l2cap_fixed_cid_process → gatt_data_recv → gatts_handle_write_req

4.1.2 gatts_handle_write_req漏洞

static err_t gatts_handle_write_req(struct bd_addr_t *bdaddr, struct bt_pbuf_t *p) {
    uint8_t req_buf_len = 0;
    uint8_t req_buf[GATT_BLE_MTU_SIZE] = {0}; // 23字节
    att_parse_write_req(p,&handle,req_buf,&req_buf_len);
}
err_t att_parse_write_req(struct bt_pbuf_t *p,uint16_t *handle,uint8_t *att_value,uint8_t *value_len) {
    uint8_t *data = p->payload;
    uint8_t data_len = p->len;
    *handle = bt_le_read_16(data,1);
    *value_len = data_len-3;
    memcpy(att_value,data+3,*value_len); // 无长度检查的拷贝
    return BT_ERR_OK;
}

漏洞点:

  • req_buf是23字节的栈缓冲区
  • value_len直接从数据包中计算得出(data_len-3)
  • 无长度检查直接进行memcpy,导致栈溢出

4.1.3 其他类似漏洞函数

  • gatts_handle_find_info_value_type_req
  • gatts_handle_write_cmd

4.2 AVRCP报文处理中的堆溢出漏洞

4.2.1 漏洞函数调用链

_l2cap_fixed_cid_process → avctp_data_input → avrcp_controller_data_handle → 
avrcp_controller_parse_vendor_dependent → avrcp_controller_parse_get_element_attr_rsp

4.2.2 avrcp_controller_parse_get_element_attr_rsp漏洞

static err_t avrcp_controller_parse_get_element_attr_rsp(...) {
    uint16_t para_len = bt_be_read_16(buffer, 8);
    uint8_t element_attr_num = buffer[10];
    uint8_t *para_palyload = buffer + 11;
    
    memset(&avrcp_pcb->now_playing_info,0,sizeof(now_playing_info_t));
    
    for(index = 0; index < element_attr_num; index++) {
        uint32_t attr_id = bt_be_read_32(para_palyload, 0);
        uint16_t attr_length = bt_be_read_16(para_palyload+6, 0);
        
        switch(attr_id) {
            case AVRCP_MEDIA_ATTR_TITLE:
                memcpy(avrcp_pcb->now_playing_info.now_playing_title,
                      para_palyload+8,attr_length); // 无长度检查的拷贝

漏洞点:

  • attr_length直接从数据包中读取
  • 无长度检查直接进行memcpy,导致堆溢出

4.3 L2CAP整数溢出导致越界读

4.3.1 _l2cap_sig_cfg_rsp_process漏洞

_l2cap_sig_cfg_rsp_process(l2cap_pcb_t *pcb,struct bt_pbuf_t *p,l2cap_sig_hdr_t *sighdr,l2cap_sig_t *sig) {
    uint16_t siglen;
    siglen = sighdr->len;
    siglen -= 6; // 如果sighdr->len < 6,会导致整数下溢
    
    while(siglen > 0) {
        opthdr = p->payload;
        bt_pbuf_header(p, -(L2CAP_CFGOPTHDR_LEN + opthdr->len));
        siglen -= L2CAP_CFGOPTHDR_LEN + opthdr->len;
    }
}

漏洞点:

  • siglen是uint16_t类型
  • 如果sighdr->len < 6,减法会导致整数下溢(0xFFFF-6)
  • 导致循环读取大量数据,造成越界读

4.4 AVRCP越界读漏洞

4.4.1 avrcp_controller_parse_list_app_setting_rsp漏洞

static err_t avrcp_controller_parse_list_app_setting_rsp(...) {
    uint8_t app_setting_attr_num = buffer[10];
    
    if(app_setting_attr_num > 0) {
        uint8_t *setting_attr = buffer+11;
        for(index = 0; index < app_setting_attr_num; index++) {
            switch(setting_attr[index]) { // 无边界检查

漏洞点:

  • app_setting_attr_num直接从数据包中读取
  • 循环访问setting_attr时无边界检查
  • 可能导致越界读

5. 漏洞总结

该协议栈代码中存在的典型问题:

  1. 内存拷贝无长度检查

    • 多处memcpy操作前未验证拷贝长度是否超过目标缓冲区大小
    • 导致栈溢出和堆溢出漏洞
  2. 整数运算无溢出检查

    • 减法操作可能导致整数下溢
    • 导致越界读漏洞
  3. 数组访问无边界检查

    • 循环访问数组元素时未验证索引是否越界
    • 导致越界读漏洞
  4. 协议字段无有效性验证

    • 直接从数据包中读取长度、数量等关键字段
    • 未验证这些字段的合理性

6. 安全开发建议

  1. 内存操作安全

    • 所有内存拷贝操作前必须验证源长度和目标缓冲区大小
    • 使用安全的字符串/内存操作函数(如带长度限制的版本)
  2. 整数运算安全

    • 对可能产生溢出的算术运算进行检查
    • 使用安全的算术运算库或宏
  3. 数组访问安全

    • 所有数组访问前必须验证索引有效性
    • 使用断言或运行时检查
  4. 协议字段验证

    • 对所有从网络接收的协议字段进行有效性验证
    • 检查长度、数量等字段的合理性
  5. 代码审计重点

    • 重点关注变长数据结构的解析
    • 关注所有内存操作和算术运算
    • 参考协议规范验证实现是否正确

7. 思维导图要点

蓝牙协议栈源码分析
├─ 核心数据结构
│  └─ bt_pbuf_t (链表式数据包存储)
├─ 数据处理流程
│  ├─ hci_acl_input (入口)
│  └─ l2cap_acl_input (L2CAP分发)
├─ 漏洞类型
│  ├─ 栈溢出 (ATT处理)
│  ├─ 堆溢出 (AVRCP处理)
│  ├─ 整数溢出 (L2CAP处理)
│  └─ 越界读 (AVRCP处理)
└─ 安全建议
   ├─ 内存操作安全
   ├─ 整数运算安全
   ├─ 数组访问安全
   └─ 协议字段验证
开源蓝牙协议栈源码分析与漏洞挖掘教学文档 1. 项目概述 该项目是一个开源的蓝牙协议栈实现,支持STM32等嵌入式芯片,主要实现了HCI层以上的协议(如L2CAP、ATT等),而HCI下层协议(如LL)则使用CSR8311芯片自带的协议栈。 项目地址:https://github.com/sj15712795029/bluetooth_ stack 2. 核心数据结构 2.1 bt_ pbuf_ t 结构 特点: 采用链表结构存储数据包 payload 指向当前pbuf的数据存放地址 len 表示当前pbuf的payload长度 tot_len 表示整个链表所有pbuf的总长度 便于数据包重组 3. 数据包处理流程 3.1 入口函数:hci_ acl_ input 3.2 L2CAP处理:l2cap_ acl_ input 处理流程: 调用 l2cap_reassembly_data 处理L2CAP分片 根据L2CAP头部的CID字段选择相应的处理函数 释放内存 4. 漏洞分析 4.1 ATT报文处理中的3处栈溢出漏洞 4.1.1 漏洞函数调用链 4.1.2 gatts_ handle_ write_ req漏洞 漏洞点: req_buf 是23字节的栈缓冲区 value_len 直接从数据包中计算得出(data_ len-3) 无长度检查直接进行 memcpy ,导致栈溢出 4.1.3 其他类似漏洞函数 gatts_handle_find_info_value_type_req gatts_handle_write_cmd 4.2 AVRCP报文处理中的堆溢出漏洞 4.2.1 漏洞函数调用链 4.2.2 avrcp_ controller_ parse_ get_ element_ attr_ rsp漏洞 漏洞点: attr_length 直接从数据包中读取 无长度检查直接进行 memcpy ,导致堆溢出 4.3 L2CAP整数溢出导致越界读 4.3.1 _ l2cap_ sig_ cfg_ rsp_ process漏洞 漏洞点: siglen 是uint16_ t类型 如果 sighdr->len < 6 ,减法会导致整数下溢(0xFFFF-6) 导致循环读取大量数据,造成越界读 4.4 AVRCP越界读漏洞 4.4.1 avrcp_ controller_ parse_ list_ app_ setting_ rsp漏洞 漏洞点: app_setting_attr_num 直接从数据包中读取 循环访问 setting_attr 时无边界检查 可能导致越界读 5. 漏洞总结 该协议栈代码中存在的典型问题: 内存拷贝无长度检查 多处 memcpy 操作前未验证拷贝长度是否超过目标缓冲区大小 导致栈溢出和堆溢出漏洞 整数运算无溢出检查 减法操作可能导致整数下溢 导致越界读漏洞 数组访问无边界检查 循环访问数组元素时未验证索引是否越界 导致越界读漏洞 协议字段无有效性验证 直接从数据包中读取长度、数量等关键字段 未验证这些字段的合理性 6. 安全开发建议 内存操作安全 所有内存拷贝操作前必须验证源长度和目标缓冲区大小 使用安全的字符串/内存操作函数(如带长度限制的版本) 整数运算安全 对可能产生溢出的算术运算进行检查 使用安全的算术运算库或宏 数组访问安全 所有数组访问前必须验证索引有效性 使用断言或运行时检查 协议字段验证 对所有从网络接收的协议字段进行有效性验证 检查长度、数量等字段的合理性 代码审计重点 重点关注变长数据结构的解析 关注所有内存操作和算术运算 参考协议规范验证实现是否正确 7. 思维导图要点