ble_ctf wp
arch3rn4r

0x0 前期配置

所需设备:蓝牙适配器,刷入了blectf的esp32,能进行蓝牙操作的设备(kali或者Ubuntu)
将blectf靶场刷入esp32
安装esptool(如果出现报错那么去查看“安装时报错”那条)

1
sudo apt-get install esptool

克隆仓库

1
git clone https://github.com/hackgnar/ble_ctf

找到当前esp32在linux的端口,一般是/dev/ttyUSB0

1
2
3
4
#查看当前所有串口设备
/dev/tty*
#查看usb口设备
/dev/ttyUSB*

开始烧录

1
2
3
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

一切正常没有报错的话那么就搭建完了


启动蓝牙

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
┌──(kali㉿kali)-[~/Desktop]
└─$ hciconfig

┌──(kali㉿kali)-[~/Desktop]
└─$ hciconfig
hci0: Type: Primary Bus: USB
BD Address: 84:E0:F4:03:0F:5E ACL MTU: 310:10 SCO MTU: 64:8
DOWN RUNNING
RX bytes:582 acl:0 sco:0 events:30 errors:0
TX bytes:367 acl:0 sco:0 commands:30 errors:0
┌──(root㉿kali)-[/home/kali/Desktop]
└─# hciconfig hci0 up
┌──(root㉿kali)-[/home/kali/Desktop]
└─# exit

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo hciconfig hci0 lestates
Supported link layer states:
YES Non-connectable Advertising State
YES Scannable Advertising State
YES Connectable Advertising State
YES Directed Advertising State
YES Passive Scanning State
YES Active Scanning State
YES Initiating State/Connection State in Central Role
YES Connection State in the Peripheral Role
YES Non-connectable Advertising State and Passive Scanning State combination
YES Scannable Advertising State and Passive Scanning State combination
YES Connectable Advertising State and Passive Scanning State combination
YES Directed Advertising State and Passive Scanning State combination
YES Non-connectable Advertising State and Active Scanning State combination
YES Scannable Advertising State and Active Scanning State combination
YES Connectable Advertising State and Active Scanning State combination
YES Directed Advertising State and Active Scanning State combination
YES Non-connectable Advertising State and Initiating State combination
YES Scannable Advertising State and Initiating State combination
YES Non-connectable Advertising State and Central Role combination
YES Scannable Advertising State and Central Role combination
YES Non-connectable Advertising State and Peripheral Role combination
YES Scannable Advertising State and Peripheral Role combination
YES Passive Scanning State and Initiating State combination
YES Active Scanning State and Initiating State combination
YES Passive Scanning State and Central Role combination
YES Active Scanning State and Central Role combination
YES Passive Scanning State and Peripheral Role combination
YES Active Scanning State and Peripheral Role combination
YES Initiating State and Central Role combination/Central Role and Central Role combination

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo hcitool lescan
LE Scan ...
64:B7:08:61:B9:7E BLECTF



这里会用到两种工具,gatttool和bleah,kali自带gatttool
查看gatttool的help来学会其用法
在这些指令里查找我们需要的参数

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
38
└─$ gatttool --help-all            
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

GATT commands
--primary Primary Service Discovery
--characteristics Characteristics Discovery
--char-read Characteristics Value/Descriptor Read
--char-write Characteristics Value Write Without Response (Write Command)
--char-write-req Characteristics Value Write (Write Request)
--char-desc Characteristics Descriptor Discovery
--listen Listen for notifications and indications

Primary Services/Characteristics arguments
-s, --start=0x0001 Starting handle (optional)
-e, --end=0xffff Ending handle (optional)
-u, --uuid=0x1801 UUID16 or UUID128 (optional)

Characteristics Value/Descriptor Read/Write arguments
-a, --handle=0x0001 Read/Write characteristic by handle (required)
-n, --value=0x0001 Write characteristic value (required for write operation)

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


关于这个挑战的源代码在main/gatts_table_creat_demo.c文件里
服务器通过内部变量(flag_state数组、score计数器)跟踪每个挑战是否完成
这是题目们对应的标志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static const uint16_t GATTS_SERVICE_UUID_TEST                   = 0x00FF;
static const uint16_t GATTS_CHAR_UUID_SCORE = 0xFF01;
static const uint16_t GATTS_CHAR_UUID_FLAG = 0xFF02;
static const uint16_t GATTS_CHAR_UUID_FLAG_SIMPLE_READ = 0xFF03;
static const uint16_t GATTS_CHAR_UUID_FLAG_MD5 = 0xFF04;
static const uint16_t GATTS_CHAR_UUID_FLAG_WRITE_ANYTHING = 0xFF05;
static const uint16_t GATTS_CHAR_UUID_FLAG_WRITE_ASCII = 0xFF06;
static const uint16_t GATTS_CHAR_UUID_FLAG_WRITE_HEX = 0xFF07;
static const uint16_t GATTS_CHAR_UUID_FLAG_SIMPLE_WRITE2_READ = 0xFF08;
static const uint16_t GATTS_CHAR_UUID_FLAG_SIMPLE_WRITE2 = 0xFF09;
static const uint16_t GATTS_CHAR_UUID_FLAG_BRUTE_WRITE = 0xFF0a;
static const uint16_t GATTS_CHAR_UUID_FLAG_READ_ALOT = 0xFF0b;
static const uint16_t GATTS_CHAR_UUID_FLAG_NOTIFICATION = 0xFF0c;
static const uint16_t GATTS_CHAR_UUID_FLAG_INDICATE_READ = 0xFF0d;
static const uint16_t GATTS_CHAR_UUID_FLAG_INDICATE = 0xFF0e;
static const uint16_t GATTS_CHAR_UUID_FLAG_NOTIFICATION_MULTI = 0xFF0f;
static const uint16_t GATTS_CHAR_UUID_FLAG_INDICATE_MULTI_READ = 0xFF10;
static const uint16_t GATTS_CHAR_UUID_FLAG_INDICATE_MULTI = 0xFF11;
static const uint16_t GATTS_CHAR_UUID_FLAG_MAC = 0xFF12;
static const uint16_t GATTS_CHAR_UUID_FLAG_MTU = 0xFF13;
static const uint16_t GATTS_CHAR_UUID_FLAG_WRITE_RESPONSE = 0xFF14;
static const uint16_t GATTS_CHAR_UUID_FLAG_HIDDEN_NOTIFY = 0xFF15;
static const uint16_t GATTS_CHAR_UUID_FLAG_CRAZY = 0xFF16;
static const uint16_t GATTS_CHAR_UUID_FLAG_TWITTER = 0xFF17;

0x1

基础验证
这里是基本的句柄写入,也可以用来测试当前环境是否可用,输完第一句后esp32会出现一个蓝色的灯代表连接好了
并且在这里说一下提交的要求:
默认情况下,读取前20个字节(如果您使用的是 gatttool,请确保使用 xxd 将其转换为十六进制。如果你使用的是 bleah,你可以将其作为字符串值发送)并将其写入句柄0x002c

1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score: 0/20

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "12345678901234567890"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:1 /20

解析指令
这是主要部分

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a
  • -b指定目标蓝牙
  • –char-read 指定要执行的操作
  • -a 指定句柄
    后面的部分是对数据的处理,过滤我们所需的信息
    1
    |awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

0x2 查看及提交句柄

%% 查看 handle 的 ascii 值 0x002e 并将其提交给 flag submision handle 0x002c。如果您使用的是 gatttool,请确保使用 xxd 将其转换为十六进制。如果你使用的是 bleah,你可以将其作为字符串值发送 %%

进行拆解
现在需要读取目标蓝牙的0x002e的值

  • 指定目标 -b 64:B7:08:61:B9:7E
  • 读取操作 –char-read
  • 指定句柄 -a 0x002e
    因此指令为:gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002e
    1
    2
    3
    └─$ gatttool -b 64:B7:08:61:B9:7E  --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

    出现的是原始数据,我们需要对其进行处理
    1
    2
    3
    ┌──(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002e|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
    d205303e099ceff44835
    读取成功
    接下来是写操作,需要将得到的值传递给句柄0x002c
  • 指定目标 -b 64:B7:08:61:B9:7E
  • 写操作 ,写操作这里有两种 ,–char-write无响应,–char-write-req有响应,推荐使用–char-writr-req
    • –char-write Characteristics Value Write Without Response (Write Command)
    • –char-write-req Characteristics Value Write (Write Request)
  • 指定句柄 -a 0x002c 读写操作都是使用-a指定句柄
  • 传入要写入指定句柄的数据 -n d205303e099ceff44835
    • 题目要求为“请确保使用 xxd 将其转换为十六进制”,因此-n $(echo -n "d205303e099ceff44835"|xxd -ps)
      总的指令就是:gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "d205303e099ceff44835"|xxd -ps)
      1
      gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "d205303e099ceff44835"|xxd -ps)
      成功
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      ┌──(kali㉿kali)-[~/Desktop]
      └─$ gatttool -b 64:B7:08:61:B9:7E --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

      ┌──(kali㉿kali)-[~/Desktop]
      └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002e|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
      d205303e099ceff44835

      ┌──(kali㉿kali)-[~/Desktop]
      └─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "d205303e099ceff44835"|xxd -ps)
      Characteristic value was written successfully

      ┌──(kali㉿kali)-[~/Desktop]
      └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
      Score:2 /20

0x3

%% 查看 handle 0x0030 的 ascii 值。按照它告诉你的去做,并将你找到的标志提交给 0x002c %%

按照之前的思路来读取句柄值

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0030|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

要求是设备的md5值(MD5 of Device Name)
接下来的步骤除了转换md5值就和0x2一样了

1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ echo -n "BLECTF" |md5sum
5cd56d74049ae40f442ece036c6f4f06 -

需要注意的是,这里只需要传入前20个字符,后续的操作也是这样的,只要前20个字符,并且依旧遵循“如果您使用的是 gatttool,请确保使用 xxd 将其转换为十六进制。”

1
gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "5cd56d74049ae40f442e"|xxd -ps)

成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0030|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
MD5 of Device Name

┌──(kali㉿kali)-[~/Desktop]
└─$ echo -n "BLECTF" |md5sum
5cd56d74049ae40f442ece036c6f4f06 -

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "5cd56d74049ae40f442e"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:3 /20

0x4 查找指定的句柄

%% 蓝牙 GATT 服务提供了一些额外的设备属性。尝试查找 Generic Access -> Device Name 的值。 %%

查找值

