blectf

通过blectf简单认识与ble的交互

基础知识

通用属性配置文件(GATT) 是 BLE(蓝牙低功耗)设备在建立连接后用于数据交换的协议框架。它定义了设备间如何通过一系列层次化的属性进行通信。GATT 结构中有三个核心概念:

  • Profile(配置文件)
  • Service(服务)
  • Characteristic(特征)

它们的关系可以类比为嵌套结构:

一个 Profile 包含多个 Service
每个 Service 又包含一个或多个 Characteristic


在 BLE 的通信中,设备分为两类:

  • 主机(Central/Master):发起连接的一方
  • 从机(Peripheral/Slave):被连接的一方,通常持有具体的数据和功能

从机通常通过 Profile 来定义其提供的服务和功能。


在 GATT 层中,通信角色被进一步细化为:

  • Server(服务端):保存数据(如 Characteristic),响应客户端请求
  • Client(客户端):发起读取或写入请求,与服务端交互数据

Characteristic(特征)的组成:

  • UUID:唯一标识
  • Properties:定义行为(如读、写、通知)
  • Value:当前值
  • Descriptors:附加说明,如格式、权限等

通信方式:

  • Client 向 Server 发送请求:读取(Read)或写入(Write)数据
  • Server 主动推送数据给 Client:通过通知(Notify)

此外,还有一个更通用的术语 Attribute(属性),用于描述 BLE 中的一切数据单元。它可以包含 UUID、属性、值、描述符等,是构成 Characteristic 的基础元素,也是 GATT 传输的核心单位。

环境搭建

道具准备

  1. esp32开发板一片
  2. USB蓝牙适配器一个, 我用的是绿联CM390(如果电脑自带的蓝牙适配器可以兼容linux可以不需要)

环境安装

首先安装esptool用于烧录开发板

1
sudo apt-get install esptool

接着克隆ble_ctf仓库并烧录

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 16MB --flash_freq 40m 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/ble_ctf.bin

flash_size可以根据自己的板子修改, 但不改问题也不大

等待烧录完成, 此时打开蓝牙可以扫描到一个BLECTF

常用工具

gatttool

命令行模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Usage:
gatttool [OPTION?]

Help Options:
-h, --help Show help options
--help-all Show all help options
--help-gatt Show all GATT commands
--help-params Show all Primary Services/Characteristics arguments
--help-char-read-write Show all Characteristics Value/Descriptor Read/Write arguments

Application Options:
-i, --adapter=hciX Specify local adapter interface
-b, --device=MAC Specify remote Bluetooth address
-t, --addr-type=[public | random] Set LE address type. Default: public
-m, --mtu=MTU Specify the MTU size
-p, --psm=PSM Specify the PSM for GATT/ATT over BR/EDR
-l, --sec-level=[low | medium | high] Set security level. Default: low
-I, --interactive Use interactive mode

交互模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
help                                           Show this help
exit Exit interactive mode
quit Exit interactive mode
connect [address [address type]] Connect to a remote device
disconnect Disconnect from a remote device
primary [UUID] Primary Service Discovery
included [start hnd [end hnd]] Find Included Services
characteristics [start hnd [end hnd [UUID]]] Characteristics Discovery
char-desc [start hnd] [end hnd] Characteristics Descriptor Discovery
char-read-hnd <handle> Characteristics Value/Descriptor Read by handle
char-read-uuid <UUID> [start hnd] [end hnd] Characteristics Value/Descriptor Read by UUID
char-write-req <handle> <new value> Characteristic Value Write (Write Request)
char-write-cmd <handle> <new value> Characteristic Value Write (No response)
sec-level [low | medium | high] Set security level. Default: low
mtu <value> Exchange MTU for GATT/ATT

bluetoothctl

虽然都说这个工具更好 但感觉使用体验上不如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
27
28
29
30
31
32
33
34
35
36
37
enu main:
Available commands:
-------------------
advertise Advertise Options Submenu
scan Scan Options Submenu
gatt Generic Attribute Submenu
list List available controllers
show [ctrl] Controller information
select <ctrl> Select default controller
devices List available devices
paired-devices List paired devices
system-alias <name> Set controller alias
reset-alias Reset controller alias
power <on/off> Set controller power
pairable <on/off> Set controller pairable mode
discoverable <on/off> Set controller discoverable mode
discoverable-timeout [value] Set discoverable timeout
agent <on/off/capability> Enable/disable agent with given capability
default-agent Set agent as the default one
advertise <on/off/type> Enable/disable advertising with given type
set-alias <alias> Set device alias
scan <on/off> Scan for devices
info [dev] Device information
pair [dev] Pair with device
trust [dev] Trust device
untrust [dev] Untrust device
block [dev] Block device
unblock [dev] Unblock device
remove <dev> Remove device
connect <dev> Connect device
disconnect [dev] Disconnect device
menu <name> Select submenu
version Display version
quit Quit program
exit Quit program
help Display help about this program
export Print environment variables

