Digital lockpicking - stealing keys to the kingdom

By Krzysztof Marciniak on 11 December, 2019

Krzysztof Marciniak

11 December, 2019

Introduction

In the era of smart devices, it should come as no surprise that more and more appliances "turn" smart. The KeyWe Smart Lock is no exception. In order to make life more convenient, it is designed to have both the mechanical lock mechanism and additional functionalities on top of it. Those include, but are not limited to, generating one-time guest codes and unlocking the door based on proximity.

Convenience does, however, always come at the cost of security.

Device overview

The lock consists of three significant parts:

  • The front panel: used to interact with the lock
  • The mechanical lock: used both by the software and standalone
  • The back panel: used to supply power and move the mechanical part of the lock

Both of the panels are connected; should someone attempt to disconnect them, an alarm will be set off.

It can be opened in a few ways:

  • Mechanically, with a key
  • Using the application (no additional confirmation is needed)
  • Using an armband (NFC - Mifare Classic)

Lock hardware

Removing the boards and inspecting them visually - without identifying the components - clearly shows the purpose of each of them.

KeyWe - boards

KeyWe - board (back)

The front one is used only to handle user input (numerical keyboard and the RFID chip). The back one, which is impossible to access by the attacker, handles all application logic.

Serial and JTAG access attempts (on the latter) did not yield results. Component identification, however, showed an STM microcontroller is used. On one of the ports exposed SWIM - Single-Wire Interface Module, a debug protocol used in STM uCs - appeared to have been left enabled. After soldering to the pin and using the ST-LINK adapter it was possible to dump the device firmware.

Although incomplete, the list of components identified on the board can be found below:

NameDescription
STM8L052R8Ultra-low-power 8-bit MCU with 64 Kbytes Flash, 16 MHz CPU, integrated EEPROM
Silicon Labs ZM5101A-CME3Z-Wave support (SoC)
adesto AT45DB021ESPI Serial Flash memory

Investigating the bridge

The bridge device is meant to expose the lock - which is handled via Bluetooth Low Energy - over WiFi. Though fairly sophisticated on the outside, inside it holds an ESP32-based board. The interesting part of this board are the pins located on the white panel.

KeyWe Bridge - board

After spending some time with a multimeter and a pinout diagram, it was possible to identify most of the pins:

KeyWe Bridge - pinout

Although ESP is fairly mature - with read protections, encryption etc. available - no such functionalities are utilized by the bridge. The serial and JTAG access are left enabled, and no encryption is employed. This can be confirmed with the output below:

$ espefuse -p /dev/ttyACM0 summary
espefuse.py v2.5.1
Connecting....
Security fuses:
FLASH_CRYPT_CNT Flash encryption mode counter = 0 R/W (0x0)
FLASH_CRYPT_CONFIG Flash encryption config (key tweak bits) = 0 R/W (0x0)
[...]
JTAG_DISABLE Disable JTAG = 0 R/W (0x0)
[...]
Efuse fuses:
WR_DIS Efuse write disable mask = 0 R/W (0x0)
RD_DIS Efuse read disablemask = 0 R/W (0x0)

Given how easy it was to access the firmware, the bridge has not been given attention since the analysis.

Bluetooth Low Energy

Let us go up a notch. Figuratively, of course. We are now moving to the communication and application layer. Based on publicly available materials, as well as the packaging with which the lock came, we know it uses "Bluetooth Smart". For those unaware, this is just another name for BLE/BTLE: Bluetooth Low Energy.

Probably the most convenient tool for BLE inspection is nRF Connect from Nordic Semiconductors (this is just an opinion of course). Before we proceed, this is a good time to recap on some basic concepts of Bluetooth Low Energy:

Contrary to Bluetooth Classic, which is a technology we all know and most probably use every day, BLE does not work on the concept of discoverability (i.e. discoverable devices). Instead, each device (called a peripheral) sends out messages (called advertisements) to anyone close enough to receive them. An advertisement contains information about the peripheral: its name, address (used to establish a connection), features, and so on. Such a device exposes a list of grouped entities, called services. Those contain characteristics which can contain some value (text, number etc.) that can either be read (read access), updated (write access) or can notify the other connected party that the value has changed (notify access).

The KeyWe lock exposes a single service with two characteristics - one read and one notify:

nRF Connect: KeyWe

We can already assume that they are used for sending/receiving messages to/from the lock. That being said, there is but one way to verify this: to inspect the mobile application. Due to personal preference, the Android application will be discussed.

Breaking the software

Searching for BLE-related functions, we stumble upon the DoorLockNdk class, that contains multiple methods. For those not familiar with Android, NDK stands for Native Development Kit, and is a way to bridge native (compiled, unmanaged) code with Java (using the Java Native Interface, JNI).

