Voltcraft SEM-3600BT, Who needs security?

I recently bought a Voltcraft SEM-3600BT(OEM of Colour HK EM200 WiTmeter) to monitor the energy consumption of one of my servers and be able to remote power cycle it. The device can be read out and controlled through Bluetooth 4.0 using an Android app. Obviously I didn’t want to use the app but control the device from Linux. So I did some poking around on it, and found that the concept of security is apparently unknown to the designers of this device.

(As I later found the following site also some reversing info on this device: http://wiki.volkszaehler.org/hardware/channels/meters/power/wittech_witenergy_e100)

Querying the device

The device uses standard Bluetooth LE/4.0, which I’ll won’t explain here. But I started by making a dump of all characteristics in the GATT using gatttool. This gave me the following:

# gatttool -b <MAC> --char-desc
handle = 0x0001, uuid = 00002800-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.primary_service_declaration)
handle = 0x0002, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0003, uuid = 00002a00-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.gap.device_name)
handle = 0x0004, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0005, uuid = 00002a01-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.gap.appearance)
handle = 0x0006, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0007, uuid = 00002a02-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.gap.peripheral_privacy_flag)
handle = 0x0008, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0009, uuid = 00002a03-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.gap.reconnection_address)
handle = 0x000a, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x000b, uuid = 00002a04-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.gap.peripheral_preferred_connection_parameters)
handle = 0x000c, uuid = 00002800-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.primary_service_declaration)
handle = 0x000d, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x000e, uuid = 00002a05-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.gatt.service_changed)
handle = 0x000f, uuid = 00002902-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.client_characteristic_configuration)
handle = 0x0010, uuid = 00002800-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.primary_service_declaration)
handle = 0x0011, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0012, uuid = 0000fee1-494c-4f47-4943-544543480000
handle = 0x0013, uuid = 00002902-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.client_characteristic_configuration)
handle = 0x0014, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0015, uuid = 0000fee2-494c-4f47-4943-544543480000
handle = 0x0016, uuid = 00002902-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.client_characteristic_configuration)
handle = 0x0017, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0018, uuid = 0000fee3-494c-4f47-4943-544543480000
handle = 0x0019, uuid = 00002902-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.client_characteristic_configuration)
handle = 0x001a, uuid = 00002800-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.primary_service_declaration)
handle = 0x001b, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x001c, uuid = 00002a23-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.system_id)
handle = 0x001d, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x001e, uuid = 00002a24-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.model_number_string)
handle = 0x001f, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0020, uuid = 00002a25-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.serial_number_string)
handle = 0x0021, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0022, uuid = 00002a26-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.firmware_revision_string)
handle = 0x0023, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0024, uuid = 00002a27-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.hardware_revision_string)
handle = 0x0025, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0026, uuid = 00002a28-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.software_revision_string)
handle = 0x0027, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x0028, uuid = 00002a29-0000-1000-8000-00805f9b34fb (org.bluetooth.characteristic.manufacturer_name_string)
handle = 0x0029, uuid = 00002800-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.primary_service_declaration)
handle = 0x002a, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x002b, uuid = 0000ffc1-494c-4f47-4943-544543480000
handle = 0x002c, uuid = 00002902-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.client_characteristic_configuration)
handle = 0x002d, uuid = 00002901-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.characteristic_user_description)
handle = 0x002e, uuid = 00002803-0000-1000-8000-00805f9b34fb (org.bluetooth.attribute.gatt.characteristic_declaration)
handle = 0x002f, uuid = 0000ffc2-494c-4f47-4943-544543480000
handle = 0x0030, uuid = 00002902-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.client_characteristic_configuration)
handle = 0x0031, uuid = 00002901-0000-1000-8000-00805f9b34fb (org.bluetooth.descriptor.gatt.characteristic_user_description)

