1. 为什么BLE协议栈是物联网安全的“灯下黑”
低功耗蓝牙(Bluetooth Low Energy,BLE)在智能穿戴、智能家居、医疗监测设备里的普及速度,远超大多数安全从业者的想象。一颗纽扣电池能撑几个月甚至几年的无线链路,背后是一整套为省电而高度精简的协议栈。精简意味着取舍,取舍就意味着攻击面。很多人一提到物联网安全,第一反应是Wi-Fi、Zigbee或者云端API,反而把BLE当成“短距离、低速率、没啥可玩”的边缘角色。这个判断在2022年之后已经明显过时了。
BLE的物理层覆盖通常在十米以内,但它的协议栈从广播信道到连接信道、从GATT属性表到配对加密流程,每一层都有可以被观测、被重放、被伪造的环节。更关键的是,大量BLE设备在出厂时就把“可用性”排在“安全性”前面:默认无配对、固定PIN、静态MAC地址、可写特征值不做鉴权。这些设计在功能验证阶段很省事,到了真实部署环境里就是一个个敞开的门。
我接触BLE安全评估的起点,其实是一次很普通的智能门锁测试。当时用手机App连上锁体,抓包发现开锁指令就是一个固定长度的字节序列,重放一次就开了。那一刻我意识到,BLE的问题不在于协议本身有多脆弱,而在于开发者对它的安全模型理解得太浅。后来系统性地啃了BLE核心规范、看了大量实际设备的GATT结构,才慢慢把攻防两端的逻辑串起来。
这篇内容面向的是有一定无线安全基础、想系统吃透BLE攻防的从业者,也适合做物联网产品安全评审的工程师。我会从协议栈的关键机制讲起,把嗅探、枚举、重放、配对降级、GATT写入这些实战动作拆开,配上我实际踩过的坑和验证过的参数。读完之后,你应该能独立完成一次针对BLE设备的完整评估,并且知道哪些环节最容易出问题、哪些工具组合最顺手。
2. BLE协议栈里真正决定攻防走向的几个机制
2.1 广播信道与连接信道:两条完全不同的战场
BLE把通信分成两种基本状态:广播态和连接态。广播态下,设备在37、38、39三个信道轮流发送广播包,任何在射程内的接收者都能收到。连接态下,主从设备约定一个跳频序列,在37个数据信道上按伪随机顺序切换。这个设计本身是为了抗干扰和省电,但对攻击者来说,广播信道是免费的侦察窗口,连接信道则是需要先“入场”才能操作的区域。
广播包里能拿到的东西比很多人想象的多:设备MAC地址、设备名称、发射功率、服务UUID、厂商自定义数据。有些设备甚至在广播数据里直接暴露固件版本和序列号。我见过一个健康手环,广播包里带着完整的厂商数据段,解析出来是生产批次和硬件修订号。这些信息单独看没什么,但配合已知固件漏洞库,就能快速缩小攻击范围。
连接信道的关键在于“接入地址”和“跳频序列”。接入地址是一个32位值,其中只有一部分是有效位,用来在物理层区分不同连接。跳频序列由主设备的时钟和接入地址共同决定。理论上,如果你能同步到主设备的时钟并知道接入地址,就能跟随跳频。实际工具里,嗅探器通常通过先捕获连接请求包来提取这些参数,然后锁定信道跟随。
注意:广播信道抓包不需要配对,也不需要知道任何密钥。很多评估项目里,光是广播包分析就能定位到目标设备的核心服务,省掉大量盲目枚举的时间。
2.2 GATT属性表:BLE的“数据库”也是攻击面
GATT(Generic Attribute Profile)是BLE设备对外暴露数据的主要方式。它把数据组织成服务(Service)、特征(Characteristic)和描述符(Descriptor)三层结构。每个特征有属性(读、写、通知、指示)和权限(需要加密、需要认证)。设备通过ATT协议读写这些属性。
问题出在权限配置上。BLE规范允许特征值设置为“无需认证即可写”,很多厂商为了简化App开发,把控制指令写成这样的特征。比如一个智能灯泡的开关特征,属性是可写,权限是开放。任何连上来的设备都能直接写0x01或0x00。更糟的是,有些设备连配对都不要求,直接开放连接和写入。
我在一次评估中遇到过一款智能插座,它的GATT表里有一个“厂商自定义服务”,里面有个特征叫“校准模式”,可写且无需认证。写进去一个特定字节序列后,设备会进入一个调试状态,串口输出所有内部日志,包括Wi-Fi配网凭证。这个特征在官方App里根本没有入口,但通过枚举GATT表就能发现。这就是典型的“功能留后门,安全没跟上”。
2.3 配对与加密:从Just Works到LE Secure Connections
BLE的配对流程决定了链路是否加密、密钥如何生成。早期BLE 4.0/4.1用的是LE Legacy Pairing,支持Just Works、Passkey Entry和Out of Band三种方式。Just Works没有中间人保护,密钥交换过程可以被嗅探并离线破解(如果配对过程中有足够熵的话,实际上Just Works的TK是0,破解几乎是瞬时的)。Passkey Entry用6位数字,理论上需要在线暴力,但很多设备没有做尝试次数限制。
BLE 4.2引入了LE Secure Connections,用ECDH密钥交换替代了原来的TK交换,中间人攻击难度大幅提升。但前提是设备真的实现了LE Secure Connections,并且配对方式不是Just Works。我实测过不少标称支持4.2的设备,配对时协商下来还是走Legacy Pairing,因为从端固件根本没更新。
还有一个容易被忽略的点:配对后的密钥存储。有些设备把LTK(Long Term Key)明文存在Flash里,或者用固定密钥加密。如果能通过调试接口或者固件提取拿到LTK,就能解密之前抓到的所有加密连接数据。这在硬件拆解结合固件分析的场景里很常见。
2.4 连接参数与信道图:影响嗅探成功率的隐藏变量
连接建立后,主从设备会协商连接间隔、从机延迟、监督超时这些参数。连接间隔决定了两台设备多久通信一次,通常是7.5ms到4s之间。间隔越短,通信越频繁,嗅探器越容易跟上;间隔越长,越省电,但嗅探时容易丢包。
信道图是另一个关键参数。BLE使用37个数据信道,但设备可以根据干扰情况动态屏蔽某些信道。如果嗅探器没有正确同步信道图,就会在跳频过程中跟丢。很多开源嗅探方案在信道图更新时会出现短暂失锁,导致抓到的连接数据不完整。
我的经验是:在开始嗅探之前,先用广播包确认目标设备的连接间隔范围,然后选择一个支持实时信道图跟踪的嗅探固件。如果目标设备连接间隔大于100ms,普通嗅探器基本够用;如果小于20ms,就需要更高性能的硬件和更精细的同步策略。
3. 实战环境搭建:硬件、固件与工具链的选型逻辑
3.1 嗅探硬件的选择:为什么不是越贵越好
BLE嗅探的硬件方案大致分三类:专用商用嗅探器、基于通用无线电的软件定义无线电(SDR)、以及基于常见开发板的开源固件方案。
商用嗅探器比如某款知名分析仪,优势是配套软件成熟、支持多协议解码、能自动跟踪跳频。缺点是价格高,而且固件封闭,遇到非标准协议或者厂商自定义加密时,扩展性差。SDR方案比如用某款宽带接收机配合开源解码工具,灵活性最高,能抓物理层原始波形,但成本高、上手曲线陡,而且对BLE这种跳频协议,实时跟踪需要相当强的计算资源。
我更常用的是基于常见无线开发板的开源固件方案。这类板子价格低,固件开源,可以自己修改抓包逻辑和过滤规则。比如某款支持BLE的开发板,刷上开源嗅探固件后,能通过USB把抓到的包实时传给主机上的分析工具。它的局限是同时只能跟踪有限数量的信道,遇到快速跳频时可能丢包,但对于大多数评估场景已经够用。
选型的时候我会问三个问题:目标设备的连接间隔是多少?是否需要解密加密连接?是否需要长时间持续抓包?如果连接间隔短且需要解密,我会优先选支持多信道并行捕获的方案;如果只是做广播包分析和GATT枚举,一块普通开发板就够了。
3.2 主机端工具链:从抓包到解析的完整链路
主机端我通常用三层工具:底层抓包工具负责从硬件读取原始HCI包或链路层包;中间层协议分析器负责解码和展示;上层脚本负责自动化枚举和测试。
底层抓包,如果硬件走HCI接口,可以直接用系统自带的蓝牙协议栈抓HCI日志。但HCI日志看不到链路层的广播包和连接建立过程,所以做BLE安全评估时,更常用的是链路层嗅探固件配合专用抓包软件。
中间层协议分析器,我主要用两款:一款是通用的无线协议分析套件,支持BLE、Zigbee、802.15.4等多种协议,图形界面友好,适合快速浏览;另一款是命令行工具,适合脚本化处理和批量分析。两款工具都支持导入链路层抓包文件,能解析出GATT交互、配对流程、加密协商等细节。
上层脚本,我一般用Python写。Python有成熟的BLE库,可以在主机上模拟中心设备,扫描、连接、枚举服务、读写特征。配合抓包工具,可以做到“一边操作一边看链路层发生了什么”。这种联动在分析配对降级或者权限绕过时特别有用。
3.3 目标设备的选择与隔离测试环境
做BLE攻防练习,不建议直接拿自己日常用的设备开刀。一来可能影响正常使用,二来某些设备在异常写入后可能变砖。我通常会准备几类目标:一款便宜的BLE温湿度计(广播包丰富,GATT简单)、一款BLE智能锁或插座(有控制特征,适合练重放和权限测试)、一款BLE键盘或手柄(涉及配对和加密,适合练降级攻击)。
测试环境要尽量隔离。BLE是无线协议,周围可能有几十个设备在广播。如果不想被干扰,可以在屏蔽箱或者远离办公区的角落做。我试过在普通办公室里抓包,结果广播包里混进了隔壁工位的耳机、手环、甚至咖啡机。后来用一个简单的金属屏蔽盒,把目标设备和嗅探器放进去,信噪比立刻改善。
提示:屏蔽盒不要完全密封,否则信号衰减太厉害。留一个小缝隙或者用带孔的法拉第袋,既能隔离外部干扰,又能保证目标设备在射程内。
4. 从广播包到GATT写入:一次完整评估的操作链路
4.1 广播包侦察:先看清楚目标在“喊”什么
拿到一个BLE目标,第一步永远是抓广播包。把嗅探器调到广播信道,或者用手机上的通用BLE扫描App,先看设备在广播什么。
广播包的类型有几种:可连接无定向广播、可连接定向广播、不可连接广播、可扫描广播。大多数外设用的是可连接无定向广播,这种广播包里包含设备地址、名称、标志位、服务UUID列表、厂商自定义数据。有些设备还会在扫描响应包里放更多信息,比如完整的本地名称和更多服务UUID。
我习惯把广播包里的信息整理成一张表:设备地址类型(公共地址还是随机地址)、地址是否可解析、广播间隔、发射功率、包含的服务UUID、厂商数据段的十六进制内容。这张表能快速判断设备的“开放程度”。如果广播包里直接带了服务UUID,说明设备希望被快速发现,通常GATT表也比较开放。
随机地址的处理是个细节。BLE设备可以用随机地址来保护隐私,随机地址又分静态随机地址、可解析私有地址、不可解析私有地址。可解析私有地址用IRK(Identity Resolving Key)生成,如果不知道IRK,就无法把同一个设备在不同时间出现的地址关联起来。遇到这种设备,广播包跟踪会变得困难,需要结合连接后的行为来分析。
4.2 连接与GATT枚举:把设备的“数据库”翻个底朝天
广播包看完,下一步是连接并枚举GATT。用主机上的BLE库或者图形化工具,发起连接,然后遍历所有主服务、包含服务、特征和描述符。
枚举的时候要注意几个点。第一,有些设备在连接后会要求配对才能访问某些服务,这时候枚举会失败或者只返回部分结果。遇到这种情况,先尝试不配对枚举,记录下哪些服务可见、哪些不可见。第二,有些设备有多个服务实例,UUID相同但句柄不同,需要逐个读取。第三,特征值的读取权限和写入权限是分开的,一个特征可能可读但不可写,也可能可写但需要认证。
我一般会把枚举结果导出成结构化数据:服务UUID、特征UUID、特征属性、特征权限、当前值。然后对照蓝牙官方定义的UUID列表,识别出标准服务(比如电池服务、设备信息服务、心率服务)和厂商自定义服务。厂商自定义服务往往是安全问题的重灾区,因为标准服务有规范约束,自定义服务全靠开发者自觉。
有一次枚举一个BLE体重秤,发现它有一个自定义服务,里面有个特征可以写入“校准值”。写进去一个负数后,秤的读数直接偏移了。这个特征在App里没有入口,但GATT表里明明白白写着可写。后来我用这个特征演示了如何在不接触秤的情况下篡改测量结果。
4.3 重放攻击:为什么固定指令是BLE安全的头号漏洞
重放攻击在BLE里之所以有效,是因为很多设备把控制指令设计成固定字节序列,而且不加入任何随机数、时间戳或序列号。攻击者只需要在合法用户操作时抓一次包,然后在任意时间重放同样的字节序列,设备就会执行同样的动作。
我做过一个实验:用一款BLE智能灯,手机App开灯时抓包,发现写入的特征值是一个固定长度的字节数组,第一个字节是命令码,后面是参数。关灯是另一个固定字节数组。我把开灯指令保存下来,断开手机,用脚本直接连接灯并写入同样的字节数组,灯亮了。整个过程不需要知道任何密钥,也不需要配对。
重放攻击的变种是“指令拼接”。有些设备把多个功能放在同一个特征里,用不同的命令码区分。如果能枚举出所有命令码,就能组合出设备没有在App里暴露的功能。比如一个BLE风扇,App里只有开关和档位,但GATT表里有个“工厂测试”特征,写入特定命令码后能读取电机转速和温度。这些信息对逆向工程很有价值。
防御重放的标准做法是加入挑战-响应机制或者单调递增的序列号。但很多低成本设备为了省电和简化固件,直接省略了这一步。评估的时候,只要看到控制特征是可写且无需认证,基本就可以判定存在重放风险。
4.4 配对降级与密钥提取:从加密链路里拿数据
如果目标设备要求配对,事情会复杂一些,但也不是无解。首先要判断配对方式。用嗅探器抓配对过程,看配对请求和配对响应里的IO能力字段和认证要求字段。如果双方都声明“无输入无输出”,那大概率走Just Works。Just Works的TK是0,配对过程中的密钥交换可以被完整捕获并离线计算出LTK。
如果设备支持Passkey Entry,但配对时没有做尝试次数限制,可以尝试在线暴力。6位数字有100万种组合,实际测试中,很多设备的配对超时很短,暴力效率不高。但如果设备允许无限次尝试,配合自动化脚本,几个小时能跑完。
LE Secure Connections的配对过程用ECDH,抓包只能拿到公钥,无法直接算出LTK。这时候需要看设备是否真的验证了公钥。有些设备虽然协商了LE Secure Connections,但固件里没有做公钥验证,理论上仍然可以被中间人。不过这种实现层面的问题,需要结合固件分析才能确认。
密钥提取的另一条路是硬件层面。如果能把设备拆开,找到调试接口(比如SWD或UART),读取Flash里的密钥存储区,就能直接拿到LTK。我见过一些设备把LTK明文存在Flash的固定偏移,用调试器读出来就能解密之前抓到的所有加密连接数据。这种攻击的前提是物理接触,但在某些场景下(比如设备回收、维修渠道)是可行的。
5. 那些只有实际动手才会遇到的坑
5.1 嗅探丢包:不是工具不行,是参数没对齐
刚开始做BLE嗅探的时候,我最常遇到的问题就是抓到的连接数据断断续续,GATT读写操作只能看到一半。一开始以为是嗅探器性能不够,换了好几个硬件,问题依旧。后来才明白,丢包的根源往往是嗅探器没有正确同步连接参数。
BLE连接建立时,主设备会在连接请求包里带上接入地址、CRC初始值、窗口大小、窗口偏移、间隔、延迟、超时、信道图这些参数。嗅探器必须完整捕获这个连接请求包,才能跟随后续的跳频。如果连接请求包丢了,后续所有数据都抓不到。如果信道图更新了但嗅探器没跟上,也会在跳频时失锁。
解决办法有两个:一是把嗅探器放得离目标设备足够近,确保连接请求包能被可靠捕获;二是选择支持实时信道图跟踪的嗅探固件,在信道图更新时自动调整。我现在的习惯是,开始抓包前先确认嗅探器能稳定收到目标设备的广播包,然后再发起连接。如果广播包都收不全,连接包更没戏。
5.2 GATT枚举超时:句柄范围和MTU的隐形限制
GATT枚举看起来简单,实际做的时候经常遇到超时或者返回不完整。一个常见原因是ATT_MTU太小。默认的ATT_MTU是23字节,减去ATT头部的3字节,每次只能读20字节。如果特征值很长,需要多次读取。有些设备的GATT表很大,枚举所有句柄需要几百次请求,如果每次请求都等超时,整个枚举过程会非常慢。
另一个原因是句柄范围。BLE的ATT句柄是16位,从0x0001到0xFFFF。但实际设备的GATT表通常只占一小段。枚举的时候,如果从0x0001开始逐个读,会浪费大量时间在无效句柄上。更高效的做法是先读“服务”声明,拿到每个服务的起始和结束句柄,然后只在有效范围内枚举。
我一般会把ATT_MTU协商到最大值(通常是247或512),减少读取次数。同时设置合理的超时时间,太短容易误判,太长会拖慢整体进度。实测下来,对于大多数BLE设备,把MTU协商到247、超时设为500ms,枚举一个中等复杂度的GATT表大概需要十几秒。
5.3 写入被拒绝:权限、加密和认证的三重门
GATT写入失败的原因有很多种,错误码能提供线索。常见的错误码有“写入不允许”“需要认证”“需要加密”“权限不足”。如果返回“需要加密”,说明链路没有加密,需要先配对。如果返回“需要认证”,说明链路加密了但配对方式不够强(比如Just Works),设备要求MITM保护。
还有一种情况是写入成功但设备没反应。这通常是因为写入的值不符合设备预期的格式。比如设备期望一个结构体,你写了一个字节数组,设备可能直接忽略。这时候需要结合广播包里的厂商数据和App的逆向分析,推断出正确的数据格式。
我遇到过一个BLE门锁,写入开锁指令时总是返回“写入不允许”。后来发现这个特征需要先写入一个“会话密钥”特征,再写入开锁指令,而且两次写入之间不能超过一定时间。这种状态机的设计在BLE设备里很常见,评估的时候需要把相关特征的操作顺序理清楚。
5.4 设备变砖:异常写入的边界在哪里
BLE设备被异常写入后变砖的情况,我遇到过两次。一次是往一个“固件更新”特征里写了随机数据,设备重启后卡在Bootloader里,再也连不上。另一次是往“校准”特征里写了超出范围的值,设备虽然还能广播,但所有服务都消失了。
这两次经历让我养成了一个习惯:在写入任何非标准特征之前,先确认这个特征是否可写、写入的值范围是否有文档说明。如果没有文档,先用最小值、最大值、边界值做试探,不要一上来就写随机数据。对于“固件更新”“恢复出厂设置”“调试模式”这类特征,除非明确知道后果,否则不要轻易触碰。
如果设备真的变砖了,有些还能救。比如通过调试接口重新刷固件,或者进入DFU模式恢复。但前提是你能物理接触到设备,并且有对应的固件包。所以在做破坏性测试之前,最好先确认设备是否在保修期内,或者是否有备用机。
6. 从攻击视角反推防御设计
6.1 广播包最小化:少说少错
从防御角度看,广播包应该只包含必要的信息。设备名称、服务UUID、厂商数据这些字段,如果不是功能必需,就不要放。特别是厂商数据段,很多厂商喜欢在里面塞序列号、生产日期、硬件版本,这些信息对攻击者来说是免费的侦察素材。
如果功能上必须广播服务UUID,可以考虑用随机地址配合可解析私有地址,限制未授权设备对设备的长期跟踪。但要注意,可解析私有地址需要IRK分发机制,如果IRK管理不当,反而会引入新的问题。
6.2 GATT权限收紧:默认拒绝,按需开放
GATT特征的权限配置应该遵循最小权限原则。控制类特征必须要求加密和认证,不能开放无认证写入。只读特征如果包含敏感信息,也应该要求加密。对于调试和工厂测试特征,量产固件里应该直接移除或者禁用。
我见过一些做得比较好的设备,它们的GATT表里所有可写特征都要求LE Secure Connections配对,而且配对方式强制Passkey Entry,不允许降级到Just Works。这种设计虽然增加了配网复杂度,但安全性提升明显。
6.3 配对策略:强制LE Secure Connections,拒绝降级
配对是BLE安全的核心环节。设备应该优先使用LE Secure Connections,并且在配对请求里明确要求MITM保护。如果对端不支持LE Secure Connections,应该拒绝配对而不是降级到Legacy Pairing。
对于有输入能力的设备(比如有按键或屏幕),Passkey Entry是比Just Works更好的选择。对于没有输入能力的设备,可以考虑Out of Band配对,通过NFC或者其他带外通道交换密钥。但OOB的实现复杂度较高,很多低成本设备直接用了Just Works,这是需要权衡的地方。
6.4 固件与密钥存储:别把钥匙放在门垫下
LTK、IRK、CSRK这些密钥的存储方式直接决定了设备被物理攻破后的损失程度。理想情况下,密钥应该存储在安全元件或者加密的Flash区域,并且有访问控制。但很多低成本设备把密钥明文存在主Flash里,调试接口一读就出来。
如果硬件上没有安全元件,至少应该对密钥做加密存储,并且把解密密钥放在独立的安全区域。同时,调试接口在量产固件里应该禁用或者锁定。我见过一些设备,调试接口虽然物理上存在,但固件里做了熔丝保护,读出来全是0xFF,这种设计就比裸奔强很多。
7. 我个人在BLE评估中的几条硬经验
做BLE安全评估这几年,踩过的坑和总结的经验不少,挑几条最实用的分享出来。
第一条,永远先抓广播包再连接。广播包是设备主动暴露的信息,不需要任何权限就能获取。很多评估项目里,光是广播包分析就能定位到目标设备的核心服务和潜在弱点,省掉大量盲目枚举的时间。
第二条,GATT枚举要导出结构化数据。手工看GATT表容易漏掉细节,把服务、特征、属性、权限、当前值整理成表格,对照蓝牙官方UUID列表和厂商自定义UUID,能快速识别出异常特征。我一般会把枚举结果存成JSON,方便后续脚本处理。
第三条,重放测试前先确认指令是否固定。不是所有可写特征都适合重放,有些特征的值包含随机数或时间戳,重放会失败。判断方法是连续抓两次相同的操作,对比写入的字节序列。如果完全一样,重放大概率有效;如果有变化,需要进一步分析变化规律。
第四条,配对降级测试要抓完整配对过程。配对请求、配对响应、密钥交换、密钥分发,每一步都可能存在降级点。用嗅探器完整捕获配对流程,对照BLE规范检查每一步的字段,能发现很多实现层面的问题。
第五条,破坏性测试前先备份。如果设备支持固件读取,先把原始固件备份出来。如果设备有DFU模式,先确认DFU的进入方法和恢复流程。这样即使测试过程中设备异常,也有恢复的余地。
BLE攻防的深度远不止这些,从链路层的跳频同步到应用层的协议逆向,每一个环节都有可以深挖的细节。但核心逻辑是相通的:先看清楚设备在做什么,再找到它没做好的地方,最后用最小代价验证问题。这个过程需要耐心,也需要对协议栈的扎实理解。希望这些内容能帮你少走一些弯路,更快地建立起自己的BLE评估方法论。