BLE_CTF

阅读Readme

查看分数

1
gatttool -b de:ad:be:ef:be:f1 --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n' 

提交flag

1
gatttool -b de:ad:be:ef:be:f1 --char-write-req -a 0x002c -n $(echo -n "some flag value"|xxd -ps)

作者还给出了共20个关卡的知识点所在

Flag Description
Flag 1 This flag is a gift and can only be obtained from reading the hint!
Flag 0x002e Learn how to read handles
Flag 0x0030 Read handle puzzle fun
Flag 0x0016 Learn about discoverable device attributes
Flag 0x0032 Learn about reading and writing to handles
Flag 0x0034 Learn about reading and writing ascii to handles
Flag 0x0036 Learn about reading and writing hex to handles
Flag 0x0038 Learn about reading and writing to handles differently
Flag 0x003c Learn about write fuzzing
Flag 0x003e Learn about read and write speeds
Flag 0x0040 Learn about single response notifications
Flag 0x0042 Learn about single response indicate
Flag 0x0046 Learn about multi response notifications
Flag 0x0048 Learn about multi response indicate
Flag 0x004c Learn about BT client device attributes
Flag 0x004e Learn about message sizes MTU
Flag 0x0050 Learn about write responses
Flag 0x0052 Hidden notify property
Flag 0x0054 Use multiple handle properties
Flag 0x0056 OSINT the author!

0x1

flag1直接在readme中给我们了, 送分, 用于了解如何提交flag

1
2
3
4
$gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x002c -n $(echo -n "12345678901234567890"|xxd -ps)
Characteristic value was written successfully
$gatttool -b 14:2B:2F:C5:8A:56 --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:1 /20

0x2

Learn how to read handles

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.

1
2
$gatttool -b 14:2B:2F:C5:8A:56 --char-read -a 0x002e
Characteristic value/descriptor: 64 32 30 35 33 30 33 65 30 39 39 63 65 66 66 34 34 38 33 35

转为ascii也就是d205303e099ceff44835

提交

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x002c -n $(echo -n "d205303e099ceff44835"|xxd -ps)

0x3

Read handle puzzle fun

Check out the ascii value of handle 0x0030. Do what it tells you and submit the flag you find to 0x002c.

还是先读

1
gatttool -b 14:2B:2F:C5:8A:56 --char-read -a 0x0030

得到结果转字符串MD5 of Device Name

Device Name也就是BLECTF了

提交

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x002c -n $(echo -n "5cd56d74049ae40f442e"|xxd -ps)

0x4

Learn about discoverable device attributes

Bluetooth GATT services provide some extra device attributes. Try finding the value of the Generic Access -> Device Name.

查看Generic Access 的Device Name

首先查看主要服务

1
2
3
4
$gatttool -b 14:2B:2F:C5:8A:56 --primary
attr handle = 0x0001, end grp handle = 0x0005 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle = 0x0014, end grp handle = 0x001c uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle = 0x0028, end grp handle = 0xffff uuid: 000000ff-0000-1000-8000-00805f9b34fb

蓝牙标准规定的某些特殊服务或者特性存在着固定的UUID

00001800-0000-1000-8000-00805f9b34fb就是Generic Access的固定UUID, 这个服务包含的特性是0x14 到 0x1c 范围

那么对该范围进行扫描

1
2
3
4
$gatttool -b 14:2B:2F:C5:8A:56 --characteristics  --start=0x0014 --end=0x001c
handle = 0x0015, char properties = 0x02, char value handle = 0x0016, uuid = 00002a00-0000-1000-8000-00805f9b34fb
handle = 0x0017, char properties = 0x02, char value handle = 0x0018, uuid = 00002a01-0000-1000-8000-00805f9b34fb
handle = 0x0019, char properties = 0x02, char value handle = 0x001a, uuid = 00002aa6-0000-1000-8000-00805f9b34fb

其中短uuid 2A00就是Device Name(这也是固定的)

也就是handle 0x0015, 但0x0015只是特征值声明, 我们需要读取其特征值数据handle也就是0x0016

读取到后取结果20位即可

0x5

Learn about reading and writing to handles

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.

让我们先读0032得到结果是Write anything here

那我们随便写一点东西

1
2
3
4
$gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0032 -n $(echo -n "2b00042f7481c7b056c4"|xxd -ps)

$gatttool -b 14:2B:2F:C5:8A:56 --char-read -a 0x0032
Characteristic value/descriptor: 33 38 37 33 63 30 32 37 30 37 36 33 35 36 38 63 66 37 61 61

写完以后再次读取就变成了flag

0x6

Learn about reading and writing ascii to handles

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

先读0x0034得到字符串Write the ascii value "yo" here

