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);
}
处理流程:
- 调用
l2cap_reassembly_data处理L2CAP分片 - 根据L2CAP头部的CID字段选择相应的处理函数
- 释放内存
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_reqgatts_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. 漏洞总结
该协议栈代码中存在的典型问题:
-
内存拷贝无长度检查
- 多处
memcpy操作前未验证拷贝长度是否超过目标缓冲区大小 - 导致栈溢出和堆溢出漏洞
- 多处
-
整数运算无溢出检查
- 减法操作可能导致整数下溢
- 导致越界读漏洞
-
数组访问无边界检查
- 循环访问数组元素时未验证索引是否越界
- 导致越界读漏洞
-
协议字段无有效性验证
- 直接从数据包中读取长度、数量等关键字段
- 未验证这些字段的合理性
6. 安全开发建议
-
内存操作安全
- 所有内存拷贝操作前必须验证源长度和目标缓冲区大小
- 使用安全的字符串/内存操作函数(如带长度限制的版本)
-
整数运算安全
- 对可能产生溢出的算术运算进行检查
- 使用安全的算术运算库或宏
-
数组访问安全
- 所有数组访问前必须验证索引有效性
- 使用断言或运行时检查
-
协议字段验证
- 对所有从网络接收的协议字段进行有效性验证
- 检查长度、数量等字段的合理性
-
代码审计重点
- 重点关注变长数据结构的解析
- 关注所有内存操作和算术运算
- 参考协议规范验证实现是否正确
7. 思维导图要点
蓝牙协议栈源码分析
├─ 核心数据结构
│ └─ bt_pbuf_t (链表式数据包存储)
├─ 数据处理流程
│ ├─ hci_acl_input (入口)
│ └─ l2cap_acl_input (L2CAP分发)
├─ 漏洞类型
│ ├─ 栈溢出 (ATT处理)
│ ├─ 堆溢出 (AVRCP处理)
│ ├─ 整数溢出 (L2CAP处理)
│ └─ 越界读 (AVRCP处理)
└─ 安全建议
├─ 内存操作安全
├─ 整数运算安全
├─ 数组访问安全
└─ 协议字段验证