This project started with the intention of exploiting vulnerabilities in BLE door locks to bypass access codes. We planned to do this using the CatSniffer board, since it supports many IoT protocols, including BLE (Bluetooth Low Energy).
Likewise, we mainly tried looking for cheap Chinese door locks that could probably have security issues. Since there was not much information online about door locks with known security issues, we thought about just buying a bunch and testing all of them.
After researching many door locks, we realized that most of them would only use BLE for initial pairing. Afterward, most switch to Wi-Fi for the actual communication. This would make it almost impossible to open the door lock once it has been configured.
After this, the focus of the project was changed, however, the part about sending packages for the door lock project was not discarded, but we decided to start with an example of sending packages.
SniffleSniffle is a BLE project that focuses on sniffing Bluetooth 5 and 4.x. Recently they have been adding more support for advertising. This piqued our interest since this project is compatible with the CatSniffer. The project already has a Python script that advertises a default package with a default device name. We wanted to embed this so it could work on the CatSniffer without the use of a host computer.
Another factor that motivated us to work with the Sniffle project was that recently the CatSniffer was added as an officially supported board. Before that, we had to compile the project ourselves to use it on the CatSniffer.
Now we can either compile the project directly or use our Catnip tool and get the latest release directly.
The Sniffle firmware is used by sending commands using Python scripts from the computer to a compatible TI CC1352/CC26x2 MCU. The Python scripts run on the computer and send UART commands to the MCU running the Sniffle firmware. We wanted to find a way to do all this directly on the board. The CatSniffer has a TI CC1352 and an RP2040. To achieve this we decided to code on the RP2040 with Arduino IDE to send the commands to the CC1352. The RP2040 communicates with the PC using Serial0 and with the CC1352 using Serial1.
Python ScriptsAnalyzing the Python scripts that send the commands we notice there are a few different scripts for different tasks, advertiser.py is chosen for our needs.
The Python code is relatively simple. First, it configures different parameters for the BLE communication.
The important part of the code comes afterward. The advertising and scan response data are defined in this section of the code. Then the cmd_advertise function is used to process the advertising scan response data and enter advertiser mode.
Using print statements, we checked how the information was formatted and generated.
The main section of the Python code is a program called sniffle_hw.py. All the other scripts that run on the terminal communicate with this one. Then that program sends the UART messages to the C code on the CC1352.
The cmd_advertise function makes a padded version of the advertise and scan response data. The packets need to be 31 bytes, so they are checked and padded with 0s if they are shorter. If the packets are longer, they are truncated. This function also checks for errors in the mode. We kept checking the data using print statements.
After padding the data, it is sent to the C code using the send_cmd. The send command is shared by all the other scripts and functions to communicate with the C code.
The send command builds the message by combining the two packets and adding their lengths. Finally, the message is encoded in base64 and sent via UART.
RP2040 CodeWe decided to jump right in and try to make the same code but using Arduino, testing with an Arduino RP2040 replicating the entire process of creating the message step by step as the Python script does. Many parts were hardcoded to avoid the complexity of the code for now, as it is a test.
First, we added some necessary libraries, created the data to be sent and the size variables of the arrays, and finally the definitions of the advertise and send functions. We also added the Serial Pass Through header file from our repository for some Cat Sniffer pin definitions.
In the setup, the code initializes the serial channels, waits for the serial monitor to be opened, and sets some necessary pins for communication. Then it prints some values to the serial to be able to compare them. Finally, the Advertise function creates the rest of the message.
In the advertise function the code pads the data, checks that they have the correct length, and calls the "cmd send function" from this function to send the data. It passes the mode and the data already with 0s as arguments.
In the send function, we create the cmd variable, the message, and b0. In this part, the code becomes a bit long because you have to get the length of the arrays and add them as their first value of the array, which is a bit tedious to do in C.
Finally, the final message is encoded in base64 and sent over serial 1.
Although this code worked correctly to create the data that was going to be sent, it did not work to establish communication with the C code of Sniffle.
It was never possible to get the code to respond to the advertisement that was sent to it. Since this did not work, we tried something much simpler and directly sent the already base64 encoded data frame that sniffle would send. This also did not work.
We also tried with another of the Sniffle Python scripts, the reset one, and had similar results.
The Python arrays as the ones sent were replicated.
In the code, we created a function to send a sync command and then reset commands 5 times in the same way as the Python script.
Unfortunately, the board would not do anything no matter what we were sending.
Bus PirateSince sending the values wasn't working, we had to find a way to see the data frame bit by bit. Since it was a digital signal, we thought of using a logic analyzer. We decided to go with the Bus Pirate since we had one lying around. Installing the tools to use the Bus Pirate turned out to be a challenge in itself. There was a bug on Windows that we had to report to get the software working. We reported that bug on the Bus Pirate forum, and they patched it, but this took a few days. The bug mainly affected the Pulse View tool that Bus Pirate uses to display the data graphically and be able to decode it, which was exactly what we needed to do. The Bus Pirate team was really nice and helped us out very quickly.
Once the bug was fixed, we ran some tests with the program using an Arduino to learn how to use it and successfully decoded UART communication between two Arduinos.
After this, the hope came back! due we would be able to see the bits on the frame. We tried the same thing but using Sniffle on the Launchpad CC1352P7-4, but this revealed another problem.
Most of the data seen with the Bus Pirate from Sniffle came out incomplete or with errors, this was because the maximum sampling rate of the Bus Pirate was 135 ks/s and the Sniffle firmware runs at 1M or 2M. Trying to change the speed of the firmware but lowering it below 1M caused it to break. For these reasons, we decided to use an oscilloscope instead.
OscilloscopeFirstly we had some trouble configuring the trigger on the oscilloscope to start measuring, newbie(not that newbie) issue. But once we had the correct configuration, it was simply a matter of connecting the Tx and Rx pins to the probes and running the Python scripts to see the data sent and received.
First, the message sent was verified using the Python script and the response from the CC1352, using the Launchpad directly. The Launchpad has a CC1352 as the CatSniffer and another MCU that can, for our purpose, be understood as an FTDI.
Now that we could be sure that the message was correctly sent. We decided to try again, this time making a function to listen for a response over serial.
After that the Arduino Nano RP2040 was connected directly to the Launchpad, disconnecting the FTDI, to then sent the commands directly to the CC1352 and checked the response again. At this point, we confirmed that the response in both cases were the same.
This confirmed that it was possible to send external commands to the firmware.
Final codeWe decided to go ahead and go back to the original Arduino code. Making some changes with what we had learned. Mainly, there were many small errors in how the commands were being made. At this point, the best we could do was visually compare the message from our code and the Python script.
Once every step was the same, we decided to go ahead and test it on the CatSniffer, going ahead and loading the Sniffle firmware to the CC1352 and our custom advertise firmware (linked in the code section) to the RP2040. To test, we used nRF Connect on our phones to check if we could see the packet we were sending. And there it was, after scanning for BLE devices we could see one listed "CatSniffer" with the same packet we had defined on our code.
Working with code that isn't originally ours presented several challenges, especially adapting it and understanding its underlying logic. However, this process greatly enhanced our understanding of how BLE communication works, especially when it comes to packet construction and transmission.
There are still many things missing from our code. The next step is to focus on implementing the scan response functionality and establishing a reliable device connection. This will allow for more comprehensive testing and greater control over the BLE communication process, ultimately moving closer to achieving the original goals of the project.
As an open-source company, we strongly believe in the power of community collaboration. If you're passionate about BLE technology, embedded systems, or security research, and are interested in contributing to this project, we warmly welcome your involvement! Your expertise and enthusiasm would be invaluable as we continue to explore the exciting possibilities of BLE communication. You can check our GitHub for the CatSniffer here.
Comments
Please log in or sign up to comment.