BLE分析及实践--抓包及风险分析

上篇BLE分析及实践——模拟智能门锁已经通过树莓派把智能门锁模拟好了,下一步来进行抓包实践

这个已经完成了分析,下篇来最终说明对智能门锁的攻击过程

使用蓝牙扫描APP

环境:android

工具:no.nordicsemi.android.mcp_87.apk

依次点开可以看到相应的属性及属性值

使用工具bleah

地址: 安装上,直接运行bleah。

输出

+ b8:27:eb:90:28:e1 (-45 dBm) -----------------------------------------------+ | Vendor | Raspberry Pi Foundation | | Allows Connections | ✓ | | Flags| LE General Discoverable, BR/EDR | | Manufacturer | u4c36b6d334c30634be49c7d89dc5 |

这个就是我们模拟出来的智能门锁。

使用bleah -b "b8:27:eb:90:28:e1" -e 连接

输入的内容为:

+--------------+------------------------------------------------------------+-----------------------+------------------+ | Handles| Service > Characteristics| Properties| Data | +--------------+------------------------------------------------------------+-----------------------+------------------+ | 0001 -> 0005 | Generic Access ( -0000-1000-8000-00805f9b34fb )| || | 0003 | Device Name ( 00002a00-0000-1000-8000-00805f9b34fb ) | READ| uraspberrypi | | 0005 | Appearance ( 00002a01-0000-1000-8000-00805f9b34fb )| READ| Generic Computer | ||| || | 0006 -> 0009 | Generic Attribute ( -0000-1000-8000-00805f9b34fb ) | || | 0008 | Service Changed ( 00002a05-0000-1000-8000-00805f9b34fb ) | INDICATE|| ||| || | 000a -> 0015 | b-6d33-4c30-634b-3 | || | 000c | b-6d33-4c30-634b-d44 | WRITE || | 000f | b-6d33-4c30-634b-4 | NOTIFY INDICATE || | 0013 | b-6d33-4c30-634b-4 | NOTIFY READ INDICATE|| ||| || +--------------+------------------------------------------------------------+-----------------------+------------------+

工具已经做好了data的解析

可以看到handle为 000c的是可写的,uuid是: b-6d33-4c30-634b-d44,是属于厂商自定义的uuid值,但是此时并不知道是写入的啥值才能操作智能门锁。

使用工具gatttool

直接运行 gatttool -b b8:27:eb:90:28:e1 -I

此时出现:

[b8:27:eb:90:28:e1][LE]> connect Attempting to connect to b8:27:eb:90:28:e1 Connection successful [b8:27:eb:90:28:e1][LE]>

提示连接成功,此时已经与智能门锁建立了链接

使用 primary查看主要的server

使用 characteristics 查看所有的属性

handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb handle: 0x0007, char properties: 0x20, char value handle: 0x0008, uuid: 00002a05-0000-1000-8000-00805f9b34fb handle: 0x000b, char properties: 0x08, char value handle: 0x000c, uuid: b-6d33-4c30-634b-d44 handle: 0x000e, char properties: 0x30, char value handle: 0x000f, uuid: b-6d33-4c30-634b-4 handle: 0x0012, char properties: 0x32, char value handle: 0x0013, uuid: b-6d33-4c30-634b-4

使用 char-read-hnd或 char-read-uuid来读取里面的值

我们来读

[b8:27:eb:90:28:e1][LE]> char-read-uuid 00002a00-0000-1000-8000-00805f9b34fb handle: 0x0003 value: 72 61 73 70 62 65 72 72 79 70 69

是ASCII码,转码的话可以看到就是 raspberrypi

里面还有

char-write-req<handle> <new value> Characteristic Value Write (Write Request) char-write-cmd<handle> <new value> Characteristic Value Write (No response)

两个方法,就可以往智能门锁中写入蓝牙数据。

获取BLE的数据

1、手机蓝牙日志首先需要打开开发者调试工具,抓取蓝牙数据包,这个网上有不少的教程,这里不再细说。

抓取到的log文件可以直接用wireshark打开,这里做了两个操作,一个开锁,一个关锁。

