Background:
Whie looking for Bluetooth learning materials online targeting Raspberry Pi with BlueZ stack, I came across an interesting project on Github called: InternalBlue. It is a Bluetooth research framework targeting Broadcom chips (Cypress later and now Infenion). The framework allows interaction with Bluetooth low level layers below HCI (Hardware Controller Interface). This opens alot of possiblities to get a deeper understanding on how Bluetooth controller work under the hood and also perform security / performance analysis on off-the-shelf Bluetooth devices.
Goal and Plan:
My goal was to check if the InternalBlue framework would actually work on the Raspberry Pi device as Pi models such as: Zero, 3, 3+ and 4 have built-in Broadcom (Cypress) chips by default. While the docuemntation says that Linux with BlueZ stack is supported, I could not really find a reference example verifying that it works on the Pi. There were some open Github issues discssing the topic and some suggested adaptions which were not verified.
So my plan in summary was to take to take a deeper look into the Raspberry Pi use case to see if I can get it to work with the documented provided by InternalBlue developers. I picked a Raspberry Pi 3B and flashed it with a "very old" Raspbian "stretch" image to be at the same point of time when the project was active and increase my chances of success.
How InternalBlue Work:
Before jumping into the tehnical steps of this experiemnt, I wanted to summarize the "theory of operation" of InternalBlue. The framework is based around the ability to patch the firmware running in RAM on certain Broadom (Cypress) chips. By doing this, addiotnal functions could be introduced in the firmware and existing implementation could be modified. Through reverse engineering of the Bluetooth firmware, developers were able to enable monitoring and injection of LMP (Link Manager Protocol) packets. This is a very powerful research feature as LMP is located below HCI and gives insights on the Bluetooth controller behavior that could not be easily extracted wihtout the usage of expensive Bluetooth sniffers. The reasearchers utilized this to test couple of known Bluetooth bugs and security attacks.
InternalBlue Platform Overview (Credits: Jiska Classen, Secure Mobile Networking Lab) |
Raspberry Pi Setup:
I used an old Raspberry Pi 3B model to conduct my first experiment with InternalBlue. It has a built-in Broadcom (Now Cypress) Bluetooth/Wifi chip (Part number: BCM43438 / CYW43438). The Bluetooth controller supports both Bluetooth Classic and Bluetooth Low Energy.
On the software side, I flashed a pretty old linux image (Raspbian Stretch released April 2019) which has Linux kernel version 4.14.xx. It also has already BlueZ installed (version 5.43). The reason I went with such old image is becuase I was not sure if the stuff from InternalBlue still apply today to latest lernel/BlueZ and Broadcom Bluetooth firmware. Therfore, I travelled back in time to such old configuration when the InternalBlue project was under active development.
According to kernel boot logs, The Bluetooth firmware (.hcd file) that gets loaded into the controller is: BCM43430A1 and is listed as one of the supported firmware files by the InternalBlue framework. I have not made any changes to this file and used the same version that got shipped with BlueZ.
Enable Broadcom Diagnostics Logs:
According to InternalBlue researchers, Broadcom chips have an undocumented diagnostic logging protocol that allows the forward of Bluetooth messages from the Baseband / Link Layer via HCI to the host. This was alredy a big important feature for me to test how Bluetooth pairing and connection takes place in these layers against different Bluetooth capable IOT devices. Anyway, after going through the documentation and couple of blog posts online I understood that there are two ways to enable this: The simplest one is to write the option flag provided by kernel/BlueZ: echo 1 >sys/kernel/debug/bluetooth/hci0/vendor_diag. Unfortanetly this failed on the Raspberry Pi with the built-in controller (exposed over UART). The vendor_diag option was missing and could not be found in the above directory.
After reading couple of Github issues on InternalBlue page, I understood that BlueZ has some "difficulties" identifying Broadcom chips when conncted via UART vs USB. I was not able to verify this and did not want to spend alot of time digging into BlueZ source code.
The second option I came across is to write a vendor-specific HCI command to enable the diagnostic logs. The comand syntax provide by InternalBlue is this one:
I got an error when trying to send this command via HCI tool (Unknow commnd). I believe that is because BlueZ does not support BCM_DIAG (0x07) messages in this case. I tried then to send the command directly via the Bluetooth scoket (see code snippet below). Here I got no errors but also did not see any response in BlueZ btmon tool I believe also for the same reason above.
As I was hopeless that is not gonna work I came accross this Github issue which suggested that a kernel patch is needed such that the kernel can forward H4 BCM_DIAG messages and not filter it out. I decided to push forward and rebuild the linux kernel of the Raspberry Pi with the provided diff here.
Linux Kernel Rebuild:
This was the most ciritcal step of this experiment as I was not sure if my Pi would still boot after the kernel patch / rebuild and Bluetooth will still be functioning. The steps to apply the patch and rebuild the kernel were nevertheless straightforward. The kernel buid took around 2 hours on the Raspberry Pi 3B model.
Here are the steps:
- Clone the kernel source code
cd linux
- Download and apply the diff via Git:
git apply -R path/to/your_file.diff
- Configure and Build:
make bcm2709_defconfig
make -j4 zImage modules dtbs
- Install the kernel:
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm/boot/zImage /boot/kernel-ib.img
- Switch to installed kernel and reboot:
Add this line at the end: kernel=kernel-ib.img
After rebooting the Pi, I was happy that everything is still working including Bluetooth :). In addition, it seems that Bluetooth diagnostic messages are now being forwarded by the kernel as shown in the logs. However, HCI diagnostic messages (type 0x07) are still not visible in "btmon". Most likely BlueZ filters them out and we need to read the raw via Wireshark.
Running InternalBlue:
InternalBlue framework comes in the form of a Python command line tool. You need (Python >3.6) and I nedded to run the it with sudo otherwise sending commnds to the Bluetooth interface does not work.
I read the firmware build info from the chip by performing a memory dump at address 0x200400 and is listed under known firmware versions by the framework
Also reading the patchram region works !
Sending lmp commands gnerated warnings that it is not a supported feature on my setup. However, When checking the HCI btmon /dmesg logs. It seems that a Vendor HCI commad was sucessfully sent and also dmesg prints that a diagnostic packet was recieved. Since I am not able to see H4 diagnostic packets in btmon. I had to use the h4bcm_wireshark_dissector plugin used also inside InternalBlue framework to read the diagnostic packets as we will see later on .
Monitor LMP Packets with Wireshark :
The next step was to install Wireshark with the h4 dissctor plugin to be able to decode forwarded diagnostic packets. You need to use Wireshark 3.x version and build/install the h4bcm plugin from source. After installation I was able to start Wireshark capture session from InternalBlue command line.
To demonstrate the capture of diagnostic packets, I started a pairing session with my Bluetooth classic headphones. As you can see from the picture below, LMP packets indeed appear along with the standard HCI traffic. We can clearly see the SDP paring process steps on HCI and Link layers !
Next, I tried to sendlmp command op code 1 from InternalBlue commandline with offset zero. This op code corresponds to name request on the link layer and our remote device should then reply with "LMP_name_res" containg the first sgement of the name with offset zero as shown below:
Conclusion:
In summary, I managed to reproduce the InternalBlue working concept on Raspberry Pi as there were no published attempts of doing so. We survived a kernel rebuild and managed to monitor and inject LMP stuff. In the future, I might try to test this on later versions of Raspberry Pi and BlueZ. Until then happy Bluetooth experimenting with this soluion ! ::)







0 comments:
Post a Comment