直接写就可以了

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0034 -n $(echo -n "yo"|xxd -ps)

写完后再读变为flag

0x7

Learn about reading and writing hex to handles

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

读取0x0036得到Write the hex value 0x07 here

直接写

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0036 -n 07

0x8

Learn about reading and writing to handles differently

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.

读取0x0038得到Write 0xC9 to handle 58

还是直接写

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 58 -n c9

0x9

Learn about write fuzzing

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.

读取0x003c得到Brute force my value 00 to ff

即暴力破解一个8比特位的数到0x003c

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
#!/bin/sh

# 蓝牙设备MAC地址
DEVICE="14:2B:2F:C5:8A:56"
# 特征值句柄
HANDLE="0x3c"

# 使用POSIX兼容的循环语法
i=0
while [ $i -le 255 ]; do
# 将十进制转换为两位十六进制
hex_value=$(printf "%02x" $i)

echo "正在发送: $hex_value"

# 执行gatttool命令
gatttool -b $DEVICE --char-write-req -a $HANDLE -n $hex_value

# 可选:添加延迟
sleep 0.1

i=$((i+1))
done

echo "所有单字节值(0x00-0xFF)已发送完毕"

执行后再次读取即可

0xa

Learn about read and write speeds

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.

读取0x003e得到Read me 1000 times

读取1000次即可

1
2
3
4
5
6
7
8
9
#!/bin/sh

DEVICE="14:2B:2F:C5:8A:56"
HANDLE="0x003e"

for i in $(seq 1 1000); do
gatttool -b $DEVICE --char-read -a $HANDLE
sleep 0.01
done

最后就可以读出flag

0xb

Learn about single response notifications

Check out handle 0x0040 and google search gatt notify. Some tools like gatttool have the ability to subscribe to gatt notifications

读取得到Listen to me for a single notification

客户端可以向服务端请求通知一项特征值,当该特征可用时服务端会通知客户端,通知(notification)不需要客户端回应 ACK

使用命令发起请求监听

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0040 -n 00 --listen

00可以是任意值没什么要求

0xc

Learn about single response indicate

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.

读取得到Listen to handle 0x0044 for a single indication

指示与通知差不多但区别在于指示需要我们回应

单指令其实和上一题没有区别

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0044 -n 11 --listen

0xd

Learn about multi response notifications

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.

读取得到Listen to me for multi notifications

还是和上一题一样的的指令, 不过要多等一下

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0046 -n 11 --listen

0xe

Learn about multi response indicate

Check out handle 0x0048 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.

读取得到Listen to handle 0x004a for multi indications

还是一样的命令

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x004a -n 11 --listen

0xf

Learn about BT client device attributes

Check out handle 0x004c and do what it says. Much like ethernet or wifi devices, you can also change your bluetooth devices mac address.

读取得到Connect with BT MAC address 11:22:33:44:55:66

也就是要修改设备的MAC地址

可以使用工具thxomas/bdaddr: Updated version of the bluez bdaddr tool

0x10

Learn about message sizes MTU

Read handle 0x004e 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 0x004e. GLHF!

读取得到Set your connection MTU to 444

MTU是蓝牙单次数据传输能承载的最大字节数

可以直接通过命令行参数设置, 不过存在一些问题, 得使用交互模式

0x11

Learn about write responses

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.

读取得到Write+resp 'hello'

对于--char-write--char-write-req差别就在于后者必须接受到目标的回复, 有更高的可靠性

所以这里直接写就可以了

1
gatttool -b 14:2B:2F:C5:8A:56 --char-write-req -a 0x0050 -n $(echo -n "hello"|xxd -ps)

0x12

Hidden notify property

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!

读取得到No notifications here! really?

根据特征属性标志位

  • 0x01:允许广播
  • 0x02:允许读取
  • 0x04:允许写入(无响应)
  • 0x08:允许写入(带响应)
  • 0x10:允许通知(无确认)
  • 0x20:允许通知(带确认)
  • 0x40:允许经过身份签名的写入
  • 0x80:运行扩展属性
1
handle = 0x0051, char properties = 0x0a, char value handle = 0x0052, uuid = 0000ff15-0000-1000-8000-00805f9b34fb

0x0a属性本不应该有通知权限, 但因为源码虽然仍然使用了esp_ble_gatts_send_indicate发送通知, 所以依然可以接收到通知

0x13

Use multiple handle properties

Check out all of the handle properties on 0x0054! Poke around with all of them and find pieces to your flag.

读取得到So many properties!

我们可以看到该句柄具有很多权限

1
handle = 0x0053, char properties = 0x9b, char value handle = 0x0054, uuid = 0000ff16-0000-1000-8000-00805f9b34fb

可以写入也可以监听, 所以只需要写入一次读取再监听一次

即可得到完整的flag

0x14

md5 of author’s twitter handle