官网就可以查到具体的UUID值,ctrl+f搜索Device Name就可以得到0x2A00,使用-u 来指定uuid就可以对Device Name进行读取
BLE的属性类型是有限的,有四个大类:

  • Primary Service(首要服务项)
  • Secondary Service(次要服务项)
  • Include(包含服务项)
  • Characteristic(特征值)
    也可以进行手动排查
    1
    2
    3
    4
    5
    6
    #找出设备提供哪些顶层服务及其范围
    gatttool -b 64:B7:08:61:B9:7E --primary
    #分区查找特征值
    gatttool -b 64:B7:08:61:B9:7E --characteristics -s 0x0001 -e 0x0007
    #直接查找全部特征值
    gatttool -b 64:B7:08:61:B9:7E --characteristics
    结果如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ┌──(kali㉿kali)-[~/Desktop]
    └─$ sudo gatttool -b 64:B7:08:61:B9:7E --primary
    [sudo] password for kali:
    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
    ┌──(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E --characteristics -s 0x0014 -e 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

在另一个窗口开启btmon
在btmon日志里得到uuid和句柄,可以看到Value Handle: 0x0016 Value UUID: Device Name (0x2a00)

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
gatttool[117489]: < ACL Data..X flags 0x00 dlen 11  #1573 [hci0] 23271.388417
ATT: Read By Type Request (0x08) len 6
Handle range: 0x0014-0x001c
Attribute type: Characteristic (0x2803)
> HCI Event: Number of Completed... (0x13) plen 5 #1574 [hci0] 23271.428098
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#1573: len 11 (2 Kb/s)
Latency: 39 msec (39-39 msec ~39 msec)
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #1575 [hci0] 23271.475504
ATT: Read By Type Response (0x09) len 22
Attribute data length: 7
Attribute data list: 3 entries
Handle: 0x0015
Value[5]: 021600002a
Properties: 0x02
Read (0x02)
Value Handle: 0x0016
Value UUID: Device Name (0x2a00)
Handle: 0x0017
Value[5]: 021800012a
Properties: 0x02
Read (0x02)
Value Handle: 0x0018
Value UUID: Appearance (0x2a01)
Handle: 0x0019
Value[5]: 021a00a62a
Properties: 0x02
Read (0x02)
Value Handle: 0x001a
Value UUID: Central Address Resolution (0x2aa6)

使用–characteristics 会有很多输出,这个时候可以对其进行过滤

1
2
3
4
#查找所有特征值
gatttool -b 64:B7:08:61:B9:7E --characteristics
# 启动 btmon 并寻找和目标(Device Name)有关的请求和响应
sudo btmon | grep -A 5 -B 5 -i 'Device Name'

结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo btmon | grep -A 5 -B 5 -i 'Device Name'
Handle: 0x0015
Value[5]: 021600002a
Properties: 0x02
Read (0x02)
Value Handle: 0x0016
Value UUID: Device Name (0x2a00)
Handle: 0x0017
Value[5]: 021800012a
Properties: 0x02
Read (0x02)
Value Handle: 0x0018


进行读取

从句柄来读取
-a

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0016|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

从uuid来读
-u

1
gatttool -b 64:B7:08:61:B9:7E --char-read -u 0x2a00 | awk -F'value: ' '{print $2}' | tr -d ' ' | xxd -r -p ; printf '\n'

结果如下,可以看到此次读取中-a和-u的结果是不一样的
虽然0x2A00对应Device Name,但设备可能将多个特性映射到同一UUID,导致实际访问的特性与预期不符。使用-u参数时,部分蓝牙栈实现可能默认读取固定长度(如20字节),而-a参数直接读取全部数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0016|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
2b00042f7481c7b056c4b410d28f33cf

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -u 0x2a00|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -u 0x2a00 | awk -F'value: ' '{print $2}' | tr -d ' ' | xxd -r -p ; printf '\n'
2b00042f7481c7b056c

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -u 0x2a00
handle: 0x0016 value: 32 62 30 30 30 34 32 66 37 34 38 31 63 37 62 30 35 36 63

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0016
Characteristic value/descriptor: 32 62 30 30 30 34 32 66 37 34 38 31 63 37 62 30 35 36 63 34 62 34 31 30 64 32 38 66 33 33 63 66


所以在此次提交中我们使用-a读取到的值(记住此次提交也是提交前20个字符,使用 xxd 将其转换为十六进制)

1
2
3
4
5
6
7
8
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "2b00042f7481c7b056c4"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:4 /20

0x5交互性验证

%% 读取句柄 0032 并按照它所说的去做。请注意,它并没有像以前那样告诉你写入标志句柄。找到标志后,请继续将其写入您在过去标志中使用的标志句柄。 %%

读取句柄

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0032|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

在0x002c写入任意值

1
gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "123"|xxd -ps)

之后再读取0x003c值就会发生变化,将这个变化的值再填入0x002c就成功了
结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0032|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Write anything here

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "123"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0032|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
3873c0270763568cf7aa

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "3873c0270763568cf7aa"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:5 /20

0x6 写入ascii值

%% 按照读取 handle 0x0034 中的说明进行作。请记住,有些工具只写入十六进制值,而其他工具提供写入十六进制或 ASCII 的方法 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

“Write the ascii value “yo” here”
要求写入ascii值(yo对应的十六进制是0x79 0x6f)

1
gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0034 -n $(echo -n "yo"|xxd -ps)

再次查看0x0034的值会发现它出现了变化,将新的值写入0x002c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Write the ascii value "yo" here

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0034 -n 796f
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
c55c6314b3db0a6128af

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "c55c6314b3db0a6128af"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:6 /20

关于这题,如果想尝试不同的写入方法可以像这样修改0x0034的值
只要把0x0034的值改成不是”yo”,它就会显示最初的提示语了“Write the ascii value “yo” he”

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
c55c6314b3db0a6128af

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0034 -n 796g
[sudo] password for kali:
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Write the ascii value "yo" he

1.gatttool

使用十六进制直接写入

1
sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0034 -n 796f

在线转换

1
gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0034 -n $(echo -n "yo"|xxd -ps)

2.bluetoothctl

1
2
3
4
5
6
7
8
9
10
11
12
sudo bluetoothctl
#进行连接
[bluetooth]# connect 64:B7:08:61:B9:7E
#进入操作菜单
[BLECTF]# menu gatt
#查看当前句柄
[BLECTF]# list-attributes
#若有目标句柄,则选中
[BLECTF]# select-attribute 0x0034
#尝试写入;新版本的bluetoothctl是可以直接写入的,如果不行那么写入十六进制形式
[BLECTF]# write "yo"
[BLECTF]# write "0x79 0x6f"

我使用bluetoothctl时找不到句柄0x0034,所以无法使用这种方法进行写入,这是list的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[BLECTF]# list-attributes
Primary Service (Handle 0x0001)
/org/bluez/hci0/dev_64_B7_08_61_B9_7E/service0001
00001801-0000-1000-8000-00805f9b34fb
Generic Attribute Profile
...
Characteristic (Handle 0x0033)
/org/bluez/hci0/dev_64_B7_08_61_B9_7E/service0028/char0033
0000ff06-0000-1000-8000-00805f9b34fb
Unknown
Characteristic (Handle 0x0035)
/org/bluez/hci0/dev_64_B7_08_61_B9_7E/service0028/char0035
0000ff07-0000-1000-8000-00805f9b34fb
Unknown
...

0x0034是一个非标准或 CTF 特定的隐藏句柄,标准发现工具 bluetoothctl 无法看到,但可以直接操作的 gatttool -a 可以访问。

  • gatttool -a 的行为: gatttool --char-read -a <handle> 和 --char-write-req -a <handle> 直接作用于任何你指定的句柄,不管它代表服务、特征声明、特征值还是描述符。只要该句柄存在且具有相应的读/写权限,gatttool 就能直接与之交互。

进行验证:
使用0x4中查找指定值的方法我找到了0x0034

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --characteristics
...
handle = 0x0033, char properties = 0x0a, char value handle = 0x0034, uuid = 0000ff06-0000-1000-8000-00805f9b34fb
...
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo btmon | grep -A 5 -B 5 -i '0x0034'
[sudo] password for kali:
Handle: 0x0033
Value[5]: 0a340006ff
Properties: 0x0a
Read (0x02)
Write (0x08)
Value Handle: 0x0034
Value UUID: Unknown (0xff06)
Handle: 0x0035
Value[5]: 0a360007ff
Properties: 0x0a
Read (0x02)


这是一些解释

  1. handle = 0x0033 (来自 gatttool –characteristics 输出)
    • 类型: 这是特征声明 (Characteristic Declaration) 的句柄。
    • 作用: 这个句柄本身代表了“这里有一个特征”的声明。它包含了一些元数据:
      • 该特征的属性/权限 (Properties): 0x0a (根据 btmon 输出,这意味着 Read 0x02 + Write 0x08)。
      • 指向实际存储该特征值的值句柄 (Value Handle): 0x0034。
      • 该特征的 UUID: 0000ff06-… (标识这个特征是什么)。
    • 交互: 你通常不直接读取或写入特征声明句柄 (0x0033) 本身。它的值 (0a340006ff 来自 btmon) 包含了上面提到的元数据,由客户端在发现过程中读取。
  2. char value handle = 0x0034 (来自 gatttool –characteristics 输出) / Value Handle: 0x0034 (来自 btmon 输出)
    • 类型: 这是特征值 (Characteristic Value) 的句柄。
    • 作用: 这个句柄指向实际存储数据的地方。当你想要读取这个特征当前的值,或者想要向这个特征写入新值时,你必须使用这个句柄
    • 交互:
      • 读取这个特征的值,你应该执行 gatttool … –char-read -a 0x0034。
      • 写入这个特征的值(因为它具有 Write 属性 0x08),你应该执行 gatttool … –char-write-req -a 0x0034 -n  或 gatttool … –char-write -a 0x0034 -n
  • 0x0033 是特征声明句柄 (Characteristic Declaration Handle): 它是一个元数据条目,描述了特征的存在、属性(权限)、值句柄和 UUID。它的值包含了这些结构化的信息,通常不由客户端直接写入来改变用户数据。
  • 0x0034 是特征值句柄 (Characteristic Value Handle): 这才是实际存储该特征数据的地方。所有针对该特征的读写操作都应该直接作用于这个句柄

所以如果要使用bluetoothctl来修改句柄0x0034的值,需要指定对象路径/org/bluez/hci0/dev_64_B7_08_61_B9_7E/service0028/char0033而不是0x0033
但我尝试了下,还是无法写入

1
2
3
4
5
6
7
[BLECTF]# select-attribute /org/bluez/hci0/dev_64_B7_08_61_B9_7E/service0028/char0033
[BLECTF]# write "0x79 0x6f"
No attribute selected
[BLECTF]# select-attribute 0000ff06-0000-1000-8000-00805f9b34fb
[BLECTF]# write "0x79 0x6f"
No attribute selected

3.脚本

bluepy 通常需要 root 权限来访问底层的蓝牙接口和 bluepy-helper 程序。
不需要额外的进行pip install bluepy,当你尝试这样做时反而会报错
不建议使用bleak来做这题,理由和bluetoothctl一样

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
38
39
40
41
42
43
44
45
# -*- coding: utf-8 -*-
import sys
from bluepy import btle

# --- 配置参数 ---
DEVICE_ADDRESS = "64:B7:08:61:B9:7E" # 替换为你的目标设备 MAC 地址
# 地址类型:通常是公共地址。如果是随机地址,使用 btle.ADDR_TYPE_RANDOM
ADDR_TYPE = btle.ADDR_TYPE_PUBLIC
TARGET_HANDLE = 0x0034 # 你要写入数据的目标句柄
DATA_TO_WRITE = b"yo" # 要写入的 ASCII 值 "yo",表示为字节串 (bytes)

print(f"正在连接到 {DEVICE_ADDRESS}...")
conn = None # 初始化连接对象为 None,以便在 finally 中检查
try:
# 创建连接对象并连接
conn = btle.Peripheral(DEVICE_ADDRESS, ADDR_TYPE)
print("连接成功!")

print(f"正在向句柄 {hex(TARGET_HANDLE)} 写入 {DATA_TO_WRITE!r}...")
# 执行写入操作
# 第一个参数是目标句柄
# 第二个参数是要写入的数据(必须是 bytes 类型)
# withResponse=True 表示执行 Write Request (需要响应),类似 gatttool --char-write-req
# 如果需要 Write Command (无响应),则设置 withResponse=False
conn.writeCharacteristic(TARGET_HANDLE, DATA_TO_WRITE, withResponse=True)
print("写入成功!")

except btle.BTLEDisconnectError as e:
# 捕获断开连接错误
print(f"连接已断开: {e}")
except btle.BTLEException as e:
# 捕获其他 bluepy 相关的蓝牙错误
print(f"蓝牙错误: {e}")
except Exception as e:
# 捕获其他任何意外错误
print(f"发生意外错误: {e}")
finally:
# 无论成功还是失败,最后都尝试断开连接
if conn:
try:
print("正在断开连接...")
conn.disconnect()
except btle.BTLEException as e:
# 如果连接已因错误断开,再次断开可能会抛出异常,此处捕获
print(f"断开连接时发生错误(可能已断开): {e}")

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ sudo python ble1.py
正在连接到 64:B7:08:61:B9:7E...
连接成功!
正在向句柄 0x34 写入 b'yo'...
写入成功!
正在断开连接...
┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
c55c6314b3db0a6128af

┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$

0x7 写入hex值

%% 按照读取 handle 0x0036 中的说明进行操作。请记住,有些工具只写入十六进制值,而其他工具提供写入十六进制或 ASCII 的方法 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

要求写入hex值:Write the hex value 0x07 here
结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Write the hex value 0x07 here

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0036 -n 7
Invalid value

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0036 -n 07
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
1179080b29f8da16ad66

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "1179080b29f8da16ad66"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:7 /20

1.gatttool