Each of the aforementioned methods returns an array of bytes - bingo! We have found ourselves a goldmine. To elaborate a bit more: if we somehow intercepted the messages, it would be for nothing; each message is encrypted with AES-128 before sending. With access to the DoorLockNdk class (its methods in particular), however, we can intercept messages before encryption. This means that the communication protocol can be analyzed. How do we intercept the function calls, though? The answer is simple... with Frida! Skipping the technicalities here (one can read more about function hooking with Frida here), we create functions that dump arguments and return values. The code can be found in the GitHub repository.

A log of messages exchanged between the lock and the mobile application - based on the information gained using Frida - can be found below:

DirectionByte arrayASCIIFunction name (if available)
 lock <- app

bb b6 d4 26 36 c9 46 ea 4d 70 56 65 00 00 00 00 

...&6.F.MpVe.... 

makeAppNumber 
 lock -> app

c6 9b 84 8b 1c 18 1c 18 1c 18 1c 18 00 00 00 00 

................ 

(makeDoorNumber) 
 lock -> app

48 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 

Hello........... 

 isHello
 lock <- app

57 65 6c 63 6f 6d 65 00 00 00 00 00 00 00 00 00 

Welcome......... 

 makeWelcome
 lock -> app

53 54 41 52 54 00 00 00 00 00 00 00 00 00 00 00 

START........... 

 isStart
 lock <- app

33 d4 02 01 10 11 03 00 00 00 00 00 00 00 00 00 

3............... 

 doorMode
 lock -> app

33 d4 02 05 10 01 00 00 00 11 03 00 00 00 00 00 

Functions that deserve an honored mention are: makeCommonKeymakeAppKey and makeDoorKey. These are called, either within the application or on the lock, but do not directly result in messages sent.

Although the exact thought process will be skipped, it should be pointed out that functions makeAppNumber and makeDoorNumber are used to generate values exchanged between the two parties. This serves as a simple "key exchange" protocol: each side generates a value, sends it to the other counterpart, and once the other "number" is received, the key pair can be calculated. One key is used to encrypt/decrypt messages originating from the lock (makeDoorKey) and the other for the mobile device (makeAppKey). Each of these numbers is encrypted with the *common key* before sending.

A list of exemplary makeCommonKey call results - common key values - can be found below:

Device addressExec. IDByte array

8c c8 f4 0f eb 81

0

c8 8f f4 15 0f 4a eb 27 81 4a 6c 5e 67 41 ef ac

8c c8 f4 0f eb 81

1

c8 8f f4 15 0f 4a eb 27 81 4a 6c 5e 67 41 ef ac

8c c8 f4 0f eb 81

2

c8 8f f4 15 0f 4a eb 27 81 4a 6c 5e 67 41 ef ac

8c c8 f4 0f ec 7b

(exec. ID is used to state which execution - first, second etc. - it was for a given device)

As we can (hopefully) see, the common key does not change between executions, but it does change with the device address.

This is a grave mistake! As an in-house key exchange is used - with just two values involved - to decrypt all of the communication, one simply needs to intercept the transmission. The common key can then be easily calculated based on the device address.

The code for calculating the keys has been released (although rendered harmless by removing key parts) in the aforementioned repository.

With this, we have ourselves the keys to the kingdom!

The technical advisory for this vulnerability has been published here

Sniffing the traffic

There is still one part missing from the puzzle. How do we actually get the messages exchanged between the lock and the mobile application? Regular applications/adapters do not have such a functionality. Fortunately, there is both hardware and firmware available to do it. In order to sniff Bluetooth LE traffic, one can use the nRF Sniffer available here. With a board such as Waveshare BLE400 or USB Armory Mk II (as it has an nRF-based Bluetooth SoC), gathering LE data is as simple as running Wireshark with one plugin installed.

Vendor's response

The vendor has acknowledged the issue and is working on fixing it. They have been responsive since F-Secure disclosed the issue and have actively participated in communication. Unfortunately, no firmware upgrade functionality has been included and thus the issue will persist until the device is replaced. According to the vendor, new devices will contain a security fix. Moreover, the next version of the lock will have the firmware upgrade functionality - although no information is available regarding the release date.

Conclusions

One cannot say that no attention has been given to security with the KeyWe lock. The AES algorithm used is the de-facto standard when it comes to cryptography. But the protocol used can be referred to as in-house crypto - and, as the industry has shown over the years, this never ends well. Moreover, the vendor appears to have a vague idea of the threat model. Had the developers/architect(s) known that the device address is available to anyone close to the lock, there would be no issue whatsoever.

The lessons learnt, and the takeaways for any organization dealing with smart, potentially IoT devices:

  • Understand your threat model
  • Do not develop or derive in-house crypto
  • Know (and test) your tools - they can save a ton of time
  • Perform in-depth analysis of seemingly secure devices so that over time you learn to see patterns in data

Thanks

  • Michał Madziar
  • Andrea Barisani
  • Mark Barnes
  • Sławomir Jasek (smartlockpicking.com; SecuRing)
  • the whole F-Secure Poznań consultancy team - cheers guys!