All characteristics with a UUID ending with the sequence ‘xxxxxxxx-0000-1000-8000-00805f9b34fb’ are well known characteristics reserved in the Bluetooth specifications.
These can be looked up on: https://www.bluetooth.com/specifications/gatt/descriptors . I added their meaning in the above dump.

Ignoring the standard Bluetooth characteristics leaves us with 5 vendor specific UUID’s:

  • handle = 0x0012, uuid = 0000fee1-494c-4f47-4943-544543480000
  • handle = 0x0015, uuid = 0000fee2-494c-4f47-4943-544543480000
  • handle = 0x0018, uuid = 0000fee3-494c-4f47-4943-544543480000
  • handle = 0x002b, uuid = 0000ffc1-494c-4f47-4943-544543480000
  • handle = 0x002f, uuid = 0000ffc2-494c-4f47-4943-544543480000

I just started dumping these characteristics while changing connected load. This showed that UUID 0000fee2-494c-4f47-4943-544543480000 contained the current load information. Since most of the data encoded as packed BCD, it is very easy to spot the values like the mains voltage and frequency in the hexadecimal output of gatttool.

UUID 0000fee2-494c-4f47-4943-544543480000 example:

228442 074153 207287 100002 049974 00 00 00 00
|      |      |      |      |      \ ???
|      |      |      |      \ Frequency: 49.974 Hz
|      |      |      \ Work Factor: 1.00002
|      |      \ Watt: 2072.87 Watt
|      \ ???
\ Voltage: 228.442 Volt

I found that UUID 0000fee3-494c-4f47-4943-544543480000 only changed when using the Android app. Some playing around with the app showed that it seems to contain the response to commands. So this is probably a RPC construction commonly used is in Bluetooth LE where you write commands to a characteristic and read back to response.

So far there seems to be no protection against reading out characteristics. The device however does disconnect fairly quickly when trying to use gatttool in interactive mode.

Sniffing Android App

To know more about the command RPC characteristic I would be great to know what the Android app sends to the device. Luckily Android has a very handy feature built in that allows you to sniff the bluetooth calls made by apps. This can be enabled under ‘Settings->Developer options->Bluetooth HCI snoop log‘ (see Google on how to enable the ‘Developer options’). When enabled all bluetooth HCI calls are logged to a file called ‘btsnoop_hci.log’, on my phone located in ‘Device storage/Android/data’.

The snoop file can be read in Wireshark for analysis.

wireshark on-off packet

Playing a bit with the On/Off switch and analysing the communication shows that UUID 0000fee3-494c-4f47-4943-544543480000 is indeed a RPC channel. The On/Off command is a 2 byte command with the first byte probably being the opcode(=0x04) and the second meaning on(=1) or off(=0).

Sending commands, first try

Ok, so now I know how to send the command to switch the power. Lets try sending it with gatttool.

$ gatttool -b <MAC> --char-write-req -a 0x0018 -n 0401

And… No reaction from device. That is disappointing. Apparently there is more going on here.

Pairing (1)

The device has a pairing button but so far I haven’t come across anything that indicates this pairing is used. But with the device declining my commands it might be worth looking in to.

Snooping the communication of the Android app while pairing showed that the app doesn’t make a connection to the device while pairing! The log only shows advertisement messages.

Advertisement messages

The device seems to send 2 types of advertisements:

  • Event Type: Scan Response (0x04)
  • Event Type: Connectable Undirected Advertising (0x00)

The ‘Scan Response’ type advertisements are standard Bluetooth stuff and only contain the device name and TX power. The ‘Connectable Undirected Advertising’ however contains some vendor specific data.

Staring at various advertisement messages showed the vendor specific data actually is a summary of the device measurements:

Raw packet data:
02010611060000484345544349474f4c49e0fe0000 09 ff 0158 191701162405 (1 byte RSSI is stripped)
\ Standard BLE header                      |  |  |    |
                                           |  |  |    \ DATA
                                           |  |  \ Company ID according to wireshark but used for data
                                           |  \ type: Manufacturer Specific
                                           \ Length: 9 byte (type+company id + data)