wireshark打开后的界面显示如下:

APP端为localhost,智能门锁为remote。

结合下文的协议栈可以看到上层的协议为ATT:

过滤协议ATT:

可以看到均是对handle为000c的写操作,符合前面的分析。

直接看数据值,有两个值:bb 和 aa

也就是说发送这两个数据就会产生开锁和关锁的操作,这里先记录下来,后面我们演示攻击时候再用。

这时候直接发送这个数据,门锁是不认的,原因是没有进行配对操作,在看树莓派上的日志输出可以看到:

Status read request: AUTH INIT: returning random challenge : 6db291acca6aa15063c4d6 >> received cmd: ae2dec2c986edeff444abfabc00 KEY ID: 0 AUTH RESP: ae2dec2c986edeff444abfabc CRYPTED step 1: 37c3fb1f48f5038af2cbe6d67a CRYPTED final: ae2dec2c986edeff444abfabc AUTHENTICATION OK!

里面是包含了认证的过程的。

我们在手机上抓包认证过程:

2、 认证过程分析我们分析这个认证过程:

客户端请求handle:0x0013 uuid:b‑6d33‑4c30‑634b‑4

服务器端返回一个值:d7c11f336ae6573e7eec

客户端写入一个值:e1cbdfb88d05890a935bf830fb9fbd1000

此时完成了认证过程。

客户端应该是利用服务器返回的值计算一个值,与服务端一致则可通过认证 这个值得计算过程在app中应该是存在的,我们反编译APP查找计算逻辑。

反编译的过程不再多说。

找到的逻辑为:

Log.d("Authentication", "Challenge: " + v5); if(v5.equals("AUTHENTICATED")) { DeviceControlActivity.this.hackmelockDevice.status = Status.AUTHENTICATED; return; } byte[] v2 = utils.hexStringToByteArray(v5); int v7 = 0; if(DeviceControlActivity.this.hackmelockDevice.isOwn()) { v7 = 0; } else if(DeviceControlActivity.this.dbHelper.getKey(DeviceControlActivity.this.hackmelockDevice.Major, DeviceControlActivity.this.hackmelockDevice.Minor, 1) != null) { v7 = 1; } else { Log.e(DeviceControlActivity.TAG, "No configured key!"); } DeviceControlActivity.this.mBluetoothLeService.queueWriteDataToCharacteristic(DeviceControlActivity.this.hackmelockCommandChar, DeviceControlActivity.this.hackmelockDevice.calculateResponse(v2, v7));

calculateResponse函数为:

public byte[] calculateResponse(byte[] arg22, int arg23) { byte[] v5; byte[] v11 = new byte[17]; new byte[16]; byte[] v10 = new byte[1]; Arrays.fill(v10, ((byte)arg23)); int v17 = 4; try { byte[] v16 = new byte[v17]; Arrays.fill(v16, 0); byte[] v9 = new byte[16]; Log.d("Crypto", "Key[" + arg23 + "]=" + utils.bytesToHex(this.keys[arg23])); System.arraycopy(this.keys[arg23], 0, v9, 0, 12); System.arraycopy(v16, 0, v9, 12, 4); Log.d("Crypto", "KeyFinal " + utils.bytesToHex(v9)); SecretKeySpec v12 = new SecretKeySpec(v9, "AES"); Cipher v4 = Cipher.getInstance("AES"); v4.init(1, ((Key)v12)); byte[] v14 = Arrays.copyOf(v4.doFinal(arg22), 16); Log.d("Auth", "Step 1 - session key : " + utils.bytesToHex(v14)); SecretKeySpec v7 = new SecretKeySpec(v14, "AES"); Cipher v13 = Cipher.getInstance("AES"); v13.init(1, ((Key)v7)); v5 = Arrays.copyOf(v13.doFinal(utils.hexStringToByteArray("DDAAFF1415")), 16); Log.d("Auth", "Step 2 - auth : " + utils.bytesToHex(v5)); } catch(Exception v8) { Log.e("Auth", "Cannot initialize AES! " + v8.getMessage()); } System.arraycopy(v5, 0, v11, 0, 16); System.arraycopy(v10, 0, v11, 16, 1); return v11; }

