15、BLE服务发现漏洞:服务UUID泄露、特征值属性滥用、通知/指示劫持、服务变更攻击
BLE的服务发现,说白了就是设备之间互相「亮家底」的过程。你的手环广播说「我有心率服务」,手机连上去之后,手环再详细列出「我有心率测量特征值、电池服务特征值...」。这个过程看起来挺简单,但漏洞就藏在这些细节里。
我个人习惯把服务发现比作「开门迎客」——你让客人进了门,但没告诉他哪些房间能进、哪些抽屉不能碰。结果呢?客人可能翻到你的私房钱,甚至把你的保险柜密码改了。
15.1 服务UUID泄露:你的设备在裸奔
UUID是服务的唯一标识。比如0x180D代表心率服务,0x180F代表电池服务。问题在于,这些UUID在广播包或服务发现过程中是明文传输的。
我在项目中遇到过一件事:某款智能门锁的广播包里直接包含了0xFFE0这个自定义UUID。攻击者一看就知道这是门锁服务,然后针对性发起攻击。你想想看,这等于在门上贴了张纸条「我是智能锁,快来黑我」。
核心问题:
- 广播包中的UUID可以被任何扫描设备捕获
- 即使不在广播包中,连接后的服务发现也是明文
- 自定义UUID往往暴露了设备类型和厂商信息
避坑指南:
我曾经建议团队把关键服务的UUID放在广播包之外,只在连接后通过加密通道暴露。虽然不能完全隐藏,但至少增加了攻击者的信息收集成本。
15.2 特征值属性滥用:权限形同虚设
每个特征值都有属性——读、写、通知、指示等。但很多开发者对这些属性的理解停留在「我设了就行」的层面,根本没考虑实际攻击场景。
举个例子:某款蓝牙温湿度计,它的校准参数特征值属性是「可写」。按理说这应该只允许工厂模式写入,但实际产品中没有任何访问控制。攻击者连上设备后,直接往这个特征值写个0x00,温度读数就永远显示0度了。
| 属性 | 滥用场景 | 后果 |
|---|---|---|
| 可写 | 本应只读的配置参数被写入 | 设备行为异常、固件损坏 |
| 可读 | 敏感数据(如密钥、序列号)被读取 | 隐私泄露、身份伪造 |
| 通知/指示 | 未授权订阅导致数据泄露 | 实时数据被窃听 |
注意:属性只是GATT层的权限标记,不是安全机制。攻击者可以通过L2CAP层直接读写,绕过属性检查。嗯,这里要特别留意——属性检查是软件层面的,而BLE协议栈本身并不强制校验。
15.3 通知/指示劫持:中间人偷听
通知和指示是BLE中常用的数据推送机制。设备主动把数据推给客户端,不需要客户端轮询。但问题来了——谁有资格接收这些通知?
标准做法是:客户端先发送CCCD(客户端特征配置描述符)写入请求,设备确认后开始推送。但很多设备根本不验证这个请求的来源。攻击者可以在连接后,直接往CCCD里写0x0001(启用通知),然后坐等数据推送。
我记得有一次分析某款运动手环,它的心率特征值CCCD没有任何访问控制。攻击者只要连上手环,订阅心率通知,就能实时获取用户的心率数据。更可怕的是,用户本人完全不知道有人在偷看他的心跳。
劫持流程:
- 攻击者扫描到目标设备并建立连接
- 发现心率服务,找到对应特征值
- 向CCCD写入0x0001(启用通知)
- 设备开始推送心率数据给攻击者
- 攻击者可以同时保持与手机的连接,实现中间人
15.4 服务变更攻击:动态服务的陷阱
有些设备支持动态服务——运行时可以添加、删除或修改服务。这个功能本身是为了灵活性,但实现不当就成了漏洞温床。
攻击者可以伪造服务变更指示(Service Changed Indication),告诉客户端「服务变了,重新发现吧」。然后客户端重新执行服务发现,攻击者趁机注入伪造的服务和特征值。
我见过一个案例:某款医疗设备支持OTA升级,升级过程中会动态替换服务。攻击者利用这个机制,在设备升级时发送伪造的服务变更指示,让手机端误以为设备已经升级完成,实际上设备还在升级中。结果手机端显示「升级成功」,但设备已经变砖了。
// 攻击者伪造的服务变更指示示例
// 注意:这不是完整代码,仅展示攻击思路
// 1. 监听服务变更指示
void onServiceChangedIndication(byte[] data) {
// 2. 伪造新的服务列表
List<Service> fakeServices = new ArrayList<>();
fakeServices.add(new Service(0x180D, true)); // 心率服务
fakeServices.add(new Service(0xFFE0, true)); // 恶意自定义服务
// 3. 注入恶意特征值
fakeServices.get(1).addCharacteristic(
new Characteristic(0xFFE1,
Property.READ | Property.WRITE,
"攻击者控制通道"
)
);
// 4. 返回给客户端
return fakeServices;
}
防御建议:
我曾经在项目中采用「服务变更签名」机制——每次服务变更都附带一个HMAC签名,客户端验证签名后才接受变更。虽然增加了复杂度,但彻底杜绝了伪造服务变更攻击。
15.5 知识体系总览
下面这张图总结了服务发现漏洞的四个维度,以及它们之间的关联关系。我建议你把它存下来,做安全审计时对照着看。
这四个漏洞不是孤立的。攻击者往往组合使用:先通过UUID泄露定位目标设备,然后利用属性滥用篡改配置,再通过通知劫持窃听数据,最后用服务变更攻击维持持久化控制。嗯,这就是为什么我总说「安全是一个整体,不是单个补丁能解决的」。
总结一下:
- UUID泄露是信息收集阶段的漏洞,暴露了设备身份
- 属性滥用是权限控制问题,让攻击者越权操作
- 通知劫持是数据通道问题,让攻击者偷听实时数据
- 服务变更是逻辑欺骗问题,让攻击者篡改设备行为
防御思路其实就三条:最小权限、访问控制、加密通道。但说起来简单,做起来...嗯,我踩过的坑比你们想象的多。
公众号:蓝海资料掘金营,微信deep3321