BLECTF
The purpose of BLE CTF is to teach the core concepts of Bluetooth Low Energy client and server interactions. While it has also been built to be fun, it was built with the intent to teach and reinforce core concepts that are needed to plunge into the world of Bluetooth hacking. After completing this CTF, you should have everything you need to start fiddling with any BLE GATT device you can find.
环境搭建
首先你得有一块 esp32
的板子跟一个支持linux平台的蓝牙适配器或者USB加密狗
安装 esptool
工具,这是一个基于 Python、开源、独立于平台的实用程序,用于与 Espressif 芯片中的 ROM 引导加载程序进行通信。我们得用它来烧录
1 | sudo apt-get install esptool |
构建 ble_ctf
项目,/dev/ttyUSB0
是我们的串口名称
1 | git clone https://github.com/hackgnar/ble_ctf |
相关知识
hcitool
在打开蓝牙设备后,就可以使用 hcitool
工具来对 BLE
设备进行控制
这个项目就需要使用 hcitool
搜索 BLE
设备来找到我们蓝牙设备的 MAC
地址
1 | sudo hcitool lescan |
GATT
通用属性配置文件(GATT) 详细描述了 BLE
设备建立连接后如何传输属性(数据),其中有三个相当重要的概念:**Profile(配置文件)、Service(服务)、Characteristics(特征)
** ,它们可以抽象成以下包含关系:
首先 GATT
主要是负责在两个已经连接的设备交互数据,定义了 BLE
网络堆栈的一般拓扑的 GAP
层把 BLE
设备区分为主机 Master(Central)
和从机 Slave(Perpherial)
。一般我们将 从机
具有的数据或者属性特征,称之为 Profile
(配置文件)
一个或多个 Characteristic
组成一个 Service
,一个或多个 Service
组成 Profile
主机可以发现和获取从机的 Service
和 Characteristic
,然后与之通信。Service
可以理解为一个功能集合,Characteristic
是主从通信的最小单元,是一个抽象功能单元
- 主机可主动向从机
Write
写入或Read
读取数据。 - 从机可主动向主机
Notify
通知数据。
而在 GATT
层则区分为 Server
和 Client
- 客户端(Client):客户端可以发送请求给GATT服务端,读取和写入存储在服务端的特征值
(Characteristics )
- 服务端(Server):该设备包含由
GATT
客户端读取或写入的Characteristic
,每当客户端发送请求时,服务端就会接受并执行相应的请求。
Characteristic
一般包含以下特征:
- UUID(Universally Unique Identifier):用于唯一标识Characteristic。
- Properties(属性):指定了Characteristic的行为,例如读取、写入、通知等。
- Value(值):存储Characteristic的当前值。
- Descriptors(描述符):提供了关于Characteristic的更多信息,如读写权限、单位、格式等。
比如我现在的服务端是我的烧录了 BLECTF
的 esp32
,它由很多 Characteristic
组成
拿着为0x45的 handle
举例,它就相当于某个 Characteristic
的编号
去读取这个0x45的 handle
的 Value/Descriptor
,可以得到以下信息
1a
就是特征属性,代表权限
46
是这个特征的特征值的句柄 ,我们可以通过其去查看这个特征的描述
00 0f ff
是 UUID
的缩略
通过0x46的 handle
去读取 Value/Descriptor
,得到的就是这个特征的描述
通过uuid去读取也是一样的结果
在BLE中,我们有时候会看到 Attributes(属性)
,这是一个更一般的术语,用于表示 特征
中的各种信息。它可以包含 Characteristic
的UUID、Properties、Value、Descriptors等。Attributes用于描述和定义 特征
的各个方面,以便设备之间能够理解和交换数据。
gatttool
hcitool
对于 BLE
设备只能进行连接上的管理,如果需要更精细化的管理,就需要使用 gatttool
1 | GATT commands |
交互模式下的命令
1 | help 显示帮助信息 |
我们这里可以查看一下设备上所有的特征(Characteristic)
1 | gatttool -b 08:B6:1F:B9:59:72 --characteristics |
handle
是特征的句柄,char properties
是特征的属性,char value handle
是特征值的句柄,uuid
是特征的通用唯一标识符
关卡
- 使用
gatttool
查看分数
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n' |
- 使用
gatttool
提交flag获得分数
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x002c -n $(echo -n "some flag value"|xxd -ps) |
Flag1
Flag one is a gift! You can only obtain it by reading this document or peaking at the source code. In short, this flag is to get you familiar with doing a simple write to a BLE handle. Do the following to get your first flag. Make sure you replace the MAC address in the examples below with your devices mac address!
这关主要是让我们熟悉对 BLE
句柄的特征进行简单的写入
首先,查看分数,初始分数为0/20
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n' |
然后,提交向 0x2c
句柄提交flag
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x002c -n $(echo -n "12345678901234567890"|xxd -ps) |
最后,检查我们的分数来查看flag是否被接受,发现分数变成了1/20
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n' |
Flag 0x002e
Check out the ascii value of handle 0x002e and submit it to the flag submision handle 0x002c. If you are using gatttool, make sure you convert it to hex with xxd. If you are using bleah, you can send it as a string value.
读取0x002e句柄的特征,只需要 --char-read
加 -a
指定 0x002e
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x002e |
转为ASCII值为 d205303e099ceff44835
Flag 0x0030
Check out the ascii value of handle 0x0030. Do what it tells you and submit the flag you find to 0x002c.
跟上一题差不多
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0030 |
转为ASCII值为 MD5 of Device Name
之前用hcitool扫描出的结果就是设备名加MAC地址
将设备名BLECTF
进行 MD5
加密后得到值取前20位就是flag
Flag 0x0016
Bluetooth GATT services provide some extra device attributes. Try finding the value of the Generic Access -> Device Name.
首先我们得知道 Generic Access
服务的UUID是预定义为 00001800-0000-1000-8000-00805f9b34fb
使用 --primary
指令来发现所有的 GATT
主服务
1 | gatttool -b 08:B6:1F:B9:59:72 --primary |
从输出可以看到第二个服务的uuid就是 00001800-0000-1000-8000-00805f9b34fb
,所以第二个服务就是 Generic Access
,根据它提供的句柄范围再去查看特征与句柄
1 | gatttool -b 08:B6:1F:B9:59:72 --characteristics --start=0x0014 --end=0x001c |
一般第一个就是我们的设备
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0016 |
转为ASCII值为 2b00042f7481c7b056c4b410d28f33cf
取前20位
后来有师傅告知:
1 | Flag 0x0016 判断依据 => Generic Access uuid(1800) -> Device Name uuid(2a00)。此外 Generic Attribute uuid(1801),Appearance uuid(2a01) |
Flag 0x0032
Read handle 0032 and do what it says. Notice that its not telling you to write to the flag handle as you have been. When you find the flag, go ahead and write it to the flag handle you have used in the past flags.
先读取看看0x0032句柄的特征的值
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0032 |
转ASCII值为 Write anything here
我们只需要往这个句柄里随便写点东西,这个特征的值就会变成flag
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0032 -n $(echo -n "2b00042f7481c7b056c4"|xxd -ps) |
转为ASCII值为 3873c0270763568cf7aa
Flag 0x0034
Follow the instructions found from reading handle 0x0034. Keep in mind that some tools only write hex values while other provide methods for writing either hex or ascii
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0034 |
转为ASCII值为 Write the ascii value "yo" here
转为ASCII值为 c55c6314b3db0a6128af
Flag 0x0036
Follow the instructions found from reading handle 0x0036. Keep in mind that some tools only write hex values while other provide methods for writing either hex or ascii
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0036 |
转为ASCII值为 Write the hex value 0x07 here
我们之前写入的数据的格式都是字符串,现在需要的是整数类型,所以只需要 -n
直接接上对应的值就行了
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0036 -n 07 |
转为ASCII值为 1179080b29f8da16ad66
Flag 0x0038
Follow the instructions found from reading handle 0x0038. Pay attention to handles here. Keep in mind handles can be refrenced by integer or hex. Most tools such as gatttool and bleah allow you to specify handles both ways.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0038 |
转为ASCII值为 Write 0xC9 to handle 58
跟上一题差不多
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 58 -n C9 |
转为ASCII值为 f8b136d937fad6a2be9f
Flag 0x003c
Take a look at handle 0x003c and do what it says. You should script up a solution for this one. Also keep in mind that some tools write faster than others.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x003c |
转为ASCII值为 Brute force my value 00 to ff
就是让我们写个脚本来往这个0x003c句柄的特征里写数据
1 |
|
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x3c |
转为ASCII值为 933c1fcfa8ed52d2ec05
Flag 0x003e
Talke a look at handle 0x003e and do what it says. Keep in mind that some tools have better connection speeds than other for doing reads and writes. This has to do with the functionality the tool provides or how it uses cached BT connections on the host OS. Try testing different tools for this flag. Once you find the fastest one, whip up a script or bash 1 liner to complete the task. FYI, once running, this task takes roughly 90 seconds to complete if done right.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x003e |
转为ASCII值为 Read me 1000 times
比上一个简单,时间间隔调小一点
1 |
|
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x3e |
转为ASCII值为 6ffcd214ffebdc0d069e
Flag 0x0040
Check out handle 0x0040 and google search gatt notify. Some tools like gatttool have the ability to subscribe to gatt notifications
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0040 |
转为ASCII值为 Listen to me for a single notification
,这个是让我们监听该句柄的一次通知
因为主机可主动向从机 Write
写入或 Read
读取数据,从机可主动向主机 Notify
通知数据。
这里的命令分为两步,首先需要 char-write-req -a 0x0040 -n 00
,往该句柄以需要响应的形式写入数据,这样就可以启用通知
然后 --listen
开启监听就能接收到该句柄发送的通知(Notify
)和指示(indicate
)
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0040 -n 00 --listen |
转为ASCII值为 5ec3772bcd00cf06d8eb
Flag 0x0042
Check out handle 0x0042 and google search gatt indicate. For single response indicate messages, like this challenge, tools such as gatttool will work just fine.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0044 |
转为ASCII值为 Listen to handle 0x0044 for a single indication
,让我们监听该句柄的一次指示。
通知和指示在作用上很相似,都是在客户端发送请求后,服务器发相应的数据给客户端。不同的地方在协议层,通知是无应答的发送但是速度快,而指示是有应答机制的,更安全,它需要我们的客户端发送确认(ACK
)来表示确认收到。
所以,其实 gatttool
的操作其实跟上一题一样
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0044 -n 00 --listen |
转为ASCII值为 c7b86dd121848c77c113
Flag 0x0046
Check out handle 0x0046 and do what it says. Keep in mind that this notification clallange requires you to recieve multiple responses in order to complete.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0046 |
转为ASCII值为 Listen to me for multi notifications
,就是接收多个通知,没啥区别,因为我们的监听功能是持续的,能一直接收
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0046 -n 00 --listen |
转为ASCII值为 c9457de5fd8cafe349fd
与 U no want this msg
Flag 0x0048
Check out handle 0x0042 and google search gatt indicate. Keep in mind that this chalange will require you to parse multiple indicate responses in order to complete the chalange.
这里题目写错了,把0x0048写成了0x0042
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0048 |
转为ASCII值为 Listen to handle 0x004a for multi indications
,上题同理
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x004a -n 00 --listen |
转为ASCII值为 b6f3a47f207d38e16ffa
Flag 0x004c
Check out handle 0x004c and do what it says. Much like ethernet or wifi devices, you can also change your bluetooth devices mac address.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x004c |
转为ASCII值为 Connect with BT MAC address 11:22:33:44:55:66
这个的意思其实就是让我们将我们蓝牙适配器的MAC地址改成 11:22:33:44:55:66
这里需要一个 bdaddr 工具,根据这个项目的 README
,是支持我这个 CSR4.0
的蓝牙适配器的。但是如果你去看看这个项目 issue
,可以发现修改 CSR
的设备的 MAC
地址时会有问题,就是显示修改成功,但是实际 MAC
地址还是原来的那个,我自己在修改时也发现这个问题。不过后来,把蓝牙适配器拔了再插上去后去查看,发现 MAC
地址成功改变了!
1 | sudo ./bdaddr -i hci0 11:22:33:44:55:66 |
转为ASCII值为 aca16920583e42bdcf5f
Flag 0x004e
Read handle 0x0048 and do what it says. Setting MTU can be a tricky thing. Some tools may provide mtu flags, but they dont seem to really trigger MTU negotiations on servers. Try using gatttool’s interactive mode for this task. By default, the BLECTF server is set to force an MTU size of 20. The server will listen for MTU negotiations, and look at them, but we dont really change the MTU in the code. We just trigger the flag code if you trigger an MTU event with the value specified in handle 0x0048. GLHF!
这里题目写错了,把0x004e写成了0x0048
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x004e |
转为ASCII值为 Set your connection MTU to 444
直接使用 -m
来设置有些问题,不过可以进入交互模式来设置
1 | gatttool -b 08:B6:1F:B9:59:72 -I |
转为ASCII值为 b1e409e5a4eaf9fe5158
Flag 0x0050
Check out handle 0x0050 and do what it says. This chalange differs from other write chalanges as your tool that does the write needs to have write response ack messages implemente correctly. This flag is also tricky as the flag will come back as notification response data even though there is no “NOTIFY” property.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0050 |
转为ASCII值为 Write+resp 'hello'
,只需要注意只能使用--char-write-req
,而不能使用 --char-write
就行了
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0050 -n $(echo -n "hello"|xxd -ps) |
转为ASCII值为 d41d8cd98f00b204e980
Flag 0x0052
Take a look at handle 0x0052. Notice it does not have a notify property. Do a write here and listen for notifications anyways! Things are not always what they seem!
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0052 |
转为ASCII值为 No notifications here! really?
这题的意思应该是,没有允许通知属性的句柄,也可能拥有通知的能力
1 | gatttool -b 08:B6:1F:B9:59:72 --characteristics |
根据常见的特征属性及其对应的位标志:
- 0x01:允许广播
- 0x02:允许读取
- 0x04:允许写入(无响应)
- 0x08:允许写入(带响应)
- 0x10:允许通知(无确认)
- 0x20:允许通知(带确认)
- 0x40:允许经过身份签名的写入
- 0x80:运行扩展属性
我们 0x0052
句柄值对应属性值为 0x0a
,所以它应该只有写入和读取权限,但是我们主动向其写入输入来启用通知,从而监听服务端的通知
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0052 -n 00 --listen |
转为ASCII值为 fc920c68b6006169477b
Flag 0x0054
Check out all of the handle properties on 0x0054! Poke around with all of them and find pieces to your flag.
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0054 |
转为ASCII值为 So many properties!
,我们可以看到该句柄属性值为0x9b,其对应权限为广播、读取、带响应写入、通知、扩展
也就是说,我们可以写入,也可以监听。根据之前解题的思路,flag应该由此而来(什么MISC)
1 | gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0054 -n 00 |
写入跟监听分别得到部分flag,转为ASCII值为 fbb966958f07e4a0cc48
Flag 0x0056
Figure out the authors twitter handle and do what 0x0056 tells you to do!
1 | gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0056 |
转为ASCII值为 md5 of author's twitter handle
作者的推特名在项目那里有:@hackgnar
将其md5加密后取前20位得到 d953bfb9846acc2e15ee
参考资料:
https://bbs.kanxue.com/thread-271543.htm
http://doc.iotxx.com/index.php?title=BLE技术揭秘#
https://leconiot.com/download/cc2640r2f/ble_stack_app/stack/gatt/gatt.html