The other day, I impulsively bought a cheap chinese RGB “smart” light bulb. I figured it would be a fun toy to play with and also an easy Bluetooth Low Energy reversing target practice.

Sniffing the communication

As the vendor provides an Android app, sniffing the communication is trivial — just enable the “Bluetooth HCI snoop log” in Developer Options and btsnoop_hci.log should magically appear on a phone-dependent location on your file system. This file can then be opened in Wireshark which comes with all the needed Bluetooth dissectors included.

Contents

Fiddling around in the application for a bit and then opening the Bluetooth log reveals some easy targets (don’t forget to filter only GATT messages and the relevant address).

These are obviously RGB values being written to the device (the first byte later turned out to be an additional warm-white brightness channel).

The device also exposes several obvious (or even standard) characteristics. There is even a battery characteristic for whatever reason.

Manually writing characteristics

Characteristics can be manually written to using bluetoothctl, as follows:

[bluetooth]# connect 31:C2:4B:19:AC:E6
Attempting to connect to 31:C2:4B:19:AC:E6
[CHG] Device 31:C2:4B:19:AC:E6 Connected: yes
Connection successful
[CHG] Device 31:C2:4B:19:AC:E6 ServicesResolved: yes
[PLAYBULB smart bulb]# select-attribute 0000fffc-0000-1000-8000-00805f9b34fb
[PLAYBULB smart bulb:/service0016/char0024]# write 00 00 255 00
Attempting to write /org/bluez/hci0/dev_31_C2_4B_19_AC_E6/service0016/char0024

Intermezzo: Characteristic UUID from the handle

Note that the logs show something called “handle” for the write/read commands. Usually, we want to refer to the characteristic by its UUID, as the handle could potentially change in between software versions.

This can easily be accomplished using the gatttool utility which comes with BlueZ.

[31:C2:4B:19:AC:E6][LE]> connect
Attempting to connect to 31:C2:4B:19:AC:E6
Connection successful
[31:C2:4B:19:AC:E6][LE]> char-desc
handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0002, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x0004, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0005, uuid: 00002800-0000-1000-8000-00805f9b34fb
...

Security considerations

Notice that nowhere did the light bulb require any sort of authentication/pairing. There is a PIN characteristic, which can be configured in the vendor application.

However, as far as I can tell, the PIN only gets read back and checked by the application itself and therefore provides only a semblance of protection. Welp.

Python library

I have (partially, there are still unknown characteristics) implemented the protocol as a Python library (which is a thin wrapper around BlueZ DBus interface). The library also includes a command line tool and an MQTT bridge.