Vendor data:
 01 58 1917 0116 2405
 |  |  |    |    \ Watt: 24.05 W
 |  |  |    \ Ampere: 0.116 A
 |  |  \ ???
 |  \ ??? (work factor * 100 in hex, ie. 64 is 1.00 ????)
 \ On(1)/Off(0)

When the device is in pairing mode the vendor specific data is different in length and value. Closer inspection of the value showed that this is actually the device’s MAC address but in reverse order.

Sending commands, second try

Ok, so far I found nothing that suggests there is any security. But why didn’t the commands I send earlier get accepted? Let’s replay all commands send by the Android app and see if that works.

First of all the app registers notifications on for handle 0x12 and 0x18. This is done by writing a 16-bit little endian value of 0x0001 to handles 0x13 and 0x19, as specified by the Bluetooth standard. The handle 0x12 notifications seem the contain the current usage data and notifications from handle 0x18 contain the RPC command response.

Second the app sends a command starting with 0x03(which I suspect to be the opcode). When I replay this in gatttool in interactive mode the device no longer disconnects me after a short time! Apparently we are on to something…

Now let’s send the power on/off command after this command…. Success!!! the load turns on/off.

Command 0x03

Ok. so what is this magic command with opcode 0x03. This must be some magic OTP security key that is negotiated during pairing and constantly changes to protect against other people controlling our device… or not…

Lets look at the commands in various captures:

1) 03 e207 0106 09391b ff02
2) 03 e207 0106 0f0a1d ff02
3) 03 e207 0106 0f0d38 ff02
4) 03 e207 0106 0f1a17 ff02
5) 03 e207 0106 0f1a07 ff02
6) 03 e207 0106 0f1b1e ff02
7) 03 e207 0106 0f1b31 ff02

The value changes every time but the changes is minimal. Older captures seem to have a bigger change compared to the last capture, so maybe it is time based. Lets look at the file dates of my captures:

1) Modify: 2018-01-06 09:57:48.000000000 +0100
2) Modify: 2018-01-06 15:10:48.000000000 +0100
3) Modify: 2018-01-06 15:14:13.000000000 +0100
4) Modify: 2018-01-06 15:26:33.000000000 +0100
5) Modify: 2018-01-06 15:26:14.000000000 +0100
6) Modify: 2018-01-06 15:27:42.000000000 +0100
7) Modify: 2018-01-06 15:27:58.000000000 +0100

With this information the sharp reader will probably have no problem decoding the commands;). But if you don’t see it, the 0x03 command apparently sets the time of the device.

 03 e207 0106 09391b ff02
 |  |    |    |      \ Timezone stuff?
 |  |    |    \ Time(hex): 09:57:27
 |  |    \ Date: 6 January
 |  \ Year: 0x7e2 = 2018
 \ Opcode

Pairing(2)

Ok, now I have full control over the device. But what is the use of the whole pairing button? Clearly this is not a security feature. As far as I can see it is only meant to tell the Android app to connect to this device.

Conclusion

It took me less then a day to get full control over this device without using any specialized hacking tool. So I can have only one conclusion:

This device is crap

Sorry, I normally try to be a bit more subtle. But I just don’t understand why in 2014 there are still wireless products designed without any form of security. It has even gotten worst compared to years ago.

Old 433 MHz products also didn’t have any strong encryption. But in general these protocols come from a different age, where crypto was still expensive. Also you need a 433 MHz transceiver and some knowledge to attack them, although with SDR this hurdle has become very small. Finally for 433 MHz switchable power sockets you have to at least intercept the traffic once to know they are there. While this device happily broadcasts its presence and you need no additional information to fully operate it.

Since this device is so broken I haven’t found much motivation to document all my findings and upload my scripts. But maybe I will in the future. For now if you aren’t scared of by my article and want more informations about the commands just e-mail me.