BlueBorne漏洞集之CVE-2017-0785深度分析
1. 漏洞概述
CVE-2017-0785是BlueBorne漏洞集中的一个重要漏洞,影响Android系统的BlueDroid和Fluoride蓝牙协议栈实现。该漏洞源于SDP(Service Discovery Protocol)服务器对continuation state处理不当,导致内存信息泄露,可被攻击者用于绕过ASLR(地址空间布局随机化)保护机制。
2. 背景知识
2.1 SDP协议简介
SDP(Service Discovery Protocol)是经典蓝牙中的高层协议,采用C/S架构,用于客户端发现服务器提供的服务。关键特点包括:
- 客户端必须等待服务器响应当前请求PDU后才能发送下一个请求PDU
- 请求PDU携带限制响应PDU大小的字段,如MaximumAttributeByteCount
- 响应PDU携带相应字段表示返回数据长度,如AttributeListsByteCount
2.2 Continuation State机制
当服务器发现请求数据无法在一个响应PDU中完整返回时,会使用continuation state机制分段传输数据:
- 分段大小不一定用完MaximumAttributeByteCount定义的最大值
- 传输分段数据的响应PDU必须携带ContinuationState字段
- ContinuationState由两部分组成:
- InfoLength:表示continuation information的长度(最大0x10)
- Continuation Information:用于解决分段传输问题的神奇字段
2.3 L2CAP导致的数据分段
实际场景中,数据分段更多由L2CAP(Logical Link Control and Adaptation Protocol)限制引起:
- L2CAP承载SDP PDU传输
- 连接建立时设备交换各自支持的MTU(Maximum Transmission Unit)
- 当SDP响应数据超过远端设备L2CAP设置的MTU时,启用continuation state分段传输
2.4 Android对Continuation Information的定义
Android对蓝牙核心规范未明确定义的continuation information字段做了具体实现:
// platform/system/bt/stack/sdp/sdpint.h#200
typedef struct {
uint16_t len; /* length of the continuation info */
uint8_t* data; /* the continuation data */
uint16_t offset; /* the offset within the data */
} tCONNECTION_STATE;
cont_offset的具体含义有两种情况:
-
使用SDP_SERVICE_SEARCH_REQ/RSP PDU时:
- 表示下一个分段中起始数据项相对不分段完整数据项的偏移
- 例如已返回10个service record handle,cont_offset为10
-
使用SDP_SERVICE_ATTR_REQ/RSP或SDP_SERVICE_SEARCH_ATTR_REQ/RSP时:
- 表示下一个分段的数据相对不分段完整数据的偏移(字节为单位)
3. 漏洞详细分析
3.1 漏洞触发流程
-
SDP服务器处理请求PDU时进入
sdp_server_handle_client_req() -
当请求PDU为SDP_SERVICE_SEARCH_REQ时,进入
process_service_search() -
提取三个关键参数:
- ServiceSearchPattern → uid_seq
- MaximumServiceRecordCount → max_replies
- ContinuationState → cont_offset
-
若使能continuation state,会检查cont_offset与先前传给客户端值是否相等
-
根据uid_seq和max_replies找到匹配的service record handle,存储在rsp_handles数组
-
max_replies限制handle总数num_rsp_handles
3.2 漏洞根本原因
关键问题在于num_rsp_handles的计算方式:
- 对于每个请求,num_rsp_handles都会被重新计算
- 使用continuation state时,所有上下文相同请求应使用相同uid_seq和max_replies
- 但AOSP未检查每次计算的num_rsp_handles是否相同
攻击者可利用此缺陷:
- 先发送普通SDP_SERVICE_SEARCH_REQ PDU,正常触发continuation state
- 然后发送MaximumServiceRecordCount为1的continuation state PDU
- 导致num_rsp_handles值被篡改为1
3.3 内存泄露过程
-
当num_rsp_handles=1且使能continuation state时:
- 计算剩余需传输handle数量:rem_handles = num_rsp_handles - cont_offset
- 由于num_rsp_handles被篡改为1,rem_handles(uint16_t)发生下溢
-
下溢的rem_handles总是大于cur_handles(当前PDU能携带的handle数量)
- 导致continuation state恒使能
-
服务器尝试读取rsp_handles[cont_offset]到rsp_handles[cont_offset + cur_handles]
- cont_offset持续增大
- 最终导致数组越界读,泄露内存信息
4. 漏洞利用技术细节
4.1 关键数据结构
typedef struct {
uint16_t len; /* length of the continuation info */
uint8_t* data; /* the continuation data */
uint16_t offset; /* the offset within the data */
} tCONNECTION_STATE;
4.2 漏洞代码片段
关键漏洞代码位于process_service_search()函数:
// platform/system/bt/stack/sdp/sdp_server.cc#207
num_rsp_handles = get_num_matching_records(uid_seq);
if (num_rsp_handles > max_replies) num_rsp_handles = max_replies;
// platform/system/bt/stack/sdp/sdp_server.cc#227
rem_handles = num_rsp_handles - cont_offset; // Underflow here when exploited
// platform/system/bt/stack/sdp/sdp_server.cc#236
if (rem_handles <= cur_handles) {
cont_info_needed = false;
} else {
cont_info_needed = true;
}
// platform/system/bt/stack/sdp/sdp_server.cc#269
for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) {
/* This will eventually read out of bounds */
UINT16_TO_BE_STREAM(p, rsp_handles[xx]);
}
4.3 攻击步骤
- 建立正常L2CAP连接,配置小MTU增加触发continuation state概率
- 发送普通SDP_SERVICE_SEARCH_REQ PDU,建立合法continuation state上下文
- 发送特制PDU,将MaximumServiceRecordCount设为1
- 服务器计算num_rsp_handles=1,导致后续rem_handles下溢
- 利用恒使能的continuation state逐步读取越界内存
- 收集泄露的内存信息用于绕过ASLR
5. 防御建议
- 检查continuation state上下文中的num_rsp_handles一致性
- 对rem_handles计算进行边界检查,防止整数下溢
- 验证cont_offset不超过rsp_handles数组边界
- 更新到已修复漏洞的蓝牙协议栈版本
6. 参考资源
- Bluetooth Core Specification Version 5.2 | Vol 3, Part B
- BlueBorne Technical White Paper
- Android AOSP源码(android-8.0.0_r1标签)
- CVE-2017-0785官方描述