gatttool 的 -n 参数期望一个十六进制字符串。对于单个字节 0x07,其十六进制字符串表示就是 07。(注意是07不是7)
gatttool 的 -n 参数期望接收的是一个有效的十六进制字符串,而不仅仅是一个数字字符

  • 在十六进制中,有效的字符是 0-9 和 a-f (或 A-F)。单个字符 7 是一个有效的十六进制数字,代表数值 7。
  • 但是, gatttool(以及底层的蓝牙协议)通常期望十六进制数据是成对出现的,因为一个字节通常用两个十六进制字符表示(例如,00 到 ff)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ┌──(kali㉿kali)-[~/Desktop]
    └─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0036 -n 7
    Invalid value

    ┌──(kali㉿kali)-[~/Desktop]
    └─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0036 -n 07
    Characteristic value was written successfully

    ┌──(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
    1179080b29f8da16ad66

    其他形式能够写入,但是判定不正确
    1
    2
    3
    4
    5
    6
    7
    8
    ┌──(kali㉿kali)-[~/Desktop]
    └─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0036 -n 0x07
    Characteristic value was written successfully

    ┌──(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
    Write the hex value 0x07 here

2.脚本

使用bluepy

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
# -*- coding: utf-8 -*-
import sys
from bluepy import btle

# --- 配置参数 ---
DEVICE_ADDRESS = "64:B7:08:61:B9:7E" # 替换为你的 MAC 地址
ADDR_TYPE = btle.ADDR_TYPE_PUBLIC
TARGET_HANDLE = 0x0036 # 目标句柄
HEX_VALUE_TO_WRITE = 0x07 # 要写入的十六进制值

# 将十六进制值转换为 bytes 对象
data_to_write = bytes([HEX_VALUE_TO_WRITE]) # -> b'\x07'

print(f"正在连接到 {DEVICE_ADDRESS}...")
conn = None
try:
conn = btle.Peripheral(DEVICE_ADDRESS, ADDR_TYPE)
print("连接成功!")

print(f"正在向句柄 {hex(TARGET_HANDLE)} 写入 {data_to_write!r}...")
# 执行写入操作, withResponse=True 表示 Write Request
conn.writeCharacteristic(TARGET_HANDLE, data_to_write, withResponse=True)
print("写入成功!")

except btle.BTLEDisconnectError as e:
print(f"连接已断开: {e}")
except btle.BTLEException as e:
print(f"蓝牙错误: {e}")
except Exception as e:
print(f"发生意外错误: {e}")
finally:
if conn:
try:
print("正在断开连接...")
conn.disconnect()
except btle.BTLEException as e:
print(f"断开连接时发生错误(可能已断开): {e}")

记得使用sudo来执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ python ble2.py
Traceback (most recent call last):
File "/home/kali/Desktop/ble2.py", line 3, in <module>
from bluepy import btle
ModuleNotFoundError: No module named 'bluepy'

┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Write the hex value 0x07 here

┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ sudo python ble2.py
[sudo] password for kali:
正在连接到 64:B7:08:61:B9:7E...
连接成功!
正在向句柄 0x36 写入 b'\x07'...
写入成功!
正在断开连接...

┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0036|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
1179080b29f8da16ad66

0x8 指定句柄的方式

%% 按照读取 handle 0x0038 中的说明进行作。请注意此处的句柄。请记住,句柄可以由整数或十六进制引用。大多数工具(如 gatttool 和 bleah)都允许您以两种方式指定句柄。 %%

读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0038|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

Write 0xC9 to handle 58
也就是说将十六进制数0xc9写入句柄58就可以了,58的十六进制是0x3A

-a后面可以接十进制也可以接十六进制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0038 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
Write 0xC9 to handle 58
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 58 -n "$(printf "\xc" | xxd -ps)"
Characteristic value was written successfully
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0038|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
f8b136d937fad6a2be9f
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "f8b136d937fad6a2be9f"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:8 /20

0x9 多次写

%% 看看 handle 0x003c 并按照它所说的去做。您应该为此编写一个解决方案。另请记住,某些工具的写入速度比其他工具快。 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x003c | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Brute force my value 00 to ff
爆破句柄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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# -*- coding: utf-8 -*-
import sys
import time
from bluepy import btle

# --- 配置 ---
DEVICE_ADDRESS = "64:B7:08:61:B9:7E" # 目标设备 MAC 地址
ADDR_TYPE = btle.ADDR_TYPE_PUBLIC # 地址类型
TARGET_HANDLE = 0x003c # 要写入和检查的句柄 (十六进制)
INITIAL_VALUE = b"Brute force my value 00 to ff" # 句柄的初始值 (bytes 类型)
SLEEP_INTERVAL = 0.05 # 每次写入后的暂停时间(秒),可调整
RECONNECT_DELAY = 2 # 尝试重连前的等待时间(秒)
WRITE_TIMEOUT_DEFAULT = 1.0 # 默认写入超时(bluepy内部使用)

print(f"Target: {DEVICE_ADDRESS}")
print(f"Target Handle: {hex(TARGET_HANDLE)}")
print(f"Initial Expected Value: {INITIAL_VALUE!r}")

conn = None # 初始化连接对象
found_value = None # 用于存储找到的正确值

try:
print("Connecting...")
conn = btle.Peripheral(DEVICE_ADDRESS, ADDR_TYPE)
# conn.setSecurityLevel("medium") # 如果需要配对,可能需要设置安全级别
print("Connected.")

# --- 主循环:暴力破解 0x00 到 0xFF ---
for i in range(256): # 0 到 255
value_to_try = i
hex_value_str = f"{value_to_try:02x}" # 用于打印的十六进制字符串
data_byte = bytes([value_to_try]) # 要写入的单字节 bytes 对象

print(f"Trying value: {hex(value_to_try)} (Hex: {hex_value_str}, Bytes: {data_byte!r})")

try:
# --- 执行写入 ---
# 使用 withResponse=True 尝试 Write Request
conn.writeCharacteristic(TARGET_HANDLE, data_byte, withResponse=True)
# print(" Write request sent successfully (protocol level).") # 可以取消注释用于调试

# --- 成功检查:读取同一个句柄的值 ---
# 等待一小段时间让设备处理
time.sleep(SLEEP_INTERVAL)

# 读取当前句柄的值
current_value = conn.readCharacteristic(TARGET_HANDLE)
print(f" -> Value read back from {hex(TARGET_HANDLE)}: {current_value!r}")

# 检查值是否已经改变 (不再是初始值)
if current_value != INITIAL_VALUE:
print(f"\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
print(f"!!! SUCCESS: Handle {hex(TARGET_HANDLE)} value changed! !!!")
print(f"!!! Correct value likely: {hex(value_to_try)} (Hex: {hex_value_str}) !!!")
print(f"!!! New value in handle: {current_value!r} !!!")
print(f"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
found_value = value_to_try
break # 找到正确值,退出循环

except btle.BTLEException as e:
# --- 处理写入或读取错误 ---
print(f" -> FAILED for value {hex(value_to_try)}: {e}")

# 检查是否是连接丢失错误
connection_lost = False
try:
# 尝试发送一个无操作的请求(如读取RSSI)来检查连接状态
conn.getrssi()
except btle.BTLEDisconnectError:
connection_lost = True
except btle.BTLEException:
# 其他蓝牙错误也可能意味着连接问题
connection_lost = True # 保守假设连接可能丢失

if connection_lost:
print("Connection lost, attempting to reconnect...")
time.sleep(RECONNECT_DELAY)
try:
conn.connect(DEVICE_ADDRESS, ADDR_TYPE)
print("Reconnected successfully.")
except Exception as recon_e:
print(f"Reconnect failed: {recon_e}. Aborting.")
break # 重连失败,放弃
else:
# 如果不是连接丢失错误,可能是写入不允许或其他错误,继续下一个尝试
# print(" Continuing to next value...")
# 短暂暂停避免过于频繁的无效尝试
time.sleep(SLEEP_INTERVAL * 2) # 错误后暂停时间稍长

# 防止循环过快占用过多 CPU
time.sleep(SLEEP_INTERVAL)

except btle.BTLEDisconnectError as e:
print(f"\nError: Disconnected during operation: {e}")
except btle.BTLEException as e:
print(f"\nError: Bluetooth operation failed: {e}")
except KeyboardInterrupt:
print("\nOperation cancelled by user.")
except Exception as e:
print(f"\nAn unexpected error occurred: {e}")
finally:
# --- 清理:确保断开连接 ---
if conn:
try:
print("Disconnecting...")
conn.disconnect()
except btle.BTLEException:
# 可能已经断开连接,忽略错误
pass

# --- 结果报告 ---
if found_value is not None:
print(f"\nBrute force finished. The correct value appears to be: {hex(found_value)} (Decimal: {found_value})")
sys.exit(0) # 成功退出
else:
print("\nBrute force finished. The correct value was not found (or the handle value did not change).")
sys.exit(1) # 失败退出

输出为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Trying value: 0xd0 (Hex: d0, Bytes: b'\xd0')
-> Value read back from 0x3c: b'Brute force my value 00 to ff'
Trying value: 0xd1 (Hex: d1, Bytes: b'\xd1')
-> Value read back from 0x3c: b'933c1fcfa8ed52d2ec05'

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!! SUCCESS: Handle 0x3c value changed! !!!
!!! Correct value likely: 0xd1 (Hex: d1) !!!
!!! New value in handle: b'933c1fcfa8ed52d2ec05' !!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Disconnecting...

Brute force finished. The correct value appears to be: 0xd1 (Decimal: 209)

进行验证

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "933c1fcfa8ed52d2ec05"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:9 /20

0x10 多次读

%% 看看 Handle 0x003e 并按照它所说的去做。请记住,某些工具在执行读取和写入时比其他工具具有更好的连接速度。这与该工具提供的功能或它如何在主机 OS 上使用缓存的 BT 连接有关。尝试针对此标志测试不同的工具。找到最快的一个后,启动脚本或 bash 1 liner 以完成任务。仅供参考,一旦运行,如果作得当,此任务大约需要 90 秒才能完成。 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x003e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Read me 1000 times
读取0x003e一千次

执行一千次shell操作

执行一千次shell操作并打印最后十次的结果

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x003e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

设定时间为三分钟,一旦出现报错就立即停止

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/bin/bash

# --- 配置 ---
MAC="64:B7:08:61:B9:7E"
HANDLE="0x003e"
COUNT=1000
LAST_N=10 # 只打印最后 N 次的结果
READ_TIMEOUT=5 # gatttool 读取超时(秒)
MAX_DURATION_SECONDS=180 # 最大总执行时间(秒),3分钟

# --- 用于存储最后 N 个结果的数组 ---
declare -a last_results

# --- 开始时间和计数器 ---
start_time=$(date +%s)
completed_reads=0
error_occurred=false

echo "Starting to read handle $HANDLE from $MAC, $COUNT times..."
echo "(Using gatttool in a loop - this will be VERY SLOW and potentially unstable)"
echo "Max duration set to $MAX_DURATION_SECONDS seconds."

# --- 主循环 ---
for (( i=1; i<=COUNT; i++ )); do
# --- 检查总执行时间 ---
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
if [ "$elapsed_time" -gt "$MAX_DURATION_SECONDS" ]; then
echo -e "\nError: Execution time exceeded ${MAX_DURATION_SECONDS} seconds. Stopping."
error_occurred=true
break # 超时则跳出循环
fi

# 打印进度 (每 10 次打印一次)
if (( i % 10 == 0 )) || [ "$i" -eq "$COUNT" ]; then
printf "\rReading %d/%d (Elapsed: %ds)..." "$i" "$COUNT" "$elapsed_time"
fi

# --- 执行命令并捕获输出 ---
# 捕获标准输出,错误输出丢弃(或重定向到日志文件)
output=$(gatttool -b "$MAC" --char-read -a "$HANDLE" -t "$READ_TIMEOUT" 2>/dev/null | awk -F': ' '{print $2}' | tr -d ' ' | xxd -r -p)
exit_code=$? # 获取管道中最后一个命令(xxd)的退出码?这不一定可靠,最好检查gatttool本身

# 更可靠地检查 gatttool 是否成功连接并读取
# 运行 gatttool 并检查其退出码,忽略解码管道的退出码
gatttool_output=$(gatttool -b "$MAC" --char-read -a "$HANDLE" -t "$READ_TIMEOUT" 2>&1)
gatttool_exit_code=$?

if [ $gatttool_exit_code -ne 0 ]; then
# 如果 gatttool 命令本身失败
echo -e "\nError: gatttool failed on read #$i (Exit code: $gatttool_exit_code). Stopping."
echo " gatttool output: $gatttool_output"
error_occurred=true
break # 遇到错误停止
else
# gatttool 成功,解码输出
decoded_output=$(echo "$gatttool_output" | awk -F': ' '{print $2}' | tr -d ' ' | xxd -r -p)

# --- 存储结果 ---
# 将结果添加到数组末尾
last_results+=("$decoded_output")
# 如果数组超长,移除第一个元素
if [ ${#last_results[@]} -gt $LAST_N ]; then
last_results=("${last_results[@]:1}") # Bash 4+ 语法移除第一个元素
fi
completed_reads=$i
fi

# (可选) 短暂暂停
# sleep 0.01

# 添加 Ctrl+C 中断处理
trap "echo -e '\nRead sequence interrupted by user.'; exit 1" INT
done

echo # 结束进度条打印,换行

# --- 打印最终结果 ---
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
echo "-----------------------------------------"
echo "Finished."
echo "Total execution time: ${elapsed_time} seconds."
echo "Completed reads: ${completed_reads}/${COUNT}."

if [ "$error_occurred" = true ]; then
echo "Operation stopped due to error or timeout."
exit 1
elif [ "$completed_reads" -lt "$COUNT" ]; then
echo "Operation stopped before completing all reads (likely interrupted)."
exit 1
else
echo "Operation completed $COUNT reads."
echo "\nLast $LAST_N read values:"
# 打印存储的最后 N 个结果
count_to_print=${#last_results[@]} # 获取实际存储的数量
if [ $count_to_print -gt 0 ]; then
start_print_index=$((COUNT - count_to_print + 1))
# 使用 printf 格式化输出
for k in "${!last_results[@]}"; do # 遍历数组索引
original_index=$((start_print_index + k))
printf " Read #%d: %s\n" "$original_index" "${last_results[$k]}"
done
else
echo " No results were successfully stored."
fi
echo "-----------------------------------------"
exit 0 # 成功退出
fi

成功,但是用了更多的多的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(kali㉿kali)-[~/Desktop]
└─$ ./test4.sh
Starting to read handle 0x003e from 64:B7:08:61:B9:7E, 1000 times...
(Using gatttool in a loop - this will be VERY SLOW and potentially unstable)
Max duration set to 180 seconds.
Reading 1000/1000 (Elapsed: 161s)...
-----------------------------------------
Finished.
Total execution time: 161 seconds.
Completed reads: 1000/1000.
Operation completed 1000 reads.
\nLast 10 read values:
Read #991: 6ffcd214ffebdc0d069e
Read #992: 6ffcd214ffebdc0d069e
Read #993: 6ffcd214ffebdc0d069e
Read #994: 6ffcd214ffebdc0d069e
Read #995: 6ffcd214ffebdc0d069e
Read #996: 6ffcd214ffebdc0d069e
Read #997: 6ffcd214ffebdc0d069e
Read #998: 6ffcd214ffebdc0d069e
Read #999: 6ffcd214ffebdc0d069e
Read #1000: 6ffcd214ffebdc0d069e

进行验证

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x002c -n $(echo -n "6ffcd214ffebdc0d069e"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Score:10 /20

bluepy

使用bluepy会更快

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# -*- coding: utf-8 -*-
import sys
import time
from bluepy import btle
from datetime import datetime, timedelta

# --- 配置 ---
DEVICE_ADDRESS = "64:B7:08:61:B9:7E" # 目标设备 MAC 地址
ADDR_TYPE = btle.ADDR_TYPE_PUBLIC # 地址类型
TARGET_HANDLE = 0x003e # 要重复读取的句柄
READ_COUNT = 1000 # 要读取的次数
LAST_N_RESULTS = 10 # 存储并打印最后多少个结果
MAX_DURATION_SECONDS = 180 # 最大执行时间(秒),3分钟
RECONNECT_DELAY = 2 # 尝试重连前的等待时间(秒)

# --- 用于存储和检测变化的变量 ---
results = [] # 存储最后 N 个结果
previous_value = None # 存储上一次读取的值
change_detected_at = None # 记录第一次发生变化的读取次数
value_after_change = None # 记录变化后的第一个值
operation_successful = False # 标记操作是否未出错完成

print(f"Target: {DEVICE_ADDRESS}")
print(f"Handle to read repeatedly: {hex(TARGET_HANDLE)}")
print(f"Number of reads required: {READ_COUNT}")
print(f"Maximum execution time: {MAX_DURATION_SECONDS} seconds")
print(f"Will store and print last {LAST_N_RESULTS} results.")
print(f"Will report first detected value change.")

conn = None
start_time = datetime.now() # 记录开始时间

try:
print("Connecting...")
conn = btle.Peripheral(DEVICE_ADDRESS, ADDR_TYPE)
print("Connected.")

# --- 重复读取循环 ---
for i in range(1, READ_COUNT + 1):
current_time = datetime.now()
elapsed_seconds = int((current_time - start_time).total_seconds()) # 计算已用时间(整数秒)

# --- 打印进度和已用时间 ---
# 使用 \r 实现行内更新,flush=True 确保立即显示
print(f"\rReading {i}/{READ_COUNT} (Elapsed: {elapsed_seconds}s)...", end='', flush=True)

# --- 检查总执行时间 ---
if elapsed_seconds > MAX_DURATION_SECONDS:
print(f"\nError: Execution time exceeded {MAX_DURATION_SECONDS} seconds. Stopping.")
break # 超时则跳出循环

try:
# --- 执行读取操作 ---
current_value = conn.readCharacteristic(TARGET_HANDLE)
results.append(current_value) # 添加到结果列表

# 保持结果列表最多为 LAST_N_RESULTS 个
if len(results) > LAST_N_RESULTS:
results.pop(0) # 移除最早的一个

# --- 检测值是否改变 (仅在第一次改变时记录) ---
if previous_value is not None and current_value != previous_value and change_detected_at is None:
change_detected_at = i
value_after_change = current_value
# 发现变化时立即打印信息,并换行以保留进度条的最后状态
print(f"\n*** Value change detected at read #{i}! ***")
print(f" Previous value (read #{i-1}): {previous_value!r}")
print(f" New value (read #{i}): {value_after_change!r}")

# 更新上一个值,用于下一次比较
previous_value = current_value

except btle.BTLEException as e:
# --- 处理读取错误 ---
print(f"\nError during read #{i}: {e}")
connection_lost = False
try:
conn.getState()
if conn.getState() != "conn": connection_lost = True
except Exception: connection_lost = True

if connection_lost:
print("Connection lost. Aborting script.")
break
else:
print("An error occurred, but connection seems stable. Aborting script.")
break
except Exception as e:
print(f"\nAn unexpected non-Bluetooth error occurred during read #{i}: {e}")
break

# 可以在这里加入非常小的暂停,如果需要的话
# time.sleep(0.01)

else: # for 循环正常结束 (没有 break)
print() # 结束进度打印,换行
if len(results) >= READ_COUNT % LAST_N_RESULTS or READ_COUNT <= LAST_N_RESULTS : # 检查是否真的完成了所有读取
print(f"\nSuccessfully completed {READ_COUNT} reads.")
operation_successful = True


except btle.BTLEDisconnectError as e:
print(f"\nError: Disconnected during operation: {e}")
except btle.BTLEException as e:
print(f"\nError: Bluetooth connection or operation failed: {e}")
except KeyboardInterrupt:
print("\nOperation cancelled by user.")
except Exception as e:
print(f"\nAn unexpected error occurred: {e}")
finally:
# --- 清理:确保断开连接 ---
if conn:
try:
print("Disconnecting...")
conn.disconnect()
except btle.BTLEException:
pass

# --- 最终报告 ---
end_time = datetime.now()
duration = end_time - start_time
print(f"\nTotal execution time: {duration}")

# 报告变化检测结果
print("-" * 40)
if change_detected_at is not None:
print(f"Value change detected at read number: {change_detected_at}")
print(f"Value after change was: {value_after_change!r}")
else:
print("No value change detected during the reads.")
print("-" * 40)

if operation_successful:
print("Operation finished within time limit and without critical errors.")
# 打印最后 N 个结果
print(f"\nLast {len(results)} read values (up to {LAST_N_RESULTS}):")
start_print_index = max(1, READ_COUNT - len(results) + 1) # 计算起始索引
for k, val in enumerate(results):
original_index = start_print_index + k
print(f" Read #{original_index}: {val.hex()} ({val!r})")
print("-" * 40)
sys.exit(0) # 成功退出
else:
print("Operation did not complete successfully or timed out.")
# 如果有部分结果,也打印出来
if results:
print(f"\nLast {len(results)} read values before stopping:")
start_print_index = max(1, i - len(results) + 1) # i 是停止时的循环次数
for k, val in enumerate(results):
original_index = start_print_index + k
print(f" Read #{original_index}: {val.hex()} ({val!r})")
print("-" * 40)
sys.exit(1) # 失败退出

更快

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo python test5.py
Target: 64:B7:08:61:B9:7E
Handle to read repeatedly: 0x3e
Number of reads required: 1000
Maximum execution time: 180 seconds
Will store and print last 10 results.
Will report first detected value change.
Connecting...
Connected.
Reading 1000/1000 (Elapsed: 80s)...

Successfully completed 1000 reads.
Disconnecting...

Total execution time: 0:01:20.515156

同样成功

1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x003e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
6ffcd214ffebdc0d069e

0x11 订阅gatt notify

%% 查看 handle 0x0040 和 google search gatt 通知。gatttool 等一些工具能够订阅 gatt 通知 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0040 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Listen to me for a single notification

Notifications are unsolicited PDUs of type ATT_HANDLE_VALUE_NTF that are sent by a server to a client. No reply PDU is defined. 通知是由服务器发送到客户端的 ATT_HANDLE_VALUE_NTF 类型的未经请求的 PDU。未定义应答 PDU。

指示和通知是可以通过 attribute(ATT) 协议发送的命令。因此,在 ATT 层定义了两个角色:客户端和服务器。指示和通知是 GATT 客户端订阅 GATT 服务器提供的数据的一种方式。客户端必须通过其客户端特征配置描述符为特征的值配置 Indications 和 Notifications,以便在每次在服务器上更新特征的值时收到通知。
指示Indications需要由客户确认。服务器在从客户端获取回知之前不会发送以下指示。客户端向服务器发送了一条确认消息;这样 Server 就知道消息到达了 Client。因此,通过指示进行通信的速度较慢。
通知Notifications不需要确认,因此速度更快。因此,服务器不知道消息是否到达客户端。

检查notify设置

1
2
3
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --characteristics
handle = 0x003f, char properties = 0x1a, char value handle = 0x0040, uuid = 0000ff0c-0000-1000-8000-00805f9b34fb
  • 句柄0x003f是该特征的声明句柄(Characteristic Declaration)
  • 句柄0x0040是该特征的值句柄(Characteristic Value)
  • 客户端通过句柄0x0040进行读写和订阅通知
  • uuid为自定义uuid
  1. 特征属性位掩码 (Characteristic Properties Bitfield):
属性 十六进制值 二进制位 说明
Broadcast 0x01 0000 0001 广播
Read 0x02 0000 0010 允许读取
Write Without Resp 0x04 0000 0100 允许无响应写入
Write 0x08 0000 1000 允许带响应写入
Notify 0x10 0001 0000 允许通知
Indicate 0x20 0010 0000 允许指示
Authenticated Write 0x40 0100 0000 认证写入
Extended Properties 0x80 1000 0000 扩展属性
  1. 解码 0x1a:
    • 我们将十六进制值 0x1a 转换为二进制:0001 1010。
    • 现在我们按位检查:
      • Bit 0 (值 0x01): …0 - Broadcast 未设置。
      • Bit 1 (值 0x02): …1. - Read 设置。 (0x1a & 0x02 = 0x02 != 0)
      • Bit 2 (值 0x04): ..0. - Write Without Response 未设置。
      • Bit 3 (值 0x08): .1.. - Write 设置。 (0x1a & 0x08 = 0x08 != 0
      • Bit 4 (值 0x10): 1… - Notify 设置。 (0x1a & 0x10 = 0x10 != 0)
    • 结论: 因此,属性值 0x1a 表示该特征支持 ReadWrite (Write Request) 和 Notify 操作

测试目标句柄0x0040

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
38
39
40
41
42
43
44
45
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0040 -n 01 --listen
Characteristic value was written successfully
Notification handle = 0x0040 value: 35 65 63 33 37 37 32 62 63 64 30 30 63 66 30 36 64 38 65 62
^C

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0040 -n 02 --listen
Characteristic value was written successfully
Notification handle = 0x0040 value: 35 65 63 33 37 37 32 62 63 64 30 30 63 66 30 36 64 38 65 62

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0041 -n 0100 --listen
Characteristic Write Request failed: Attribute can't be written
^C

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0041 -n 00 --listen
Characteristic Write Request failed: Attribute can't be written
^C

#注意--listen的用法
#如果什么也不做只是listen是listen不到的
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --listen
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


分析

Notify (通知): 是特征的一种属性 (Property)。如果一个特征具有 Notify 属性,意味着服务器 (Peripheral,例如 BLECTF 设备) 可以在其值发生改变时,主动地、无需客户端请求地将新值发送给已订阅该通知的**客户端 (Central,例如你的 Kali 机器)**。
向句柄0x0040写入任何值都会触发通知,这个通知就是目标字符

1
2
3
4
5
char notify_data[20] = "5ec3772bcd00cf06d8eb"; // 这是要发送的通知数据
// 将句柄 0x0040 的值重置回初始指令
esp_ble_gatts_set_attr_value(blectf_handle_table[IDX_CHAR_FLAG_NOTIFICATION]+1, sizeof(notification_read_value)-1, (uint8_t *)notification_read_value);
// 发送通知,通知的句柄是特征值句柄,通知的数据是 notify_data
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, blectf_handle_table[IDX_CHAR_VAL_FLAG_NOTIFICATION], sizeof(notify_data), (uint8_t *)notify_data, false); // false 表示是 Notification 而不是 Indication

这是收到的日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gatttool[176501]: < ACL Data TX: Handle 70 flags 0x00 dlen 8                                        #213 [hci0] 3098.759446
ATT: Write Request (0x12) len 3
Handle: 0x0040
Data[1]: 02
> HCI Event: Number of Completed Packets (0x13) plen 5 #214 [hci0] 3098.799759
Num handles: 1
Handle: 70 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#213: len 8 (1 Kb/s)
Latency: 40 msec (40-40 msec ~40 msec)
> ACL Data RX: Handle 70 flags 0x02 dlen 5 #215 [hci0] 3098.843632
ATT: Write Response (0x13) len 0
> ACL Data RX: Handle 70 flags 0x02 dlen 27 #216 [hci0] 3098.843644
ATT: Handle Value Notification (0x1b) len 22
Handle: 0x0040
Data[20]: 3565633337373262636430306366303664386562


[!NOTE]
在这里留一个疑问,当我在现实中遇到了蓝牙设备我会来确认这点的
“ 这个结论仅适用于这个特定设备和句柄 0x0040,因为它的行为是由其独特的源代码逻辑决定的。对于其他标准的 BLE 设备或其他特征,你仍然需要遵循写入 CCCD 来启用通知的标准流程。”>

0x12 查看gatt indicate

%% 查看 handle 0x0042 和 google search gatt indicate。对于单个响应指示消息,例如此挑战,gatttool 等工具将正常工作。 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0042 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Listen to handle 0x0044 for a single indication
参照0x11来听就可以了

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0042 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
Listen to handle 0x0044 for a single indication

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0044 -n 02 --listen
Characteristic value was written successfully
Indication handle = 0x0044 value: 63 37 62 38 36 64 64 31 32 31 38 34 38 63 37 37 63 31 31 33
^C

在源码能直接核对答案了

1
2
3
4
if (strcmp(writeData,"c7b86dd121848c77c113") == 0){
//indicate
flag_state[11] = 'T';
}

作为一个 indication,客户端(kali)应该返回一个回应这个过程才算是完成了,但是这里我们只需要接受信息(题目要求只需要听)
这是它相关的日志

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
gatttool[174176]: < ACL Data TX: Handle 71 flags 0x00 dlen 8                                        #190 [hci0] 2811.800923
ATT: Write Request (0x12) len 3
Handle: 0x0044
Data[1]: 02
> HCI Event: Number of Completed Packets (0x13) plen 5 #191 [hci0] 2811.840726
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#190: len 8 (1 Kb/s)
Latency: 39 msec (39-39 msec ~39 msec)
> ACL Data RX: Handle 71 flags 0x02 dlen 5 #192 [hci0] 2811.887299
ATT: Write Response (0x13) len 0
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #193 [hci0] 2811.887312
ATT: Handle Value Indication (0x1d) len 22
Handle: 0x0044
Data[20]: 6337623836646431323138343863373763313133
gatttool[174176]: < ACL Data TX: Handle 71 flags 0x00 dlen 5 #194 [hci0] 2811.887499
ATT: Handle Value Confirmation (0x1e) len 0
> HCI Event: Number of Completed Packets (0x13) plen 5 #195 [hci0] 2811.927611
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#194: len 5 (1 Kb/s)
Latency: 40 msec (39-40 msec ~39 msec)


这是对日志的解释,它自动处理了“客户端的确认”

  1. gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0044 -n 02 --listen: 这是你执行的命令,向蓝牙地址为64:B7:08:61:B9:7E的设备句柄0x0044写入0x02,并监听响应。
  2. ACL Data TX: Handle 71 ... ATT: Write Request (0x12) len 3 ... Handle: 0x0044 Data[1]: 02gatttool发送了一个ATT Write Request,句柄是0x0044,写入的数据是0x02。 Handle 71 指的是客户端与服务器之间连接的句柄。
  3. HCI Event: Number of Completed Packets (0x13) ... Handle: 71 ... Count: 1: HCI事件表明之前发送的数据包已经成功传输。
  4. ACL Data RX: Handle 71 ... ATT: Write Response (0x13) len 0: 服务器发送了一个ATT Write Response,表示成功接收到写入请求。
  5. ACL Data RX: Handle 71 ... ATT: Handle Value Indication (0x1d) len 22 ... Handle: 0x0044 Data: 6337623836646431323138343863373763313133: 关键的一行!服务器发送了一个ATT Handle Value Indication。
    • Handle: 0x0044:表明Indication是针对句柄0x0044的。
    • Data: 6337623836646431323138343863373763313133:这是Indication携带的数据,总共20个字节,内容是6337623836646431323138343863373763313133
  6. ACL Data TX: Handle 71 ... ATT: Handle Value Confirmation (0x1e) len 0gatttool发送了一个ATT Handle Value Confirmation,表示已经接收到Indication。

0x13 读取多个notify

%% 查看 处理 0x0046 并按照它所说的去做。请记住,此通知 clallange 要求您收到多个回复才能完成。
%%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0046 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Listen to me for multi notifications
听到更多的通知

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0046 -n 02 --listen
Characteristic value was written successfully
Notification handle = 0x0046 value: 55 20 6e 6f 20 77 61 6e 74 20 74 68 69 73 20 6d 73 67 00 00
Notification handle = 0x0046 value: 63 39 34 35 37 64 65 35 66 64 38 63 61 66 65 33 34 39 66 64
Notification handle = 0x0046 value: 63 39 34 35 37 64 65 35 66 64 38 63 61 66 65 33 34 39 66 64

日志是这样的

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
38
39
40
41
42
43
44
45
gatttool[179010]: < ACL Data TX: Handle 71 flags 0x00 dlen 8                                        #294 [hci0] 3401.231380
ATT: Write Request (0x12) len 3
Handle: 0x0046
Data[1]: 02
> HCI Event: Command Status (0x0f) plen 4 #295 [hci0] 3401.232129
NOP (0x00|0x0000) ncmd 1
Status: Success (0x00)
> HCI Event: Number of Completed Packets (0x13) plen 5 #296 [hci0] 3401.271124
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#294: len 8 (1 Kb/s)
Latency: 39 msec (39-39 msec ~39 msec)
> HCI Event: LE Meta Event (0x3e) plen 12 #297 [hci0] 3401.310917
LE Read Remote Used Features (0x04)
Status: Success (0x00)
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Features: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00
LE Encryption
Connection Parameter Request Procedure
Extended Reject Indication
Peripheral-initiated Features Exchange
LE Ping
LE Data Packet Length Extension
LL Privacy
Extended Scanner Filter Policies
> ACL Data RX: Handle 71 flags 0x02 dlen 5 #298 [hci0] 3401.315294
ATT: Write Response (0x13) len 0
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #299 [hci0] 3401.315301
ATT: Handle Value Notification (0x1b) len 22
Handle: 0x0046
Data[20]: 55206e6f2077616e742074686973206d73670000
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #300 [hci0] 3402.315328
ATT: Handle Value Notification (0x1b) len 22
Handle: 0x0046
Data[20]: 6339343537646535666438636166653334396664
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #301 [hci0] 3403.315400
ATT: Handle Value Notification (0x1b) len 22
Handle: 0x0046
Data[20]: 6339343537646535666438636166653334396664
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #302 [hci0] 3404.315311
ATT: Handle Value Notification (0x1b) len 22
Handle: 0x0046
Data[20]: 6339343537646535666438636166653334396664

收到通知后等待一下就可以了,它会自动发送剩下的通知
对其63 39 34 35 37 64 65 35 66 64 38 63 61 66 65 33 34 39 66 64 解码后得到c9457de5fd8cafe349fd

0x14 读取多个indicate

%% 查看 handle 0x0042 和 google search gatt 指示。请记住,此 chalange 将要求您解析多个 indicate 响应才能完成 chalange。 %%
读取要求
注意,这里写错了,不是0x0042,是0x0048

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0048 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Listen to handle 0x004a for multi indications
依旧是等待一会就可以了

1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0048 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
Listen to handle 0x004a for multi indications

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x004a -n 02 --listen
Characteristic value was written successfully
Indication handle = 0x004a value: 55 20 6e 6f 20 77 61 6e 74 20 74 68 69 73 20 6d 73 67 00 00
Indication handle = 0x004a value: 62 36 66 33 61 34 37 66 32 30 37 64 33 38 65 31 36 66 66 61
Indication handle = 0x004a value: 62 36 66 33 61 34 37 66 32 30 37 64 33 38 65 31 36 66 66 61
Indication handle = 0x004a value: 62 36 66 33 61 34 37 66 32 30 37 64 33 38 65 31 36 66 66 61

对应的日志

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
38
39
40
41
gatttool[183635]: < ACL Data TX: Handle 71 flags 0x00 dlen 8                                        #481 [hci0] 3972.157187
ATT: Write Request (0x12) len 3
Handle: 0x004a
Data[1]: 02
> HCI Event: Number of Completed Packets (0x13) plen 5 #482 [hci0] 3972.196904
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#481: len 8 (1 Kb/s)
Latency: 39 msec (39-39 msec ~39 msec)
> ACL Data RX: Handle 71 flags 0x02 dlen 5 #483 [hci0] 3972.243389
ATT: Write Response (0x13) len 0
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #484 [hci0] 3972.243405
ATT: Handle Value Indication (0x1d) len 22
Handle: 0x004a
Data[20]: 55206e6f2077616e742074686973206d73670000
gatttool[183635]: < ACL Data TX: Handle 71 flags 0x00 dlen 5 #485 [hci0] 3972.244208
ATT: Handle Value Confirmation (0x1e) len 0
> HCI Event: Number of Completed Packets (0x13) plen 5 #486 [hci0] 3972.276942
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#485: len 5 (1 Kb/s)
Latency: 32 msec (32-39 msec ~36 msec)
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #487 [hci0] 3972.323414
ATT: Handle Value Indication (0x1d) len 22
Handle: 0x004a
Data[20]: 6236663361343766323037643338653136666661
gatttool[183635]: < ACL Data TX: Handle 71 flags 0x00 dlen 5 #488 [hci0] 3972.323538
ATT: Handle Value Confirmation (0x1e) len 0
> HCI Event: Number of Completed Packets (0x13) plen 5 #489 [hci0] 3972.356801
Num handles: 1
Handle: 71 Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Count: 1
#488: len 5 (1 Kb/s)
Latency: 33 msec (32-39 msec ~34 msec)
> ACL Data RX: Handle 71 flags 0x02 dlen 27 #490 [hci0] 3972.411338
ATT: Handle Value Indication (0x1d) len 22
Handle: 0x004a
Data[20]: 6236663361343766323037643338653136666661

解码得到

1
2
3
4
5
6
62 36 66 33 61 34 37 66 32 30 37 64 33 38 65 31 36 66 66 61 

┌──(kali㉿kali)-[~/Desktop]
└─$ echo "6236663361343766323037643338653136666661" | xxd -r -p
b6f3a47f207d38e16ffa

0x15 修改蓝牙mac地址

%% 查看 处理 0x004c 并按照它所说的去做。与以太网或 wifi 设备非常相似,您也可以更改蓝牙设备的 mac 地址。 %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x004c | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Connect with BT MAC address 11:22:33:44:55:66
当有设备连接时,它会检查连接过来的设备的 MAC 地址。只有当连接设备的 MAC 地址正好是 11:22:33:44:55:66 时,它才会认为这个连接是“特殊”的或“授权”的,并触发某个成功的状态(例如,更新某个特征的值,像源代码里处理 MAC 地址匹配的部分)。

这是我自己当前的蓝牙地址

1
2
3
4
5
6
7
8
┌──(kali㉿kali)-[~/Desktop]
└─$ hciconfig
hci0: Type: Primary Bus: USB
BD Address: 84:E0:F4:03:0F:5E ACL MTU: 310:10 SCO MTU: 64:8
UP RUNNING
RX bytes:7079 acl:107 sco:0 events:414 errors:0
TX bytes:6887 acl:85 sco:0 commands:214 errors:0

bdaddr

在这里使用bdaddr来对地址进行修改,会显示安装失败,也没有办法找到它的安装包

1
2
sudo apt-get update
sudo apt-get install bluez-utils

使用bdaddr会简单方便很多,但是它在新版本中好像不存在了
方法如下

1
2
3
sudo hciconfig hci0 down
sudo bdaddr -i hci0 11:22:33:44:55:66
sudo hciconfig hci0 up

btmgmt

这里找到了另外一个工具

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
└─$ sudo btmgmt                     
[mgmt]# help
Menu mgmt:
Available commands:
-------------------
monitor Advertisement Monitor Submenu
select <index> Select a different index
revision Get the MGMT Revision
commands List supported commands
config Show configuration info
info Show controller info
extinfo Show extended controller info
auto-power Power all available features
power <on/off> Toggle powered state
discov <yes/no/limited> [timeout] Toggle discoverable state
connectable <on/off> Toggle connectable state
fast-conn <on/off> Toggle fast connectable state
bondable <on/off> Toggle bondable state
pairable <on/off> Toggle bondable state
linksec <on/off> Toggle link level security
ssp <on/off> Toggle SSP mode
sc <on/off/only> Toggle SC support
hs <on/off> Toggle HS support
le <on/off> Toggle LE support
advertising <on/off> Toggle LE advertising
bredr <on/off> Toggle BR/EDR support
privacy <on/off> [irk] Toggle privacy support
class <major> <minor> Set device major/minor class
disconnect [-t type] <remote address> Disconnect device
con List connections
find [-l|-b] [-L] Discover nearby devices
find-service [-u UUID] [-r RSSI_Threshold] [-l|-b] Discover nearby service
stop-find [-l|-b] Stop discovery
name <name> [shortname] Set local name
pair [-c cap] [-t type] <remote address> Pair with a remote device
cancelpair [-t type] <remote address> Cancel pairing
unpair [-t type] <remote address> Unpair device
keys Load Link Keys
ltks Load Long Term Keys
irks [--local index] [--file file path] Load Identity Resolving Keys
block [-t type] <remote address> Block Device
unblock [-t type] <remote address> Unblock Device
add-uuid <UUID> <service class hint> Add UUID
rm-uuid <UUID> Remove UUID
clr-uuids Clear UUIDs
local-oob Local OOB data
remote-oob [-t <addr_type>] [-r <rand192>] [-h <hash192>] [-R <rand256>] [-H <hash256>] <addr> Remote OOB data
did <source>:<vendor>:<product>:<version> Set Device ID
static-addr <address> Set static address
public-addr <address> Set public address
ext-config <on/off> External configuration
debug-keys <on/off> Toggle debug keys
conn-info [-t type] <remote address> Get connection information
io-cap <cap> Set IO Capability
scan-params <interval> <window> Set Scan Parameters
get-clock [address] Get Clock Information
add-device [-a action] [-t type] <address> Add Device
del-device [-t type] <address> Remove Device
clr-devices Clear Devices
bredr-oob Local OOB data (BR/EDR)
le-oob Local OOB data (LE)
advinfo Show advertising features
advsize [options] <instance_id> Show advertising size info
add-adv [options] <instance_id> Add advertising instance
rm-adv <instance_id> Remove advertising instance
clr-adv Clear advertising instances
add-ext-adv-params [options] <instance_id> Add extended advertising params
add-ext-adv-data [options] <instance_id> Add extended advertising data
appearance <appearance> Set appearance
phy [LE1MTX] [LE1MRX] [LE2MTX] [LE2MRX] [LECODEDTX] [LECODEDRX] [BR1M1SLOT] [BR1M3SLOT] [BR1M5SLOT][EDR2M1SLOT] [EDR2M3SLOT] [EDR2M5SLOT][EDR3M1SLOT] [EDR3M3SLOT] [EDR3M5SLOT] Get/Set PHY Configuration
wbs <on/off> Toggle Wideband-Speech support
secinfo Show security information
expinfo Show experimental features
exp-debug <on/off> Set debug feature
exp-privacy <on/off> Set LL privacy feature
exp-quality <on/off> Set bluetooth quality report feature
exp-offload <on/off> Toggle codec support
read-sysconfig Read System Configuration
set-sysconfig <-v|-h> [options...] Set System Configuration
get-flags [-t type] <address> Get device flags
set-flags [-f flags] [-t type] <address> Set device flags
menu <name> Select submenu
version Display version
quit Quit program
exit Quit program
help Display help about this program
export Print environment variables
script <filename>

使用方法如下

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
# 1. 进入 btmgmt 交互模式
sudo btmgmt

# 2. 选择适配器
select hci0

# 3. 关闭电源
power off
# 等待确认

# 4. 设置静态地址
set-static-addr 11:22:33:44:55:66
# 观察是否报错

# 5. 打开电源
power on
# 等待确认

# 6. 查看信息确认
info
# 检查输出中的 "current settings" 部分,看 public address 或 static address 是否改变
# 注意:'info' 命令可能仍然显示硬件的公共地址,但连接时使用的地址可能已经变成静态地址了。

# 7. 退出
exit

# 8. 尝试连接目标设备
sudo bluetoothctl
connect 64:B7:08:61:B9:7E
# 检查目标设备上的状态是否按预期改变

这段时间的部分日志
从这几分日志可以得到:当前的蓝牙适配器 (hci0, CSR8510 A10) 和其驱动程序组合,不支持通过 btmgmt 工具的 public-addr 或 set-static-addr 命令来伪装 MAC 地址。
btmon

1
2
3
4
5
6
7
8
9
= Close Index: 84:E0:F4:03:0F:5E                                                                         [hci0] 5495.330780
btmgmt[193534]: @ MGMT Command: Set Public Address (0x0039) plen 6 {0x0002} [hci0] 5555.288257
Address: 11:22:33:44:55:66 (OUI 11-22-33)
@ MGMT Event: Command Status (0x0002) plen 3 {0x0002} [hci0] 5555.288272
Set Public Address (0x0039)
Status: Not Supported (0x0c)
btmgmt[193534]: @ MGMT Command: Set Powered (0x0005) plen 1 {0x0002} [hci0] 5574.242334
Powered: Enabled (0x01)
= Open Index: 84:E0:F4:03:0F:5E

btmgmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[mgmt]# select hci0
Selected index 0
[hci0]# power off
[hci0]# hci0 Set Powered complete, settings: bondable ssp br/edr le secure-conn
[hci0]# hci0 class of device changed: 0x000000
[hci0]# set-static-addr 11:22:33:44:55:66
Invalid command in menu mgmt: set-static-addr

Use "help" for a list of available commands in a menu.
Use "menu <submenu>" if you want to enter any submenu.
Use "back" if you want to return to menu main.
[hci0]# public-addr 11:22:33:44:55:66
[hci0]# Set Public Address for hci0 failed with status 0x0c (Not Supported)
[hci0]# power on
[hci0]# hci0 class of device changed: 0x7c0000
[hci0]# hci0 Set Powered complete, settings: powered bondable ssp br/edr le secure-conn
[hci0]# info
[hci0]# hci0: Primary controller
[hci0]# addr 84:E0:F4:03:0F:5E version 6 manufacturer 10 class 0x7c0000
[hci0]# supported settings: powered connectable fast-connectable discoverable bondable link-security ssp br/edr le advertising secure-conn debug-keys privacy static-addr phy-configuration
[hci0]# current settings: powered bondable ssp br/edr le secure-conn
[hci0]# name kali
[hci0]# short name

ifconnfig

来自:
在 Linux 上,您可以使用命令 sudo hciconfig hci0 down 将蓝牙设备关闭,使用 sudo hciconfig hci0 hw ether NEW_MAC_ADDRESS 修改 MAC 地址,再用 sudo hciconfig hci0 up 重新启动设备
这个试了,不行

1
2
3
sudo hciconfig hci0 down
sudo hciconfig hci0 hw ether 11:22:33:44:55:66
sudo hciconfig hci0 up

代码
也不行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import subprocess

def change_bluetooth_mac(new_mac):
# 关闭设备
subprocess.call(["sudo", "hciconfig", "hci0", "down"])

# 修改 MAC 地址
subprocess.call(["sudo", "hciconfig", "hci0", "up"])
subprocess.call(["sudo", "hciconfig", "hci0", "up", new_mac])

# 重新启动设备
subprocess.call(["sudo", "hciconfig", "hci0", "up"])

new_mac = "11:22:33:44:55:66" # 需要替换为新的 MAC 地址
change_bluetooth_mac(new_mac)

物理修改

找到了篇文章:https://zhuanlan.zhihu.com/p/633839213
等新的蓝牙适配器到了就来更新

0x16 设置MTU

%% 阅读 handle 0x0048 并按照它所说的去做。设置 MTU 可能是一项棘手的事情。一些工具可能会提供 mtu 标志,但它们似乎并没有真正触发服务器上的 MTU 协商。尝试使用 gatttool 的交互模式来完成此任务。默认情况下,BLECTF 服务器设置为强制 MTU 大小为 20。服务器将监听 MTU 协商并查看它们,但我们并没有真正更改代码中的 MTU。如果您使用 handle 0x0048 中指定的值触发 MTU 事件,我们只触发标志代码。祝你好运! %%

读取要求
注意这里应该是0x004e

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x004e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Set your connection MTU to 444

  • ATT MTU 协商: MTU 的大小是在连接建立后由客户端和服务器自动协商的。客户端会告知服务器它能支持的最大 MTU,服务器也会告知客户端它能支持的最大 MTU,最终双方会选择一个两者都能接受的最小值作为本次连接实际使用的 MTU。
  • gatttool的交互模式下有**mtu <value> 命令**,可以用来发起 MTU 交换请求。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    ┌───(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x004e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
    Set your connection MTU to 444

    ┌───(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E -I
    [64:B7:08:61:B9:7E][LE]> connect
    Attempting to connect to 64:B7:08:61:B9:7E
    Connection successful
    [64:B7:08:61:B9:7E][LE]> mtu
    Usage: mtu <value>
    [64:B7:08:61:B9:7E][LE]> mtu 444
    MTU was exchanged successfully: 444
    [64:B7:08:61:B9:7E][LE]> char-read-hnd 0x004e
    Characteristic value/descriptor: 62 31 65 34 30 39 65 35 61 34 65 61 66 39 66 65 35 31 35 38
    [64:B7:08:61:B9:7E][LE]>

    ┌───(kali㉿kali)-[~/Desktop]
    └─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x004e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
    b1e409e5a4eaf9fe5158

    得到
    1
    2
    62 31 65 34 30 39 65 35 61 34 65 61 66 39 66 65 35 31 35 38 
    b1e409e5a4eaf9fe5158

使用bluepy库

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
38
39
40
41
42
43
44
45
46
47
48
49
# -*- coding: utf-8 -*-
import sys
from bluepy import btle

DEVICE_ADDRESS = "64:B7:08:61:B9:7E"
ADDR_TYPE = btle.ADDR_TYPE_PUBLIC
REQUESTED_MTU = 444
# 用于检查结果的状态句柄 (从源码看是 0xFF13 的值句柄)
CHECK_HANDLE = 0x004e # IDX_CHAR_FLAG_MTU + 1 (假设)

print(f"Connecting to {DEVICE_ADDRESS}...")
conn = None
try:
conn = btle.Peripheral(DEVICE_ADDRESS, ADDR_TYPE)
print("Connected.")

print(f"Requesting MTU size: {REQUESTED_MTU}")
try:
# 尝试设置 MTU
conn.setMTU(REQUESTED_MTU)
print(f"MTU exchange likely completed (actual MTU depends on server).")
# 注意:setMTU 不会返回实际协商的 MTU,
# Bluepy 没有标准方法直接获取协商后的 MTU。
# 但请求已经发出。

# 等待片刻让设备可能更新状态
import time
time.sleep(1)

# 读取状态句柄检查结果
print(f"Reading check handle {hex(CHECK_HANDLE)} to verify...")
result_value = conn.readCharacteristic(CHECK_HANDLE)
print(f"Value read from check handle: {result_value.hex()} ({result_value!r})")
# 在这里判断 result_value 是否变成了预期的成功值
# 例如,源码中是 "b1e409e5a4eaf9fe5158"
if result_value == b"b1e409e5a4eaf9fe5158":
print("SUCCESS: Check handle value indicates MTU challenge likely completed!")
else:
print("Check handle value did not match expected success value.")

except btle.BTLEException as e:
print(f"Error during MTU request or subsequent read: {e}")

except btle.BTLEException as e:
print(f"Connection failed or Bluetooth error: {e}")
finally:
if conn:
conn.disconnect()
print("Disconnected.")

也成功了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x004e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
Set your connection MTU to 444

┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ sudo python test7.py
Connecting to 64:B7:08:61:B9:7E...
Connected.
Requesting MTU size: 444
MTU exchange likely completed (actual MTU depends on server).
Reading check handle 0x4e to verify...
Value read from check handle: 6231653430396535613465616639666535313538 (b'b1e409e5a4eaf9fe5158')
SUCCESS: Check handle value indicates MTU challenge likely completed!
Disconnected.

┌──(myenv)─(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x004e | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
b1e409e5a4eaf9fe5158

这次没有办法通过重新写入0x004e来改变0x004e的值,可以选择把esp32断电(从电脑拔出)再插回去,它就会初始化了,可以这样来进行多种方法的测试

0x17 写入响应ack信息

%% 查看 处理 0x0050 并按照它所说的去做。此 chalange 与其他写入 chalange 不同,因为执行写入的工具需要正确实现写入响应 ack 消息。这个标志也很棘手,因为即使没有 “NOTIFY” 属性,该标志也会作为通知响应数据返回。 %%

读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0050 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

Write+resp ‘hello’
GATT特征的写入类型与响应机制

  • 写入带响应(Write with Response)
    特征属性中GATT_PROP_WRITE(0x08)表示写入操作需要服务器返回确认响应(ACK),客户端写入后必须等待服务器确认写入成功。
  • 写入无响应(Write without Response)
    属性GATT_PROP_WRITE_NO_RSP(0x04)允许客户端写入后不等待确认,提升效率但不保证写入成功。
    本题强调“写入工具需要正确实现写入响应ACK”,说明目标特征支持带响应的写入,客户端必须处理写入确认包。
    查看help,这次使用–char-write来写
    1
    2
    --char-write                              Characteristics Value Write Without Response (Write Command)
    --char-write-req Characteristics Value Write (Write Request)

操作

1
gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0050 -n $(echo -n "hello"|xxd -ps)

结果

1
2
3
4
5
6
7
8
9
10
11
12
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0050 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
Write+resp 'hello'

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0050 -n $(echo -n "hello"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0050 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
d41d8cd98f00b204e980

0x18 固件 is boss

%% 看看 handle 0x0052。请注意,它没有 notify 属性。在这里写个字,无论如何都要听通知!事情并不总是像看起来那样! %%
读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0052 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

No notifications here! really?
向0x0052写入任意数据然后听0x0052的notify
先检查目标句柄的属性
0000 1010,只有读写两种属性

1
2
3
4
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --characteristics |grep "0x0052"
handle = 0x0051, char properties = 0x0a, char value handle = 0x0052, uuid = 0000ff15-0000-1000-8000-00805f9b34fb

按照0x11的方法继续听notify

1
gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0052 -n 01 --listen

听到了

1
2
3
4
5
6
7
8
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0052 -n 01 --listen
Characteristic value was written successfully
Notification handle = 0x0052 value: 66 63 39 32 30 63 36 38 62 36 30 30 36 31 36 39 34 37 37 62

┌──(kali㉿kali)-[~/Desktop]
└─$ echo "66 63 39 32 30 63 36 38 62 36 30 30 36 31 36 39 34 37 37 62" | tr -d ' ' | xxd -r -p
fc920c68b6006169477b

为什么没有notify属性还是会发送notify呢
这是源码部分
代码会强制发送一个通知 (Notification) (因为 esp_ble_gatts_send_indicate 最后一个参数是 false)。

1
2
3
4
5
6
7
8
9
10
// notify hidden notify flag
if (blectf_handle_table[IDX_CHAR_FLAG_HIDDEN_NOTIFY]+1 == param->write.handle) // 检查是否写入 0x0052
{
// indicate_handle_state = blectf_handle_table[IDX_CHAR_FLAG_HIDDEN_NOTIFY]; // 这行可能是遗留代码或用于其他目的
char notify_data[20] = "fc920c68b6006169477b"; // <-- 要发送的通知数据 (Flag?)
// 重置句柄 0x0052 的值为初始指令
esp_ble_gatts_set_attr_value(blectf_handle_table[IDX_CHAR_FLAG_HIDDEN_NOTIFY]+1, sizeof(hidden_notify_value)-1, (uint8_t *)hidden_notify_value);
// 发送通知!即使属性没声明 Notify,也强制发送
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, blectf_handle_table[IDX_CHAR_VAL_FLAG_HIDDEN_NOTIFY], sizeof(notify_data), (uint8_t *)notify_data, false); // false = Notification
}

为什么可以这样设置,为什么没有notify属性依然能接收到notify信息
固件拥有最终控制权: 蓝牙协议栈(如 ESP-IDF 中的 BlueZ 衍生栈)提供了发送 GATT 操作(如通知、指示、读写响应)的 API 函数(例如 esp_ble_gatts_send_indicate)。设备端的应用程序(固件)代码可以在任何它认为合适的时机调用这些函数。
固件开发者决定了设备的实际行为。 如果开发者编写代码,在收到对句柄 A 的写请求后,就调用函数向客户端发送一个关于句柄 B 的通知,那么设备就会这样做,即使句柄 B 的特征声明中没有 Notify 属性
固件可以不完全遵守自己声明的属性.

0x19 小总结

%% 查看 0x0054 上的所有 handle 属性!四处逛逛,找到你国旗的碎片。 %%
先检查属性

1
2
3
4
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --characteristics |grep "0x0054"
handle = 0x0053, char properties = 0x9b, char value handle = 0x0054, uuid = 0000ff16-0000-1000-8000-00805f9b34fb

0x9b=10011011
对照这个表

属性 十六进制值 二进制位 说明
Broadcast 0x01 0000 0001 广播
Read 0x02 0000 0010 允许读取
Write Without Resp 0x04 0000 0100 允许无响应写入
Write 0x08 0000 1000 允许带响应写入
Notify 0x10 0001 0000 允许通知
Indicate 0x20 0010 0000 允许指示
Authenticated Write 0x40 0100 0000 认证写入
Extended Properties 0x80 1000 0000 扩展属性
那么要关注的属性就是
扩展属性、notify、write、read、broadcast
一个个看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0054 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
So many properties!

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write -a 0x0054 -n 01 --listen
^C

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0054 -n 01 --listen
Characteristic value was written successfully
Notification handle = 0x0054 value: 30 37 65 34 61 30 63 63 34 38
^C

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0054 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
fbb966958f

┌──(kali㉿kali)-[~/Desktop]
└─$ echo "30 37 65 34 61 30 63 63 34 38 " | tr -d ' ' | xxd -r -p
07e4a0cc48

拼接起来就好了fbb966958f07e4a0cc48
这里运用的指令都是前面出现过的,还记得怎么用就行

0x20

%% 找出作者的 twitter 句柄并按照 0x0056 告诉您的去做! %%

读取要求

1
gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0056 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'

作者的twitter是hackgnar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0056 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
md5 of author's twitter handle

┌──(kali㉿kali)-[~/Desktop]
└─$ echo -n "hackgnar" | md5sum
fe40eb2449bda7f9a997331ac09424e7 -

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-write-req -a 0x0056 -n $(echo -n "fe40eb2449bda7f9a997"|xxd -ps)
Characteristic value was written successfully

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0056 | awk -F':' '{print $2}' | tr -d ' ' | xxd -r -p; printf '\n'
fe40eb2449bda7f9a997


但是答案是d953bfb9846acc2e15ee
这个答案只有前20个字符又很难还原最初的是什么

记录报错

安装时报错

直接使用系统的esptool可能会报错(指安装途径为sudo apt-get install esptool)

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
┌──(myenv)─(kali㉿kali)-[~/Desktop/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

esptool.py v4.7.0
Serial port /dev/ttyUSB0
Connecting....
Chip is ESP32-D0WDQ6 (revision v1.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 8c:4f:00:c8:78:60
Traceback (most recent call last):
File "/usr/bin/esptool", line 37, in <module>
esptool._main()
~~~~~~~~~~~~~^^
File "/usr/lib/python3/dist-packages/esptool/__init__.py", line 1139, in _main
main()
~~~~^^
File "/usr/lib/python3/dist-packages/esptool/__init__.py", line 751, in main
esp = esp.run_stub()
File "/usr/lib/python3/dist-packages/esptool/loader.py", line 996, in run_stub
stub = StubFlasher(get_stub_json_path(self.CHIP_NAME))
File "/usr/lib/python3/dist-packages/esptool/loader.py", line 159, in __init__
with open(json_path) as json_file:
~~~~^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/python3/dist-packages/esptool/targets/stub_flasher/stub_flasher_32.json'

安装esptool

1
2
pip install esptool -i https://pypi.tuna.tsinghua.edu.cn/simple

然后从git安装esptool.py

1
git clone https://github.com/espressif/esptool.git

尝试启动esptool.py

1
python '/home/kali/Desktop/esptool/esptool.py' -h   

如果报错

1
2
3
4
5
6
7
8
9
┌──(myenv)─(kali㉿kali)-[~/Desktop/ble_ctf]
└─$ python '/home/kali/Desktop/esptool/esptool.py' -h
Traceback (most recent call last):
File "/home/kali/Desktop/esptool/esptool.py", line 34, in <module>
import esptool
File "/home/kali/Desktop/esptool/esptool/__init__.py", line 41, in <module>
import rich_click as click
ModuleNotFoundError: No module named 'rich_click'

就去安装这个缺少的库

1
pip install rich-click -i https://pypi.tuna.tsinghua.edu.cn/simple

接着就可以直接运行了

1
2
python '/home/kali/Desktop/esptool/esptool.py' -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

效果如下

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
 ┌──(myenv)─(kali㉿kali)-[~/Desktop/ble_ctf]
└─$ python '/home/kali/Desktop/esptool/esptool.py' -h

Usage: esptool.py [OPTIONS] COMMAND [ARGS]...

esptool.py v4.8.1 - serial utility for flashing, provisioning, and interacting with
Espressif SoCs.

╭─ Options ─────────────────────────────────────────────────────────────────────────────────╮
│ --chip -c [auto|esp8266|esp32|esp32s2|esp Target chip type. │
│ 32s3|esp32c3|esp32c2|esp32c6|es │
│ p32c61|esp32c5|esp32h2|esp32h21 │
│ |esp32p4|esp32h4] │
│ --port -p TEXT Serial port device. │
│ --baud -b INTEGER Serial port baud rate used when │
│ flashing/reading. │
│ --port-filter [TEXT] Serial port device filter, can │
│ be vid=NUMBER, pid=NUMBER, │
│ name=SUBSTRING, │
│ serial=SUBSTRING. │
│ --before [default-reset|usb-reset|no-res Which reset to perform before │
│ et|no-reset-no-sync] connecting to the chip. │
│ --after -a [hard-reset|soft-reset|no-reset Which reset to perform after │
│ |no-reset-stub|watchdog-reset] operation is finished. │
│ --no-stub Disable launching the flasher │
│ stub, only talk to ROM │
│ bootloader. Some features will │
│ not be available. │
│ --trace -t Enable trace-level output of │
│ esptool.py interactions. │
│ --override-vddsdio [1.8V|1.9V|OFF] Override ESP32 VDDSDIO internal │
│ voltage regulator (use with │
│ care). │
│ --connect-attempts INTEGER Number of attempts to connect, │
│ negative or 0 for infinite. │
│ Default: 7. │
│ --help -h Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Basic commands ──────────────────────────────────────────────────────────────────────────╮
│ write-flash Write a binary blob to flash. The address is followed by binary │
│ filename, separated by space. │
│ read-flash Read SPI flash memory content. │
│ erase-flash Erase the SPI flash memory. │
│ erase-region Erase a region of the SPI flash memory. │
│ read-mac Print the device MAC address. │
│ flash-id Print the SPI flash memory manufacturer and device ID. │
│ elf2image Create an application image from ELF file │
│ image-info Print information about a firmware image (bootloader or │
│ application). │
│ merge-bin Merge multiple raw binary files into a single flashable file. │
│ version Print esptool version. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Advanced commands ───────────────────────────────────────────────────────────────────────╮
│ verify-flash Verify a binary blob against the flash memory content. │
│ load-ram Download an image to RAM and execute. │
│ dump-mem Dump arbitrary memory to a file. │
│ read-mem Read arbitrary memory location. │
│ write-mem Modify or write to arbitrary memory location. │
│ read-flash-status Read SPI flash memory status register. │
│ write-flash-status Write SPI flash memory status register. │
│ read-flash-sfdp Read SPI flash SFDP (Serial Flash Discoverable Parameters). │
│ get-security-info Print security information report. │
│ chip-id Print the device chip ID. │
│ run Run application code loaded in flash. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯


┌──(myenv)─(kali㉿kali)-[~/Desktop/ble_ctf]
└─$ python '/home/kali/Desktop/esptool/esptool.py' -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


Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' instead.
Warning: Deprecated: Option '--flash_size' is deprecated. Use '--flash-size' instead.
Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' instead.
Warning: Deprecated: Choice 'default_reset' for option '--before' is deprecated. Use 'default-reset' instead.
Warning: Deprecated: Choice 'hard_reset' for option '--after' is deprecated. Use 'hard-reset' instead.
Warning: Deprecated: Command 'write_flash' is deprecated. Use 'write-flash' instead.
esptool.py v4.8.1
Connected to ESP32 on /dev/ttyUSB0:
Chip type: ESP32-D0WDQ6 (revision v1.1)
Features: Wi-Fi, BT, Dual Core + LP Core, 240MHz, Vref calibration in eFuse, Coding Scheme None
Crystal frequency: 40MHz
MAC: 8c:4f:00:c8:78:60

Stub flasher running.
Changing baud rate to 460800...
Changed.

Configuring flash size...
Flash will be erased from 0x00001000 to 0x00007fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x00010000 to 0x000cafff...
SHA digest in image updated.
Wrote 26464 bytes (16501 compressed) at 0x00001000 in 0.7 seconds (299.5 kbit/s).
Hash of data verified.
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (538.0 kbit/s).
Hash of data verified.
Wrote 765904 bytes (452895 compressed) at 0x00010000 in 11.0 seconds (559.3 kbit/s).
Hash of data verified.

Hard resetting via RTS pin...

┌──(myenv)─(kali㉿kali)-[~/Desktop/ble_ctf]
└─$

Connection refused (111)

1
2
3
4
5
6
7
8
9
──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
connect to 64:B7:08:61:B9:7E: Connection refused (111)


┌──(kali㉿kali)-[~/Desktop]
└─$ sudo gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
connect to 64:B7:08:61:B9:7E: Connection refused (111)

先是排查功能

1
2
sudo hciconfig hci0 lestates
sudo hcitool lescan

均正常输出

使用bluetooth进行交互

1
2
3
4
5
6
7
8
#检查 bluetoothd 状态
sudo systemctl status bluetooth
#启动它
sudo systemctl start bluetooth
#重启它
sudo systemctl restart bluetooth
#运行它
sudo bluetoothctl

尝试进行交互

1
2
3
4
5
6
7
8
└─$ sudo bluetoothctl                                       
[bluetooth]# Agent registered
[bluetooth]# connect 64:B7:08:61:B9:7E
Device 64:B7:08:61:B9:7E not available
[bluetooth]# scan on
[bluetooth]# SetDiscoveryFilter success
[bluetooth]# Failed to start discovery: org.bluez.Error.InProgress

在旁边另开一个终端,使用btmon进行底层HCI的查看
得到的部分日志

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
@ MGMT Event: Command Complete (0x0001) plen 263  {0x0001} [hci0] 461.113935
Set Local Name (0x000f) plen 260
Status: Success (0x00)
Name: kali
Short name:
bluetoothctl[24851]: @ MGMT..pen (privileged) version 1.23 {0x0002} 522.145845
bluetoothctl[24851]: @ MGMT Close: bluetoothctl {0x0002} 839.044348
@ MGMT Event: Connect Failed (0x000d) plen 8 {0x0001} [hci0] 850.944326
LE Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Status: Disconnected (0x0e)
@ MGMT Event: Connect Failed (0x000d) plen 8 {0x0001} [hci0] 897.298500
LE Address: 64:B7:08:61:B9:7E (Espressif Inc.)
Status: Disconnected (0x0e)
bluetoothctl[27969]: @ MGMT..pen (privileged) version 1.23 {0x0002} 901.948847
scanbluetoothd[24308]: @ MGMT Comm..d (0x0023) plen 1 {0x0001} [hci0] 919.350036
Address type: 0x07
BR/EDR
LE Public
LE Random
@ MGMT Event: Command Complete (0x0001) plen 4 {0x0001} [hci0] 919.350060
Start Discovery (0x0023) plen 1
Status: Busy (0x0a)
Address type: 0x07
BR/EDR
LE Public
LE Random


给出的解决措施是重启蓝牙服务

1
2
3
4
sudo systemctl stop bluetooth  # Stop the service first
sleep 2 # Give it a second to shut down
sudo systemctl start bluetooth # Start it fresh
sudo systemctl status bluetooth # Verify it's active (running)

还有蓝牙适配器

1
2
sudo hciconfig hci0 down
sudo hciconfig hci0 up

一顿重启后终于成功了

1
2
3
4
5
6
7
8
9
└─$ sudo bluetoothctl
[bluetooth]# Agent registered
[bluetooth]# scan on
[bluetooth]# SetDiscoveryFilter success
[bluetooth]# hci0 type 7 discovering on
[bluetooth]# Discovery started
[bluetooth]# [CHG] Controller 84:E0:F4:03:0F:5E Discovering: yes
[bluetooth]# [NEW] Device 64:B7:08:61:B9:7E BLECTF

Operation not possible due to RF-kill (132)

报错

1
2
3
4
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo hciconfig hci0 up
Can't init device hci0: Operation not possible due to RF-kill (132)

解决

1
2
3
4
5
6
7
#检查蓝牙状况
sudo rfkill list bluetooth
#解除软件阻塞 (Soft blocked):如果显示 "Soft blocked: yes"
sudo rfkill unblock bluetooth
#解除所有类型的阻塞
sudo rfkill unblock all

处理硬件阻塞 (Hard blocked): 如果显示 “Hard blocked: yes”,这意味着有一个物理开关(通常在笔记本电脑的侧面、前面或通过 Fn 组合键)关闭了蓝牙。你需要找到这个开关并物理上打开它。软件命令 (rfkill unblock) 无法解除硬件阻塞。

之后把蓝牙拔了又重新插回去就好了,重启大法妙呀()

Device or resource busy (16)

1
2
3
4
┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
connect to 64:B7:08:61:B9:7E: Device or resource busy (16)

意味着你的蓝牙适配器 (hci0) 已经被另一个应用程序或进程打开并独占使用了
可能是没有完全退出的bluetoothctl或者gatttool服务
进行排查

1
2
3
4
5
6
7
┌──(kali㉿kali)-[~/Desktop]
└─$ ps aux | grep bluetoothctl
kali 202460 0.0 0.1 6520 2184 pts/0 S+ 07:20 0:00 grep --color=auto bluetoothctl

┌──(kali㉿kali)-[~/Desktop]
└─$ ps aux | grep gatttool
kali 202840 0.0 0.1 6520 2336 pts/0 S+ 07:21 0:00 grep --color=auto gatttool

除了 grep 进程本身,没有其他名为 bluetoothctl /gatttool的进程在运行。
所以依然是重启

1
sudo systemctl restart bluetooth

适配器也可以重启

1
2
3
4
sudo hciconfig hci0 down
sudo hciconfig hci0 up
# 检查状态,确保是 UP RUNNING
hciconfig hci0

Device 64:B7:08:61:B9:7E not available

当一切都没有问题只有bluetoothctl有问题时,尝试scan on然后再scan off(记得关掉,因为前台没有显示但是用btmon看后台时它是一直在扫描的),然后再连接就可以了,因为这强制更新了 bluetoothd 的设备状态缓存。

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
38
39
40
41
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo bluetoothctl
[bluetooth]# Agent registered
[bluetooth]# connect 64:B7:08:61:B9:7E
Device 64:B7:08:61:B9:7E not available
[bluetooth]#
[bluetooth]# exit

┌──(kali㉿kali)-[~/Desktop]
└─$ gatttool -b 64:B7:08:61:B9:7E --char-read -a 0x0034|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Write the ascii value "yo" he

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo bluetoothctl
[bluetooth]# Agent registered
[bluetooth]# connect 64:B7:08:61:B9:7E
Device 64:B7:08:61:B9:7E not available
[bluetooth]# scan on
[bluetooth]# SetDiscoveryFilter success
[bluetooth]# hci0 type 7 discovering on
[bluetooth]# Discovery started
[bluetooth]# [CHG] Controller 84:E0:F4:03:0F:5E Discovering: yes
[bluetooth]# [NEW] Device 64:B7:08:61:B9:7E BLECTF
[bluetooth]# [NEW] Device 54:61:CC:20:CD:5B 54-61-CC-20-CD-5B
[bluetooth]# scan off[NEW] Device 50:81:5C:20:B9:E0 50-81-5C-20-B9-E0
[bluetooth]# scan off
[bluetooth]# hci0 type 7 discovering off
[bluetooth]# Discovery stopped
[bluetooth]# [CHG] Device 50:81:5C:20:B9:E0 TxPower is nil
[bluetooth]# [CHG] Device 50:81:5C:20:B9:E0 RSSI is nil
[bluetooth]# [CHG] Device 54:61:CC:20:CD:5B TxPower is nil
[bluetooth]# [CHG] Device 54:61:CC:20:CD:5B RSSI is nil
[bluetooth]# [CHG] Device 64:B7:08:61:B9:7E TxPower is nil
[bluetooth]# [CHG] Device 64:B7:08:61:B9:7E RSSI is nil
[bluetooth]# [CHG] Controller 84:E0:F4:03:0F:5E Discovering: no
[bluetooth]# connect 64:B7:08:61:B9:7E
Attempting to connect to 64:B7:08:61:B9:7E
[bluetooth]# hci0 64:B7:08:61:B9:7E type LE Public connected eir_len 18
[BLECTF]# [CHG] Device 64:B7:08:61:B9:7E Connected: yes
[BLECTF]# Connection successful

Failed to start discovery: org.bluez.Error.InProgress

尝试连接时的显示

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/Desktop]
└─$ sudo bluetoothctl
[bluetooth]# Agent registered
[bluetooth]# connect 64:B7:08:61:B9:7E
Device 64:B7:08:61:B9:7E not available
[bluetooth]# scan on
[bluetooth]# SetDiscoveryFilter success
[bluetooth]# Failed to start discovery: org.bluez.Error.InProgress
[bluetooth]#

对应的btmon日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bluetoothctl[12451]: @ MGMT Open: bluetoothctl (privileged) version 1.23                                 {0x0002} 753.015752
bluetoothd[8979]: @ MGMT Command: Start Discovery (0x0023) plen 1 {0x0001} [hci0] 775.725210
Address type: 0x07
BR/EDR
LE Public
LE Random
@ MGMT Event: Command Complete (0x0001) plen 4 {0x0001} [hci0] 775.725234
Start Discovery (0x0023) plen 1
Status: Busy (0x0a)
Address type: 0x07
BR/EDR
LE Public
LE Random


  • bluetoothd[8979]: @ MGMT Command: Start Discovery (0x0023) …: bluetoothd 确实通过管理接口向 hci0 发送了“启动发现”的命令。

  • @ MGMT Event: Command Complete (0x0001) … Start Discovery (0x0023) … Status: Busy (0x0a): 这是关键。 蓝牙适配器 (hci0) 通过管理接口回复说,启动发现命令失败,因为适配器当前状态是 **Busy (忙碌,状态码 0x0a)**。

措施依然是重启

参考资料

靶场:https://github.com/hackgnar/ble_ctf
蓝牙知识扩展:
https://www.bluetooth.com/
https://github.com/Eronwu/Getting-Started-with-Bluetooth-Low-Energy-in-Chinese
https://github.com/pauloborges/bluez/blob/master/attrib/gatttool.c
https://community.nxp.com/t5/Wireless-Connectivity-Knowledge/Indication-and-Notification/ta-p/1129270
https://blog.csdn.net/qq_36347513/article/details/118762746

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
总字数 78k 访客数