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
2
3
git clone https://github.com/hackgnar/ble_ctf
cd ble_ctf
esptool -p /dev/ttyUSB0 -b 460800 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode dio --flash_size 2MB --flash_freq 40m 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/ble_ctf.bin

相关知识

hcitool

在打开蓝牙设备后,就可以使用 hcitool 工具来对 BLE 设备进行控制

这个项目就需要使用 hcitool 搜索 BLE 设备来找到我们蓝牙设备的 MAC 地址

1
sudo hcitool lescan

Untitled

GATT

通用属性配置文件(GATT) 详细描述了 BLE 设备建立连接后如何传输属性(数据),其中有三个相当重要的概念:**Profile(配置文件)、Service(服务)、Characteristics(特征)** ,它们可以抽象成以下包含关系:

Untitled

首先 GATT 主要是负责在两个已经连接的设备交互数据,定义了 BLE 网络堆栈的一般拓扑的 GAP 层把 BLE 设备区分为主机 Master(Central) 和从机 Slave(Perpherial) 。一般我们将 从机 具有的数据或者属性特征,称之为 Profile(配置文件)

一个或多个 Characteristic 组成一个 Service,一个或多个 Service 组成 Profile

主机可以发现和获取从机的 ServiceCharacteristic,然后与之通信。Service 可以理解为一个功能集合,Characteristic 是主从通信的最小单元,是一个抽象功能单元

  • 主机可主动向从机 Write 写入或 Read 读取数据。
  • 从机可主动向主机 Notify 通知数据。

而在 GATT 层则区分为 ServerClient

  • 客户端(Client):客户端可以发送请求给GATT服务端,读取和写入存储在服务端的特征值 (Characteristics )
  • 服务端(Server):该设备包含由 GATT 客户端读取或写入的 Characteristic,每当客户端发送请求时,服务端就会接受并执行相应的请求。

Characteristic 一般包含以下特征:

  • UUID(Universally Unique Identifier):用于唯一标识Characteristic。
  • Properties(属性):指定了Characteristic的行为,例如读取、写入、通知等。
  • Value(值):存储Characteristic的当前值。
  • Descriptors(描述符):提供了关于Characteristic的更多信息,如读写权限、单位、格式等。

比如我现在的服务端是我的烧录了 BLECTFesp32 ,它由很多 Characteristic 组成

Untitled

拿着为0x45的 handle 举例,它就相当于某个 Characteristic 的编号

去读取这个0x45的 handleValue/Descriptor ,可以得到以下信息

Untitled

1a 就是特征属性,代表权限

46 是这个特征的特征值的句柄 ,我们可以通过其去查看这个特征的描述

00 0f ffUUID 的缩略

通过0x46的 handle 去读取 Value/Descriptor ,得到的就是这个特征的描述

Untitled

通过uuid去读取也是一样的结果

Untitled

在BLE中,我们有时候会看到 Attributes(属性) ,这是一个更一般的术语,用于表示 特征 中的各种信息。它可以包含 Characteristic 的UUID、Properties、Value、Descriptors等。Attributes用于描述和定义 特征 的各个方面,以便设备之间能够理解和交换数据。

gatttool

hcitool 对于 BLE 设备只能进行连接上的管理,如果需要更精细化的管理,就需要使用 gatttool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
GATT commands
--primary 用于进行主服务发现,查找蓝牙设备上的主服务
--characteristics 用于进行特征发现,查找指定主服务下的特征
--char-read 用于写入特征的值,需要指定一个句柄
--char-write 用于写入特征的值,不需要响应
--char-write-req 用于写入特征的值,需要响应
--char-desc 于进行特征描述符发现,查找指定特征下的描述符
--listen 监听特征的通知和指示

Primary Services/Characteristics arguments
-s, --start=0x0001 用于指定起始句柄(可选)
-e, --end=0xffff 用于指定结束句柄(可选)
-u, --uuid=0x1801 用于指定16bit或者128bit的UUID(可选)

Characteristics Value/Descriptor Read/Write arguments
-a, --handle=0x0001 用于指定要读取或写入的特征或描述符的句柄
-n, --value=0x0001 用于指定要写入的特征值

Application Options:
-i, --adapter=hciX 用于指定本地适配器接口,如hci0
-b, --device=MAC 用于指定远程蓝牙设备的MAC地址
-t, --addr-type=[public | random] 用于设置LE地址类型,可以选择公共地址还是随机地址,默认公共地址
-m, --mtu=MTU 用于指定ATT协议MTU大小
-p, --psm=PSM 用于指定GATT/ATT over BR/EDR的PSM
-l, --sec-level=[low | medium | high] 用于设置安全级别,可以选择低、中、高,默认低安全级别
-I, --interactive 用于启用交互模式

交互模式下的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
help                                           显示帮助信息
exit 退出交互模式
quit 退出交互模式
connect [address [address type]] 连接到远程设备
disconnect 断开与远程设备的连接
primary [UUID] 发现主要服务
included [start hnd [end hnd]] 查找包含的服务
characteristics [start hnd [end hnd [UUID]]] 发现特征
char-desc [start hnd] [end hnd] 发现特征描述符
char-read-hnd <handle> 通过句柄读取特征值/描述符
char-read-uuid <UUID> [start hnd] [end hnd] 通过UUID读取特征值/描述符
char-write-req <handle> <new value> 写入特征值(写请求)
char-write-cmd <handle> <new value> 写入特征值(无响应)
sec-level [low | medium | high] 设置安全级别,默认为低安全级别
mtu <value> 设置交换GATT/ATT的MTU(最大传输单元)

