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没有任何访问控制。攻击者只要连上手环,订阅心率通知,就能实时获取用户的心率数据。更可怕的是,用户本人完全不知道有人在偷看他的心跳。

劫持流程:

  1. 攻击者扫描到目标设备并建立连接
  2. 发现心率服务,找到对应特征值
  3. 向CCCD写入0x0001(启用通知)
  4. 设备开始推送心率数据给攻击者
  5. 攻击者可以同时保持与手机的连接,实现中间人

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 知识体系总览

下面这张图总结了服务发现漏洞的四个维度,以及它们之间的关联关系。我建议你把它存下来,做安全审计时对照着看。

BLE服务发现漏洞知识体系 服务发现漏洞 UUID泄露 广播包明文暴露 自定义UUID泄露设备类型 连接后服务发现无加密 → 攻击者快速识别目标 属性滥用 可写属性无访问控制 可读属性泄露敏感数据 属性检查可被绕过 → 设备行为被篡改 通知/指示劫持 CCCD无访问控制 攻击者订阅通知通道 实时数据被窃听 → 中间人偷听数据 服务变更攻击 伪造变更指示 注入恶意服务/特征值 动态服务实现缺陷 → 设备逻辑被劫持 核心防御:最小权限原则 + 访问控制 + 加密通道 相互关联 相互关联 相互关联 四个漏洞维度往往同时出现,攻击者组合利用效果更佳 例如:UUID泄露 → 定位目标 → 属性滥用 → 篡改设备 → 通知劫持 → 窃听数据

这四个漏洞不是孤立的。攻击者往往组合使用:先通过UUID泄露定位目标设备,然后利用属性滥用篡改配置,再通过通知劫持窃听数据,最后用服务变更攻击维持持久化控制。嗯,这就是为什么我总说「安全是一个整体,不是单个补丁能解决的」。

总结一下:

  • UUID泄露是信息收集阶段的漏洞,暴露了设备身份
  • 属性滥用是权限控制问题,让攻击者越权操作
  • 通知劫持是数据通道问题,让攻击者偷听实时数据
  • 服务变更是逻辑欺骗问题,让攻击者篡改设备行为

防御思路其实就三条:最小权限、访问控制、加密通道。但说起来简单,做起来...嗯,我踩过的坑比你们想象的多。


公众号:蓝海资料掘金营,微信deep3321