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设备在收到扫描请求后,可以发送扫描响应数据。这个扫描响应数据,和主动广播数据是分开的。很多开发者只检查主动广播数据,却忽略了扫描响应数据。

攻击手法是这样的:

  1. 攻击者设备发送一个合法的主动广播包,里面只包含最基本的Flags
  2. 当扫描设备发送扫描请求时,攻击者返回一个精心构造的扫描响应数据
  3. 扫描响应数据里包含伪造的服务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广播漏洞攻击链路 攻击者设备 ① 广播数据篡改 ② 过滤规则绕过 ③ 扫描响应注入 ④ 广播风暴攻击 Android扫描设备 设备发现失败 连接被劫持 数据被篡改 系统ANR/崩溃 防御:应用层认证 + 加密

总结一下:BLE广播的安全问题,根子在于广播数据本身没有保护机制。你想想看,一个没有任何加密、没有完整性校验、没有来源认证的通信方式,怎么可能安全?

我个人在做BLE安全设计时,遵循一个原则:广播只做发现,不做认证;连接之后再做安全通信。 这个原则虽然简单,但能挡住90%以上的广播层攻击。

核心要点回顾:

  • 广播数据格式是明文的,AD Structure可以被任意篡改
  • Android的ScanFilter只做字段匹配,不做来源验证,容易被绕过
  • 扫描响应数据是独立通道,攻击者可以注入恶意内容
  • 广播风暴攻击可以导致设备发现失败和系统崩溃
  • 所有广播层的安全决策都不可信,必须在连接建立后重新验证

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