我们这里可以查看一下设备上所有的特征(Characteristic)

1
gatttool -b 08:B6:1F:B9:59:72 --characteristics

Untitled

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'

Untitled

  • 使用 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'

Untitled

然后,提交向 0x2c 句柄提交flag

1
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x002c -n $(echo -n "12345678901234567890"|xxd -ps)

Untitled

最后,检查我们的分数来查看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'

Untitled

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

Untitled

转为ASCII值为 d205303e099ceff44835

Untitled

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

Untitled

转为ASCII值为 MD5 of Device Name

之前用hcitool扫描出的结果就是设备名加MAC地址

Untitled

将设备名BLECTF 进行 MD5 加密后得到值取前20位就是flag

Untitled

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

Untitled

从输出可以看到第二个服务的uuid就是 00001800-0000-1000-8000-00805f9b34fb ,所以第二个服务就是 Generic Access ,根据它提供的句柄范围再去查看特征与句柄

1
gatttool -b 08:B6:1F:B9:59:72 --characteristics --start=0x0014 --end=0x001c

Untitled

一般第一个就是我们的设备

1
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0016

Untitled

转为ASCII值为 2b00042f7481c7b056c4b410d28f33cf 取前20位

Untitled

后来有师傅告知:

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

Untitled

转ASCII值为 Write anything here

我们只需要往这个句柄里随便写点东西,这个特征的值就会变成flag

1
2
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0032 -n $(echo -n "2b00042f7481c7b056c4"|xxd -ps)
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0032

Untitled

转为ASCII值为 3873c0270763568cf7aa

Untitled

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

Untitled

转为ASCII值为 Write the ascii value "yo" here

Untitled

转为ASCII值为 c55c6314b3db0a6128af

Untitled

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

Untitled

转为ASCII值为 Write the hex value 0x07 here

我们之前写入的数据的格式都是字符串,现在需要的是整数类型,所以只需要 -n 直接接上对应的值就行了

1
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0036 -n 07

Untitled

转为ASCII值为 1179080b29f8da16ad66

Untitled

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

Untitled

转为ASCII值为 Write 0xC9 to handle 58

跟上一题差不多

1
2
3
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 58 -n C9
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 58
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x38

Untitled

转为ASCII值为 f8b136d937fad6a2be9f

Untitled

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

Untitled

转为ASCII值为 Brute force my value 00 to ff

就是让我们写个脚本来往这个0x003c句柄的特征里写数据

1
2
3
4
5
6
7
#!/bin/zsh

for value in $(seq 0 255); do
hex_value=$(printf "%02X" $value)
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x003c -n $hex_value
sleep 0.5
done
1
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x3c

Untitled

转为ASCII值为 933c1fcfa8ed52d2ec05

Untitled

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

Untitled

转为ASCII值为 Read me 1000 times

比上一个简单,时间间隔调小一点

1
2
3
4
5
6
#!/bin/zsh

for ((i=1; i<=1000; i++)); do
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x3e
sleep 0.1
done
1
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x3e

Untitled

转为ASCII值为 6ffcd214ffebdc0d069e

Untitled

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

Untitled

转为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

Untitled

转为ASCII值为 5ec3772bcd00cf06d8eb

Untitled

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

Untitled

转为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

Untitled

转为ASCII值为 c7b86dd121848c77c113

Untitled

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

Untitled

转为ASCII值为 Listen to me for multi notifications ,就是接收多个通知,没啥区别,因为我们的监听功能是持续的,能一直接收

1
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0046 -n 00 --listen

Untitled

转为ASCII值为 c9457de5fd8cafe349fdU no want this msg

Untitled

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

Untitled

转为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

Untitled

转为ASCII值为 b6f3a47f207d38e16ffa

Untitled

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

Untitled

转为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

Untitled

转为ASCII值为 aca16920583e42bdcf5f

Untitled

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

Untitled

转为ASCII值为 Set your connection MTU to 444

直接使用 -m 来设置有些问题,不过可以进入交互模式来设置

1
2
3
gatttool -b 08:B6:1F:B9:59:72 -I
connect
mtu 444

Untitled

Untitled

转为ASCII值为 b1e409e5a4eaf9fe5158

Untitled

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

Untitled

转为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)

Untitled

转为ASCII值为 d41d8cd98f00b204e980

Untitled

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

Untitled

转为ASCII值为 No notifications here! really?

这题的意思应该是,没有允许通知属性的句柄,也可能拥有通知的能力

1
gatttool -b 08:B6:1F:B9:59:72 --characteristics

Untitled

根据常见的特征属性及其对应的位标志:

  • 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

Untitled

转为ASCII值为 fc920c68b6006169477b

Untitled

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

Untitled

转为ASCII值为 So many properties! ,我们可以看到该句柄属性值为0x9b,其对应权限为广播、读取、带响应写入、通知、扩展

Untitled

也就是说,我们可以写入,也可以监听。根据之前解题的思路,flag应该由此而来(什么MISC)

1
2
3
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0054 -n 00
gatttool -b 08:B6:1F:B9:59:72 --char-read -a 0x0054
gatttool -b 08:B6:1F:B9:59:72 --char-write-req -a 0x0054 -n 00 --listen

Untitled

写入跟监听分别得到部分flag,转为ASCII值为 fbb966958f07e4a0cc48

Untitled

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

Untitled

转为ASCII值为 md5 of author's twitter handle

作者的推特名在项目那里有:@hackgnar

Untitled

将其md5加密后取前20位得到 d953bfb9846acc2e15ee

Untitled

参考资料:

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

[https://www.cnblogs.com/iini/p/12334646.html](