跳转至

对某款智能手表的分析与攻击

对某款智能手表的分析与攻击

手表介绍

功能上看起来只是个加大表盘版本的手环... 而且我打字习惯性的打出手环来,所以后面就不纠结这个叫法了,可能混着用哈哈哈;不过表带挺软的,摸起来很舒服,带起来也很轻

1727825469018-beb9d5e2-8634-434b-8a57-ed68b05841c7.jpeg

官方 APP 叫 Runmefit,在国内叫:即米运动健康

1727825641608-5c623d64-a263-44d0-ab1d-7b7456141c09.png

该有的功能都有,不过我主要关注两个,一是能不能让手环震动(查找设备)另一个是能不能显示任意消息(消息通知)

1727826166032-fbe71314-1769-4fe5-9f4f-cf00f2237ff8.png

BLE 抓包

那先来简单抓个包看看吧,BLE 的空口抓包方案我已经做过总结,可以直接参考我的语雀文档:

1727826262006-b2c78a96-4bae-4767-8267-08cb7eed4b40.png

这里我推荐 Sniffle 这个项目,可以用 python 来操作,可自定义程度比较高,而且价格也算实惠,具体方法直接看语雀文档吧,不再重复赘述

https://www.yuque.com/hxfqg9/iot/wpqb50p4gu18mhs2

对于 BLE 抓包有不熟悉的小伙伴,这里再以手机和手表为例,介绍一下为什么能够抓到 BLE 的空口数据,首先要明确一点,BLE 的通信是跳频的,也就是说如果你的设备只能抓取一个信道的数据,那你百分百是抓不全的,那上面那些方案是咋实现空口抓包的呢,这就需要从 BLE 的连接机制讲了

手机能扫描到手表是因为手表正在往外发送 BLE 广播包,手机扫描到广播包知道手表的存在,与手表建立连接时双方会沟通一系列参数来确定后续的 BLE 跳频通信信道(如下图中的 Channel Selection Algorithm 信道选择算法、Channel Map 可用信道、Hop 跳频参数),只要抓包设备能够捕获到这个包,就可以提取通信双方约定的一系列参数,也跟着它们同步跳频

1727841850038-1e5a4976-6b25-409f-8f4d-3b64a910f07b.png

而 "众所周知",BLE 的广播信道也是在 37、38、39 这三个信道跳频的,你抓包工具怎么就能保证抓到手机连接手表的数据包呢?那就需要抓包设备可以同时抓取三个广播信道进行监听了,有些设备你往虚拟机里面插的时候会发现要连接三次,就是因为它内置了三颗芯片,来确保能够同时捕获三个广播信道中的连接包,从而同步跳频

那么既然想要跟着手表同步跳频,就需要能够抓到手机与手表建立连接的包,因此抓包之前要确保手机已经和手表断开连接,抓包后再进行连接

然后抓包的时候可以指定 MAC 地址进行过滤,手环的 MAC 地址可以通过手环中的关于选项看到,也可以通过扫描得到:21:23:10:21:05:43

1727826644823-5ae85654-9433-498f-9e95-90ba45d6b35d.png

-m 参数过滤 MAC 地址,-o 参数将捕获到的流量保存为 pcapng 文件

./sniff_receiver.py -m 21:23:10:21:05:43 -o 1.pcapng

1727826911647-805683e9-20d5-4a26-ad0e-4d34322ab889.png

开始抓包后就可以用手机正常连接手表,然后操作对应的功能了,这时候要考虑好,想通过抓这次包来分析什么,别上来一通乱点,最后看着满屏的数据包不知道啥是啥,带着目的去抓,比如我想分析手环震动的数据包(查找设备)那这次抓包我就只点查找设备这一个功能,多点几次,这样在流量包里面好定位哪一条数据包是该功能的 BLE 流量

这里还要吐槽一点感觉在很多 BLE 的设备上并没有很好的区分出来 BLE 的连接、配对的概念,在我的理解中,配对是要走 SMP 协议的,是要进行链路层加密的,但是这个手环的配对显然仅仅是做了一个 BLE 的连接,更像是他自己在应用层进行的手机与手表的牵手配对

1727827522281-adcfc504-4af4-4376-ad11-c3758ae07a02.png

在捕获到的流量包中过滤掉呼吸包,选中 Empty PDU 的数据包,右键作为过滤器应用 -> 非选中

1727828777982-df3dc25d-31ee-42e6-b37a-089252b6e35f.png

我这次等待所有信息加载完后,按了三次查找设备,应该就是这三次了

1727828914225-3d7c1c37-1d68-4d3c-9d7c-60a520374752.png

不用考虑太多,直接展开具体的数据包,可以看到有对应的 Value 和对应的 UUID,直接连接发送一下试试,经过尝试确认这两次就是查找设备和找到设备后点击确定从而停止查找的 BLE 指令,而且因为是个手表的缘故,除了震动还附带声音,效果不错

da03010001c997  开始查找
da030100000857  停止查找

同样的,可以开启消息通知之后抓一下收到消息的 BLE 指令,然后重放也是有效的,但是怎么发送自定义消息呢

比如我用 test 账户发了个 6,可以看到直接明文传输的

1727854231379-2250aa2c-1ce3-48ba-b5cb-6932fe3aa45c.png