相关的配置信息是:

public static final String cmdCloseLock = "BB"; public static final String cmdDataTransfer = "01AA021415"; public static final String cmdOpenLock = "AA"; public byte[][] keys; public int own; public static final String passLogin = "DDAAFF1415"; public Status status; public static final String statusAuthenticated = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; public static final String statusConfigMode = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"; static { HackmelockDevice.HACKMELOCK_IBEACON_UUID = UUID.fromString("b-6d33-4c30-634b-e"); HackmelockDevice.HACKMELOCK_SERVICE_UUID = UUID.fromString("b-6d33-4c30-634b-3"); HackmelockDevice.HACKMELOCK_COMMAND_UUID = UUID.fromString("b-6d33-4c30-634b-d44"); HackmelockDevice.HACKMELOCK_STATUS_UUID = UUID.fromString("b-6d33-4c30-634b-4"); HackmelockDevice.HACKMELOCK_DATATRANSFER_UUID = UUID.fromString("b-6d33-4c30-634b-4");

从这个配置信息也能看到开锁和关锁的蓝牙数据。

加密逻辑采用了AES算法:

1. 首先使用key对传递过来的挑战码进行AES加密,生成下一步用的加密key

2. 使用生成的加密key对登入密码DDAAFF1415进行加密,生成认证码

关键是找首次加密的key,在第一次启动的时候会有个唯一的设备号,第一次的私钥应该是和设备号的生成有关。

初始化设备和app,重新使用wireshark抓包 门锁传递密钥.log

可以看到是app给设备端写的密钥,所以应该是app生成的密钥信息,然后写给设备端。

触发这个写密钥的操作为:

app首先请求设备端的handle为0x0013的值,设备返回未空,

也就是BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 后面app向0x000c写一个设备号,然后写密钥信息进去,然后设备AES计算完成配对。

其实设备端留有个后门:

if ( (authResponse === fin_16.toString(hex)) || (authResponse === b6d654c6f636b4d)) { console.log(AUTHENTICATION OK!.green); this.authenticated = true; this.status = statusAuthenticated; }

有个万能密钥:b6d654c6f636b4d

3、ubertooth嗅探

ubertooth是个很强大的蓝牙嗅探工具,安装比较复杂,你要信任docker的话直接用docker启动ubertooth就可以,还是比较方便的。

需要注意的是需要特权模式,将dev目录挂载到docker里面

dockerrun--rm-it--privileged-v/dev:/devmeatmanek/ubertooth:latest

查看版本号:

root@da81bdbaf272:/ubertooth-2015-10-R1/host/kismet/plugin-ubertooth-phyneutral# ubertooth-util -v Firmwarerevision:2015-10-R1

说明docker识别了设备

上面我们知道了模拟门锁的mac地址为:b8:27:eb:90:28:e1,只要使用ubertooth ‑f ‑t b8:27:eb:90:28:e1 跟踪数据,进行嗅探即可。

4、中间人

采用的工具是

搭建测试环境,具体过程不细说了,具体用到了一个虚拟机,可以在`#!vxU0zIwJ!fMotiX3ARWcwNm67IAtn2ymHKldpVJgo43YkVZrtH4A` 下载虚拟机镜像

过程中出现了vbox无法识别usb设备的问题,这个只需要把当前用户加到vbox的组里就行,还有出现一些无法发现 service的问题,具体可以在issus中查找到问题原因。

搭建好了之后:

vbox运行代理:btlejuice_proxy

宿主机运行btlejuice ‑u 代理ip ‑w

打开本地的8080端口,出现web页面

后面直接操作即可,很直观的操作。

下面主要是分析,我们用APP连接设备,看到web上有数据输出,也就是说代理已经截获到了app与设备之间的数据:

根据前面的分析得知read首先去读取的是挑战码,然后APP根据AES计算处认证值写入设备完成匹配,看到第二条数据应该就是认证码,第三条数据是写入开锁的操作。