Hacking Bluetooth the Easy way with ESP32 HCI Commands and hidden features
We already have a Bluetooth security methodology for guidance, but there is still a lack of tools to audit and test Bluetooth security
After spending a long time performing Bluetooth assessments, in 2021, due to the lack of information and centralized documentation resources, we decided to start a research line on Bluetooth.
After the first year, we published our initial attack called BlueTrust which enables the establishment of trust relationships between Bluetooth devices, even if they are not in the same location.
This period also made us realize the need to compile available information on Bluetooth security, which culminated in BSAM, a free and open framework for Bluetooth, like OWASP, that simplifies auditing a Bluetooth device.
After another year of research, we still felt that something was missing…
To put this in perspective, 30 attacks or news (https://darkmentor.com/bt.html) related to Bluetooth security have been published in 2024. Of these, only 7 have public proofs of concept. Of these 7, only 3 can be implemented with standard hardware, and only 1 is compatible with multiple operating systems.
If we investigate further back in time and not only look at 2024, focusing on existing tools, a quick glance at GitHub reveals quite some tools related to Bluetooth and Bluetooth security.
There are many tools, but most are either unmaintained or have many unresolved issues and pull requests, and are generally obsolete (often over three years old), making them difficult to compile or run with current APIs, libraries, and SDK versions.
If we focus on hardware, we have multipurpose hardware such as Flipper Zero, M5Stack, CapibaraZero, with a varied landscape.
This multipurpose hardware designed for security assessments – not only for Bluetooth – presents too many options and does not meet all market needs; for instance, it lacks support for low-level functionalities, and the available Bluetooth attacks are very basic (e.g. merely spamming advertisement packets).
When considering more generic hardware such as USB dongles or PC sniffers, the situation does not improve. There are still too many options – which depend on the chip model used – and many devices are expensive and cater to very different requirements.
To develop a Bluetooth security and auditing tool there are not many options.
On one hand, high-level APIs limit functionality to Bluetooth services and ignore low-level processes such as pairing or discovery.
On the other hand, low-level APIs depend on the operating system’s support. Additionally, these APIs sanitize parts of the input data, limiting available actions to those specified by the standard, and they are inconsistent across operating systems. This results in varying outcomes depending on the OS used, due to different implementations in the underlying stack or framework.
Finally, since each operating system has its own stack, it becomes infeasible to develop on one platform and easily port the tool to another.
We already have a guide to follow to audit a Bluetooth device in detail, but a common framework is needed to simplify the development of tools:
- It should not depend on the operating system.
- It should allow the use of any hardware, ensuring hardware independence.
- It should be able to be used independently of the programming language.
- It should be simple enough that you only need to focus on the 4000+ Bluetooth specifications.
From the points above, it seems that we need some universal way to be able to interact with Bluetooth hardware.
Let’s briefly review how Bluetooth works at a high level.
Bluetooth physical architecture consists of two main components: the host, which is the software that manages high level connections and data, and the controller, which is the chip that runs firmware and manages the wireless packets in real-time.
To simplify, let’s say that the host is your computer that performs high-level actions, and it communicates with the controller – the chip responsible for sending packets, maintaining connections, and so on.
The host software and the controller communicate via the HCI protocol, with protocols encapsulated and nested within packets. This standard protocol is OS-independent and operates over USB and UART/Serial interfaces.
With this knowledge in mind, it seems that what is needed is an HCI driver that can be run anywhere and can be used from any programming language.
The resulting tool is USB Bluetooth.
It’s an HCI driver written in C, and is both platform-independent and hardware-independent. This driver is released as open source, making it accessible to everyone.
The driver operates by directly accessing the Bluetooth hardware using WinUSB and libusb.Additionally, it provides bindings for Python and C# to extend support to other programming languages, thereby facilitating tool development.
For Python, we also publish a package that allows you to use USB Bluetooth in Scapy making the crafting and interpretation of Bluetooth packets easier.
Now we have a platform independent driver, with support for several languages and that allows us to interact with our hardware without restrictions and without interference from the operating system.
This driver enables us to implement tools that utilize the controller’s standard functionality, such as device scanners or tools for automating connection and pairing tests.
It also allows us to build tools that require the use of malformed HCI packets such as fuzzers, since this driver does not check the integrity of the HCI packets that are sent to the Bluetooth controller.
Among this driver’s limitations are the inability to bypass standard controller behavior and the need for specific hardware to implement advanced attacks that require such capability.
It is also important to note that many Bluetooth controllers integrated in laptops include both WiFi and Bluetooth connectivity, and thus, if we use this integrated adapter, we are likely to lose WiFi connectivity. Therefore, the use of external hardware with this driver is recommended.
Which Bluetooth hardware should be used?
Now that we seem to have solved the issue of how to develop software, what can be done with the hardware
Ideally, the hardware should be:
- Is available and does not run out of stock easily, not like with development boards.
- Can be purchased in any country.
- It should be cheap.
- If possible, that it supports both Classic and Low Energy.
If we revisit the multipurpose hardware mentioned earlier – used for wireless Bluetooth and WiFi hacking (with the exception of Flipper Zero) – we see that most tools are developed using the ESP32 chip, with projects such as ESP32Marauder, Ghost ESP, Bruce, and many more.
There are also many boards that utilize this chip, both for custom hardware development and as ready-to-use devices with a USB connector, offered at very low prices and available almost everywhere.
The cost is around a couple of euros for a chip that supports Bluetooth Classic, LE, WiFi and in some cases ZigBee, so it is not surprising that it is used in so many projects. For this reason, we thought it was a good candidate to see if it could be used as advanced hardware and be able to implement tools that use the Bluetooth protocol at a lower level.
So, let’s analyze what can be done today at the Bluetooth level with the ESP32, how existing projects utilize it, and identify what can be improved.
Usually, custom firmware builds must be flashed onto a device. These specialized builds focus on a single function, often rendering the device incompatible with other functionalities – for example, serving as a generic Bluetooth adapter for a PC at the same time Programming knowledge is required, and the complexity of developing patches can affect firmware stability, especially since it is provided only in binary format.
Over the past year, we have undertaken a reverse engineering effort on the ESP firmware, focusing on the Bluetooth radio. To achieve our goal, we collected code for analysis.
Firstly, Espressif provides the internal ROM binaries of the device in ELF format. This facilitates the reverse engineering process since the files even include some symbol names. Also, libraries related to Bluetooth or wireless communications available in the SDK are compiled in binary format at https://github.com/espressif/esp32-bt-lib/ and https://github.com/espressif/esp-phy-lib/.
For library analysis, we chose to generate a ‘golden binary’ by unifying them into a single file, avoiding separate analyses and the need to define relationships between files. For ESP32 applications to call the chip’s internal ROM functions, the compiler must know their locations.
PS C:\> xtensa-esp32-elf-ld -r -o golden_bin.elf --whole-archive .\libbtdm_app.a .\libbttestmode.a .\libphy.a .\librftest.a .\librtc.a PS C:\> ls .\golden_bin.elf Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 25/02/2025 9:17 4156284 golden_bin.elf
For ESP32 applications to call the chip’s internal ROM functions, the compiler needs to know where these functions are located. For this purpose, Espressif publishes linker scripts that contain lists of symbols and their addresses. This information can greatly simplify the engineering process. “A Ghidra plugin that allows importing this information into the files for analysis will be published.
Finally, it should be noted that much of the code analyzed performs reads and writes from static memory regions of the chip. These addresses are used to control the peripherals, components of the chip that allow us to interact with the outside world and exchange information with other chips.
The ESP32 datasheet contains information about these memory regions, but this information is not very detailed and is also very tedious to incorporate by hand into reverse engineering programs. Fortunately, Espressif also publishes SVD files documenting just this at https://github.com/espressif/svd and https://github.com/espressif/svd. SVD is an XML-based standard that was originally conceived for ARM but has become widely used to document chip information so that other tools can generate code or facilitate debugging tasks Ghidra does not natively support importing this type of file, but there are plugins that allow you to incorporate this information.
Once all this information has been collected, we proceed to analyze the files. We find that the ESP32 is programmed with FreeRTOS. Following the initialization calls in the Bluetooth code, we discovered that it is not the only RTOS running on this device.
More specifically, following the Bluetooth initialization functions we encounter a function called ‘RWIP_INIT’. The prefix “RW” in many functions indicates that some of the hardware may have been developed by RivieraWaves so this RTOS/Kernel may also have that origin.
Continuing our analysis, within the same function we found a reference to the H4 Transport layer. It is a term that was used in early versions of the Bluetooth standard to refer to HCI.
Examining the ‘H4 INIT’ function in detail, it sets a couple of callbacks that are executed when HCI packets are received. By analyzing the callbacks, we identified a function that queries a memory table to determine what should be executed for each HCI packet.
The command table contains sub-tables separated by a field called OGF (Opcode Group Field) documented in the Bluetooth standard. As an example, group 1 corresponds to the “Link Control” section of the Bluetooth spec.
The last entry of the table refers to group 0x3f, which is a group reserved for manufacturer proprietary commands, of which there is no record in the documentation or in previous publications.
Navigating to the vendor table we find 29 commands not documented anywhere. We discover the opcodes of each of these 29 hidden commands and what each of them is used for.
Opcode | Command |
OxFC01 | Read memory |
OxFC02 | Write memory |
OxFC03 | Delete NVDS parameter |
OxFC05 | Get flash ID |
OxFC06 | Erase flash |
OxFC07 | Write flash |
OxFC08 | Read flash |
OxFC09 | Read NVDS parameter |
OxFC0A | Write NVDS parameter |
OxFC0B | Enable/disable coexistence |
OxFC0E | Send LMP packet |
OxFC10 | Read kernel stats |
OxFC11 | Platform reset |
OxFC12 | Read memory info |
OxFC30 | Register read |
OxFC31 | Register write |
OxFC32 | Set MAC address |
OxFC35 | Set CRC initial value |
OxFC36 | LLCP msgs discard |
OxFC37 | Reset RX count |
OxFC38 | Reset TX count |
OxFC39 | RF register read (Not implemented) |
OxFC3A | RF register write (Not implemented) |
OxFC3B | Set TX password |
OxFC40 | Set LE parameters |
OxFC41 | Write LE default values |
OxFC42 | LLCP pass through enable |
OxFC43 | Send LLCP packet |
OxFC44 | LMP msgs discard |
The commands enable everything we were looking for. They allow actions such as reading CPU registers, sending packets at various Bluetooth layers, reading proprietary kernel statistics, changing the device MAC, and much more.
What is all this good for?
Now we can control the ESP32 hardware in unanticipated ways, all by using code provided by Espressif, without the need to customize the firmware or apply patches that can be complicated to maintain or manage.
Specifically, the ability to change the device’s MAC stands out. Despite its simplicity, it offers significant versatility by expanding the attack surface and allowing us to impersonate other devices.
For the advanced attacks, instead of customizing the firmware, we can now make use of commands that allow us to access Bluetooth low-level protocols.
From a cybersecurity perspective, what applications do these ESP32 findings have?
Sometimes, the ESP32 is used solely as a communications module, with the device’s logic managed by another processor (often ARM), while the ESP32 serves only as a Bluetooth and WiFi communications chip.
In this case, among other capabilities, we can change the MAC and access the ESP32 memory (both read and write) through HCI commands. This also grants access to Bluetooth low-level traffic, enabling attacks that require advanced chip functionality.
With this in mind, for any device on which we can send HCI commands we can implement advanced Bluetooth attacks, i.e. any IOT device with ESP32 is a potential device from which to perform attacks.
If we can execute code on an IoT device that uses the ESP32 as its communications module—due to a security flaw compromising the device—then, using HCI commands, code can be stored in the ESP32 memory and executed without the main processor’s knowledge. Moreover, this modification can be persistent. This means that by gaining legitimate access to an IOT device that uses the ESP32 as a communications module via HCI, it is possible to achieve persistence, execute code, perform Bluetooth attacks, track devices and exfiltrate information via Bluetooth or WiFi.
While this is not a remote Bluetooth vulnerability, these hidden firmware functionalities can be used to inject persistent code that carries out malicious activities on any device using the ESP32 as its HCI communications chip.
Conclusion
Currently, the following are available:
- A methodology that serves as a guide to follow when assessing the security of a Bluetooth device.
- A way to develop software that is easy to port and allows you to use your favorite programming language.
- Integrations with libraries such as Scapy.
- Inexpensive hardware that enables us to implement existing attacks and serves as a foundation for further tool development.
Additionally, we have demonstrated how to use these hidden commands to unlock advanced functionalities, enabling direct and complete access to the ESP32’s memory and execution from any hardware that utilizes this chip via HCI. Technical articles with full documentation, code and examples will be published in the coming days.