复制这段指令出来 da380e00180a010f063004010a74657374369cc2 ,直接重放也是有效的,但是重放也太没用了,能不能修改内容呢

1727854376357-05363eb8-66a1-4d14-9cf1-902c9de4eb98.png

根据 ASCII 直接解析后的内容修改十六进制值,例如 test 对应了 74657374,修改为 tesA,即 74657341,整个数据就是:da380e00180a010f063004010a74657341369cc2,发过去并没有响应,说明他对数据做了校验,我不认为你能靠多次抓包分析出来校验格式,那么接下来逆向 APP 吧

APP 逆向

APP 没加固啥的,直接丢到 jadx 里面看一下,一开始搜索了一下 wechat,发现有个位置是 bluetoothsdk 且是 MessageType 可以说跟蓝牙消息通知十分关联了

1727856047229-a77e86d6-efa1-4f38-9433-2787ea480d32.png

点进来看看,是一个枚举类型,列了一串的 APP 对应的值,大概率就是根据这个值来显示不同的 APP 图标了,例如上面显示的是微信的图标,那应该是 MessageType 是 10 也就是十六进制 0xa,在上面的数据中也确实有这个值

1727856221618-bd631a86-cf76-4669-94f7-e31ad2b7c863.png

再来看一眼数据:da380e00180a010f063004010a74657374369cc2

04 01 0a 可能就是 发消息人的名字长度、消息长度、消息来源类型

那再往下咋看呢,选择交叉引用(快捷键也是 X)看哪里调用了 MessageType,其中有些 sendMessage 函数比较有意思,发送消息?这不摆明了就是你吗,跟进去看看

1727856647338-f811e8fe-ec11-41d5-b5a5-acc714eed78a.png

跟着跟着跟到了这里:com.starmax.bluetoothsdk.StarmaxSend,可以看到他接收到了 messageType、title、content,并把它们转成了字节数组,然后使用 Calendar.getInstance() 取了当前时间,但是从前面的重放的效果看,时间应该不会去校验,可能就是在手表上区分先后顺序吧。反正它把一些参数一顿拼凑,然后放进了 StarmaxSendRequest 函数

1727777099121-3290b4f4-0aba-4d4c-a927-3663548a106a.png

com.starmax.bluetoothsdk.StarmaxSendRequest 里的 StarmaxSendRequest 函数重新定义了一个字节数组,然后把自己的一些数据拼了进去

1727777131908-5fc5e80b-64dd-4cdf-b7f1-6d74cd62766c.png

其中 companion.check 函数就是把数据从 int 类型转成字节类型

1727857182884-900c25f0-cfe3-4673-b8b0-e54b5ca7d056.png

所以这堆数据的头部我们已经可以看出来了,218 就是 0xDA,i10 是传进来的参数 56 也就是 0x38

1727857333236-3c11c74c-eff5-4269-84de-366e5e4b1548.png

后面的内容是传进来的字节数组,再往后有一个 CRC 校验

1727860312740-234e1078-5087-4545-a1db-d1c9868a9a2c.png

跟进 CRC 函数,看一下是做的何种校验方式,可以看到明显是 CRC16,但是是何种 CRC16 呢

1727774807984-3f9e3599-24b6-4a6f-ad47-e04ff4d0f5e7.png

打开一个在线 CRC 计算的网站(http://www.ip33.com/crc.html),可以看到一堆 CRC16 的函数,对于 CRC 算法还没有深入了解,我决定先试一试

1727860391876-7a476c79-3d45-4467-830a-3333226e3635.png

上来就中奖了,可以看到是 CRC-16/IBM 方式计算的,算出来的值要反转一下

1727860493257-d1cc7b9d-00a4-4544-a907-e64d6551efb8.png

那么整个数据格式如下

首先由时间、title 长度、context 长度、消息类型、title、content 组成一段字节数组记为 data

再创建一组字节数组,0xda、0x38 是开头固定的,后面跟 data 的长度 和 data 长度右移 8,在跟上 data 组成要发送的 BLE 指令

最后对 BLE 指令进行 CRC16-IBM 计算,得到两个字节的 CRC16 反转后附到 BLE 指令中

写个脚本转换一下 BLE 指令,搞定!

from crcmod import mkCrcFun

title = "yichen:".encode('utf-8')
content = "点赞! 转发!".encode('utf-8')
title_len = len(title).to_bytes(1,byteorder='big')
content_len = len(content).to_bytes(1,byteorder='big')
msg_type = bytes.fromhex("0a")
data = bytes.fromhex("180a010f0616") + title_len + content_len + msg_type + title + content
head = bytes.fromhex("da38") + len(data).to_bytes(1,byteorder='big') + (len(data) >> 8 ).to_bytes(1,byteorder='big')
ble_data = head + data

# 定义CRC16/IBM的函数
crc16_ibm = mkCrcFun(0x18005, rev=True, initCrc=0x0000, xorOut=0x0000)
crc_value = crc16_ibm(ble_data).to_bytes(2, byteorder='big')[::-1]
ble_data = ble_data + crc_value
print(ble_data.hex())

还要我再说一遍吗?点赞!转发!

1727828014293-5b6b17b2-b1fc-473f-916c-dffc0873e41e.png

原文: https://www.yuque.com/hxfqg9/iot/lf70n79yhsbubtha