手头有一个马蹄形状的自行车锁,手机通过蓝牙可以操作开锁。正好最近在学习低功耗蓝牙(BLE)协议,就拿他练练手。
网上有挺多抓包方式,包括手机端HCI,这里推荐国外的开源项目Sniffle。Sniffle是英国网络安全公司NCC Group在2019年底开源的一个基于使用TI CC1352/CC26x2硬件的蓝牙5和4.x LE嗅探器(抓包工具),最新的release版本是2022年8月发布的1.7。使用Sniffle需要购买指定的蓝牙开发板,并刷入固件才能使用,电脑通过串口与蓝牙开发板通信。Sniffle项目中fw文件夹是固件源码,如果只是抓包的话,在Sniffle项目release1.7中有上传的编译好的固件,根据型号下载。搜索相关资料得知,可以在TI网站上安装UNIFLASH软件来进行刷固件:https://www.ti.com/tool/UNIFLASH?keyMatch=&tisearch=search-everything&usecase=software%23downloads#downloads
抓包测试:Sniffle抓包方式是开发板抓到数据之后,通过串口发送给PC,PC收到数据包之后再根据设定条件来过滤数据,最后根据参数决定是否保存成pcap文件。因此需要用参数-s 指定某个串口,串口可以通过设备管理器查看,共有两个,选择UART的串口:从README中可以知道python_cli中的sniff_receiver.py为抓包脚本,支持多个参数,这里简单介绍下常用的设置:-a 只抓广播包,不知道设备mac地址的情况下,可以用此参数-m 只抓特定mac地址的数据包,可以从广播包中分析出目标设备mac地址-o 抓包结果保存到pcap文件下图是命令sniff_receiver.py -s COM7 -m xx:xx:xx:xx:xx:xx -o data.pcap的显示:下图是保存的pcap文件中的BLE开始连接过程截图至此抓包工作完成,下面开始分析数据包。
根据BLE协议栈,链路层主要是维持连接,我们暂时不关注链路层,所以使用wireshark显示过滤器btatt只显示att协议数据包。前面主要是GATT的过程,这里只简单介绍一下: GATT层定义了一个4层树形框架,其中根节点为Profile(配置),它有不同的Services(服务),不同的服务有不同的Characteristics(特征),不同的特征通过一个具体的Value(值)或者Descriptor(描述符)来定义。网上关于GATT的资料很多,看的迷迷糊糊地,反正是没特别明白这东西。大致意思就是通过GATT交互可以知道BLE设备有哪些接口,这些接口是可读/可写等等。可以把GATT当做接口说明,是固定的,跟应用层交互关系不大,直接跳过。往下翻在215帧Sent Write Command, Handle: 0x000d出现了可变数据,很大概率是应用层数据。简单查看后续数据包发现所有的发送数据包长度都是16字节,无可见字符串与00等数据,很明显是加密了,大概率是AES(因为16字节正好一整行,并且安卓上对称加密普遍采用AES)。有加密就要有密钥交换方式,有的开发者采用固定字符串在代码中写死,有的是动态传输或计算的,比如SSL的密钥交换算法。一帧一帧的往前翻数据包,发现第一个应用层数据帧在214帧:这一帧是Handle Value Notification,之前没有Master发送给Slave的应用层数据,并且这个数据长度超过16,多次抓包前面的14 00 80是固定的,根据经验猜测是每次开始通信之前,Slave先把本次会话密钥发送给Master,一次一密,防止重放攻击。
既然数据包加密了,再分析pcap就没有啥意义了,于是打开jadx开始逆向APP。首先搜索AES,简单翻找后发现提取key的方式:这里的20不就是刚才数据包开头的0x14么,密钥是从下标3开始的,0x15的话是从下标4开始。最开始看数据包还有疑问,为什么前3个字节不变的情况下,后面是17个随机字节而不是16呢?看到代码就明白了有两种情况,干脆生成17个字节,各取所需。再看加密函数,ECB模式无IV,无填充,直接拿key解密后续数据就行。这里有个疑问,ECB模式要求数据16字节对齐,不填充怎么凑齐16字节?直到解密数据后发现:好家伙,怪不得不用填充,自己手动填充了00。填充00意味着得有地方写明长度,不然末尾字节恰好为00咋办?继续逆向看代码吧。通过logcat可以知道,command和command2就是发送的数据包,在调用mBleService.sendMessage之前进行了加密。那数据的组包就是getCommand干的了。继续进入getCommand发现jadx出错无法显示:于是祭出版主的GDA:通过GDA的逆向分析,弄清出了组包格式。数据包示例:08 01 02 000D9038 E0长度:1字节,包含自身的有效数据长度方向:1字节,0表示BLE设备回复,1表示发送给BLE设备功能码:示例中02表示验证密码,05表示开锁参数:不同功能码参数数量和长度不同,具体可看getCommand函数。示例中0xD9038换算成十进制是888888,即锁的密码(这个锁限制密码只能是6位数字)校验码:前面的数据逐字节相加,最后与上0xFF通过简单分析不难弄懂开锁流程:1、蓝牙完成连接后,锁主动发送本次会话密钥给手机端;2、手机端生成确认密钥功能码并加密发送,锁再回复确认数据包;3、手机端将十进制密码转成16进制,发送验证密码功能包,锁回复验证结果;4、如果密码正确,手机端点开锁会发送05开锁功能码打开锁;5、开锁完成后,锁马上主动断开连接,节省功耗。中间有穿插时间戳同步的数据包,就不多介绍了。这里额外提一句:逆向最怕的是无法反编译、反汇编,GDA还是很强大的,只要能反编译,再难看也能给他一点点的梳理明白。有总比没有强!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
New对象处 好家伙,你拆了小蓝