18、Android BLE广播漏洞:广播数据格式安全、广播过滤绕过、扫描响应数据注入、广播风暴攻击
BLE广播,说白了就是设备在“喊话”。它不停地对外说:“我在这儿,我叫某某,我能干这些事。” 听起来挺简单对吧?但问题就出在这个“喊话”过程里。我做了这么多年蓝牙安全,可以负责任地告诉你:广播阶段是整个BLE通信链条里最容易被攻击的环节之一。
为什么?因为广播是单向的、无连接的。攻击者不需要和你配对,甚至不需要知道你长啥样,只要在射频范围内,就能监听、篡改、甚至伪造广播包。今天我们就来把这四个漏洞掰开揉碎了讲清楚。
18.1 广播数据格式安全
先看广播包长什么样。BLE广播包的数据部分,遵循一个叫AD Structure的格式。每个AD Structure由三部分组成:长度、类型、数据。嗯,就这么简单。
// 一个典型的广播数据包结构
// Length (1 byte) | Type (1 byte) | Data (variable)
// 示例:完整广播数据
0x02 0x01 0x06 // Flags: LE General Discoverable, BR/EDR Not Supported
0x03 0x19 0x00 0x01 // Appearance: Generic Phone
0x05 0x09 0x48 0x65 0x6C 0x6C 0x6F // Complete Local Name: "Hello"
你看,每个AD Structure都是自描述的。长度字段告诉接收方“我这个结构有多长”,类型字段告诉“我是什么数据”。这种设计很灵活,但也带来了安全隐患。
核心问题:广播数据本身没有任何加密或完整性保护。任何在射频范围内的设备都可以读取、修改、重放这些数据。
我记得有一次做客户项目,他们用BLE做门禁系统。手机广播一个“开门”指令,门锁收到后就开锁。你想想看,这个广播包在空气中是明文传输的。我拿一个抓包工具,三分钟就把“开门”的广播包格式摸清楚了。然后我写了个脚本,每隔100毫秒重放一次这个包——门就一直在那里开开关关。客户脸都绿了。
避坑指南:千万不要在广播数据里传输任何敏感信息。我曾经见过有人在广播数据里直接放用户ID和权限等级,这等于把钥匙挂在门外。广播数据只能用于设备发现和基本特征声明,真正的安全通信必须建立在连接之后。
18.2 广播过滤绕过
Android系统提供了一些广播过滤机制,比如基于设备地址的白名单、基于服务UUID的过滤等。但说实话,这些过滤机制在攻击者面前,基本形同虚设。
常见的过滤方式有两种:
- 地址过滤:只接受特定MAC地址的广播
- 服务UUID过滤:只接受包含特定服务UUID的广播
问题在哪?MAC地址是可以伪造的。Android从6.0开始支持随机地址,但攻击者也可以随机。服务UUID更是明文写在广播包里的,改一下就行。
// 攻击者伪造广播包示例
// 原始合法设备的广播
uint8_t legal_packet[] = {
0x02, 0x01, 0x06, // Flags
0x03, 0x02, 0xAA, 0xBB, // 16-bit Service UUID: 0xBBAA
0x0A, 0x09, 0x4C, 0x65, 0x67, 0x61, 0x6C, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65 // "LegalDevice"
};
// 攻击者伪造的广播包(修改了设备名,但保留了服务UUID)
uint8_t fake_packet[] = {
0x02, 0x01, 0x06, // Flags
0x03, 0x02, 0xAA, 0xBB, // 相同的Service UUID
0x08, 0x09, 0x46, 0x61, 0x6B, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65 // "FakeDevice"
};
你看,攻击者只需要保留合法的服务UUID,改个设备名,就能绕过基于UUID的过滤。Android系统在底层并不会验证广播包的来源是否可信。
注意:Android的ScanFilter API只做字段匹配,不做来源验证。这意味着攻击者可以轻松伪造一个看起来合法的广播包,让你的应用误以为发现了可信设备。
我个人习惯的做法是:不在应用层依赖广播过滤来做安全决策。广播过滤只能用于UI展示或初步筛选,真正的设备认证必须放在连接建立之后,通过配对或应用层加密来完成。
18.3 扫描响应数据注入
这里有个很多人忽略的点。BLE设备在收到扫描请求后,可以发送扫描响应数据。这个扫描响应数据,和主动广播数据是分开的。很多开发者只检查主动广播数据,却忽略了扫描响应数据。
攻击手法是这样的:
- 攻击者设备发送一个合法的主动广播包,里面只包含最基本的Flags
- 当扫描设备发送扫描请求时,攻击者返回一个精心构造的扫描响应数据
- 扫描响应数据里包含伪造的服务UUID、设备名、甚至厂商自定义数据
// 攻击者构造的扫描响应数据
// 主动广播包(看起来人畜无害)
uint8_t adv_packet[] = {
0x02, 0x01, 0x06 // 只有Flags
};
// 扫描响应数据(包含恶意内容)
uint8_t scan_rsp[] = {
0x03, 0x02, 0xEE, 0xFF, // 伪造的服务UUID: 0xFFEE
0x0C, 0x09, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, // "TrustedDevice"
0x06, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04 // 伪造的厂商数据
};
为什么这招管用?因为很多Android应用在解析BLE设备时,会把主动广播数据和扫描响应数据合并处理。如果应用只验证了主动广播包,攻击者就可以在扫描响应里塞任何东西。
我曾经在一个智能家居项目里遇到过这种攻击。设备厂商把配网信息放在扫描响应数据里,想着这样能加快配网速度。结果攻击者伪造了一个扫描响应,里面放了个恶意Wi-Fi的SSID和密码。用户的手机自动连上了攻击者的Wi-Fi,后面的事情你懂的。
我的建议:如果你必须在广播或扫描响应里放数据,请加上签名。在广播数据里放一个HMAC,接收方用预共享密钥验证。虽然不能完全防住重放攻击,但至少能防止数据被篡改。
18.4 广播风暴攻击
这个攻击手法比较粗暴,但效果出奇的好。攻击者用多个BLE设备(或者一个设备模拟多个虚拟设备),同时发送大量广播包,造成信道拥塞。
BLE有37个数据信道(0-36),其中37、38、39是主要广播信道。正常情况下,设备在这三个信道上轮流广播。但如果攻击者在这三个信道上同时发送大量广播包,会发生什么?
| 攻击参数 | 正常环境 | 广播风暴下 |
|---|---|---|
| 广播间隔 | 100ms - 10.24s | 20ms(攻击者) |
| 信道占用率 | < 5% | > 80% |
| 设备发现时间 | 1-3秒 | 30秒以上或超时 |
| 连接成功率 | > 95% | < 20% |
你看,广播风暴直接导致设备发现失败、连接超时。更严重的是,Android系统在处理大量广播包时,CPU占用率会飙升,导致系统卡顿甚至ANR。
我记得有一次做渗透测试,我用三台树莓派,每台模拟20个虚拟BLE设备,每个设备以20ms间隔发送广播包。不到30秒,测试手机上的BLE扫描就完全瘫痪了。系统提示“蓝牙扫描超时”,应用直接崩溃。
防御建议:
- 在应用层实现扫描超时重试机制,不要依赖系统默认行为
- 对扫描到的设备数量做上限限制,比如超过50个设备就暂停扫描
- 使用主动扫描模式时,控制扫描窗口长度,避免长时间连续扫描
- 考虑使用连接后的通信替代广播发现,减少对广播的依赖
知识体系总览
下面这张图把四个漏洞的关系和攻击链路画清楚了。我建议你保存下来,以后做安全设计时对照着看。
总结一下:BLE广播的安全问题,根子在于广播数据本身没有保护机制。你想想看,一个没有任何加密、没有完整性校验、没有来源认证的通信方式,怎么可能安全?
我个人在做BLE安全设计时,遵循一个原则:广播只做发现,不做认证;连接之后再做安全通信。 这个原则虽然简单,但能挡住90%以上的广播层攻击。
核心要点回顾:
- 广播数据格式是明文的,AD Structure可以被任意篡改
- Android的ScanFilter只做字段匹配,不做来源验证,容易被绕过
- 扫描响应数据是独立通道,攻击者可以注入恶意内容
- 广播风暴攻击可以导致设备发现失败和系统崩溃
- 所有广播层的安全决策都不可信,必须在连接建立后重新验证