如何实现蓝牙配对方法混淆攻击
背景
蓝牙技术是一种无线数据和语音通信开放的全球规范,它是基于低成本的近距离无线连接,为固定和移动设备建立通信环境的一种特殊的近距离无线技术连接。蓝牙具有无线、短程等特点,主要能够提供设备到设备之间的通信。蓝牙协议目前从最初的 1.0 版本更新到了 5.3 版本,并在 4.0 版本之后分为了经典蓝牙(BC)和低功耗蓝牙(BLE),BLE 在保证了 BC 的大部分性能的情况下最大程度上降低了功耗,因此在如今的更多智能设备中被采用。
本篇博客将介绍一种新型的低功耗蓝牙攻击方式,攻击发生在蓝牙的配对过程中。论文来自于《Method Confusion Attack on Bluetooth Pairing》,作者将代码放在了 github。
原理
低功耗蓝牙配对过程
低功耗蓝牙配对共分为四个步骤:配对信息交换、公钥交换、认证阶段、长期密钥的生成与验证。
配对信息交换
在配对信息交换阶段,将要进行配对的设备会交换配对过程中一些所必须的信息,比如:
- OOB-bit:表明带外信息是否准备就绪
- MitM-bit:表明是否需要验证
- IOCaps:表明待配对设备的输入输出能力
IOCaps一般会表明待配对设备的输入输出能力,具体分为以下六种:
- DisplayOnly:设备只能显示一个6位数的数字值。
- DisplayYesNo:设备可以显示一个6位数的数值,用户可以输入确认(是或否)。
- KeyboardOnly:用户可以输入一个6位数的数字值和一个确认。
- KeyboardDisplay:设备可以显示一个6位数的数字值,用户可以输入一个6位数的数字值并确认。
- NoInputNoOutput。设备没有能力与用户通信
根据不同的输入输出能力,认证阶段会采取不同的认证方式。
公钥交换
在完成配对信息交换后,会进入公钥交换阶段,如图1所示。首先发起者I与响应者R会分别选取一个私钥 \(SK_I\) 与 \(SK_R\) ,之后采用 Elliptic Curve Diffie-Hellman (ECDH) 算法加密生成公钥 \(PK_I\) 与 \(PK_R\)。在交换公钥之后,在交换公钥之后,再分别根据自己的私钥计算出相同的 \(DHK\),从而生成相同的密钥。
认证阶段
为了验证交换的公钥是否正确,保证交换过程中没有遭到中间人攻击,目前常用的认证方法共有四种:
-
Just Works(直接工作):密钥未经验证(即未经验证的安全要求)。
-
Out of Band(带外认证):公钥通过独立于蓝牙的反向通道(例如,NFC、二维码)进行验证。
-
Numeric Comparison(数字比较):用户在两台设备上显示一个 6 位数字,必须确认它们是否相等。
-
Passkey Entry(密码输入):用户在一台设备上显示一个 6 位密码,并被要求将其输入另一台设备。
在认证阶段,会根据交换得到的 IOCaps 位,即两个待配对设备的输入输出能力,来采取不同的认证方式,如下图所示。
方法混淆攻击的主要对象是数字比较与密码输入两种方法,下面将详细说明两种认证方法的异同。
数字比较(Numeric Comparison)
首先发起者 \(I\) 与响应者 \(R\) 会分别生成一个设定值 \(ra\) 和 \(rb\)(为0),一个随机值 \(N_I\) 和 \(N_R\)。之后响应者 \(R\) 会用由协议规定的 \(f4\) 算法根据公钥 \(PK_I\) 与 \(PK_R\)、随机值 \(N_R\) 与设定值 \(rb\) 生成一个校验值 \(C_R\),传递给发起者 \(I\),并在交换 \(N_I\) 和 \(N_R\) 后,由发起者进行校验。随后发起者 \(I\) 与响应者 \(R\) 会由公钥 \(PK_I\) 与 \(PK_R\),随机值 \(N_I\) 和 \(N_R\) 哈希出一个六位数字,并在两边的设备上显示,由用户对两个数字进行比较并确认。若确认结果相同,则完成认证,否则认证失败。
下面这个视频演示了数字比较的过程:
密码输入(Passkey Entry)
首先在发起者 \(I\) 与响应者 \(R\) 会有一方生成一个 20 位密码,并由用户输入到另外一方,双方便获得了相同的设定值 \(ra\) 和 \(rb\)。随后双方会对 \(ra\) 与 \(rb\) 进行逐位检验。具体过程是,对于 \(ra\) 和 \(rb\) 的每一位,发起者 \(I\) 与响应者 \(R\) 会首先生成一个随机值 \(N_I\) 和 \(N_R\)。之后双方会用由协议规定的 \(f4\) 算法根据公钥 \(PK_I\) 与 \(PK_R\)、随机值 \(N_I\) 和 \(N_R\) 生与设定值当前位 \(rai\) 与 \(rbi\) 生成校验值 \(C_I\) 与 \(C_R\),并在交换 \(N_I\) 和 \(N_R\)、\(C_I\) 与 \(C_R\) 后,由双方进行校验。在这个过程循环 20 次,\(ra\) 与 \(rb\) 的每一位都得到校验后,若所有位相同,即完成了密码输入的认证过程,否则认证失败。
下面这个视频演示了密码输入的过程:
长期密钥的生成与验证
在完成验证之后,双方会由协议规定的 \(f5\) 算法根据 \(DHK\),随机值 \(N_I\) 和 \(N_R\),地址 \(addr_I\) 和 \(addr_R\) 生成长期密钥 \(LTK\),\(LTK\) 会作为长期通讯中的加密密钥,有了 \(LTK\) 之后下次两个蓝牙设备之间就无需配对过程:
此外,在生成长期密钥后,还会进行最终阶段的验证,如下图所示
方法混淆攻击
根据 Initiator 和 Responder 与中间人验证方式的不同,方法混淆攻击分为 PoN 攻击与 NoP 攻击两种,其中 PoN 方法混淆攻击的原理如下图所示。
首先在配对信息交换阶段,在接收到发起者 \(I\) 的信息后,触发中间人发起者 \(M_I\) 与响应者 \(R\) 进行配对信息交换。注意此时中间人响应者 \(M_R\) 返回给发起者 \(I\) 的 IOCaps 为 DisplayOnly,而中间人发起者 \(M_I\) 发给响应者 \(R\) 的 IOCaps 为 DisplayYesNo,这样便保证了之后在认证阶段中间人响应者 \(M_R\) 与发起者 \(I\) 进行密码输入认证,而中间人发起者 \(M_I\) 与响应者 \(R\) 进行数字比较认证。
之后中间人者 \(M_R\) 与 \(M_I\) 分别与发起者 \(I\) 与响应者 \(R\) 进行公钥交换。完成后首先中间人发起者 \(M_I\) 和响应者 \(R\) 会进行数字比较认证,并在此过程中将比较值传给中间人响应者 \(M_R\), 中间人响应者便会利用这个值与真正的发起者进行密码输入认证。由于所有值都是由中间人传输的,所以双方最终都会认证通过并进入长期密钥的生成与验证阶段,同理,最终验证也会通过。至此,便完成了中间人 \(M_R\) 与 \(M_I\) 与真正的发起者 \(I\) 与响应者 \(R\) 之间的蓝牙配对。NoP 攻击原理和 PoN 类似,唯一的差别就是交换了 Initiator 和 Responder 与中间人的验证方式。
BTstack
BTstack 开源库实现了蓝牙协议栈,它的架构如下图所示。可以看到,BTstack 使用单个 Run Loop 处理应用程序或者蓝牙模块发来的数据包。蓝牙协议栈有 GAP、ATT 和 L2CAP 等多个层级,BTstack 在每个层级处都提供了回调函数,应用程序可以在这些回调函数中处理收到的数据。
实验过程
实验配置
为了实现 NoP 攻击,我们准备了 4 个不具备独立 MAC 地址的 CSR 蓝牙加密狗,接着使用 Bluetooth Mac Address Changer 工具修改加密狗的 MAC 地址,使得他们的 MAC 地址各不相同。NoP 攻击框图如下图所示,两个加密狗作为 Initiator\((I)\) 和 Responder\((R)\),另外两个作为 Mitm Responder\((M_R)\) 和 Mitm Initiator\((M_I)\),其中 \(I\) 与 \(M_R\)、\(R\) 与 \(M_I\) 之间通过蓝牙进行通信,\(M_R\) 与 \(M_I\) 之间使用管道进行进程间通信。
实验一开始,启动 Responder 进程,向外界广播 ADV_IND 帧,表明自己处于可连接的状态。接着启动 \(M_R\) 与 \(M_I\) 父子进程,其中 \(M_R\) 的蓝牙名和 \(R\) 保持一致,这样 Initiator 就可能错误地将 \(M_R\) 当做 \(R\) 而与中间人进行连接。如下图所示,手机蓝牙的可用设备中有两个名为 CARDREADER 的蓝牙设备,Initiator 无法辨别出哪个是 \(R\) 那个是 \(M_R\)。
配对过程
- \(I\) 与 \(M_R\) 建立连接,触发
HCI_EVENT
,BTstack 创建类型为HCI_EVENT_LE_META
类型的 packet 并调用 \(M_R\) 的responder_sm_packet_handler
回调函数,这个回调函数用来处理与加密连接配对相关的数据包; - 在
responder_sm_packet_handler
回调函数中将启动 \(M_I\) 与 \(R\) 的连接的消息写入管道; - \(M_I\) 监听到管道中有让自己与 \(R\) 进行连接的消息,就立即与 \(R\) 建立连接,并接着等待 \(M_R\) 给自己发过来 \(V_a\);
- \(M_R\) 将计算出的 \(V_a\) 写入管道;
- \(M_I\) 收到管道中传来的 \(V_a\),完成与 \(R\) 之间的 Passkey Entry 验证过程
通信过程
- \(I\) 通过蓝牙模块发送消息给 \(M_R\);
- \(M_R\) 收到消息,触发
l2cap_mitm_responder_packet_handler
回调函数,这个回调函数用来处理 L2CAP 层的数据包; l2cap_mitm_responder_packet_handler
中识别到 packet 类型为L2CAP_DATA_PACKET
,将 packet 的数据拷贝到缓冲区中,此时也可以直接修改缓冲区的数据为中间人想要的消息;- BTStack 的 Run Loop 的每次循环都会调用
forward_packet
函数,在这个函数中 \(M_R\) 将缓冲区的数据写入管道; - \(M_I\) 在
forward_packet
函数中将管道的数据拷贝到缓冲区,接着转发给 \(R\)
实验结果
NoP攻击结果如下述视频,Initiator 发送的原始消息“硝子真可爱”被中间人截获,并被修改为“我家硝子真可爱”后转发给 Responder,此时 Responder 收到的就是修改后的消息。
总结
在本文中,我们复现了蓝牙配对方法混淆攻击,该攻击方式利用蓝牙配对过程中设备不会确认双方实际使用的认证方式是否相同的漏洞,成功实现了发起者和响应者的双向欺骗。该攻击方式被提出之后,部分手机厂商(如华为)在蓝牙配对的确认环节会弹出提示框,要求设备使用者仔细核对认证方式是否相同,这在一定程度上可以减少方法混淆攻击的危害性,但治标不治本,要想彻底杜绝方法混淆攻击的危害,需要对蓝牙协议打上补丁。