Examining the August Smart Lock

A blog post about the security implemented in the August Smart Lock, with special focus on the Bluetooth Low Energy capabilities.

Introduction

The following document describes all the work done so far regarding the reverse engineering process of the August Smart Lock 3rd generation.

My motivation for this project was to understand how smart lock devices work and how safe they are. In my case, I only had an August Smart Lock so all the work was done against it. I had a very specific attack vector: a potential attacker comes to you door with a cellphone/computer and the only thing between him and your house is the lock. Can he connect to the lock? Can he interact with the lock in some way to gain access? What's the attack surface at that point? Of course, the attacker can break the lock with a hammer or kick the door down but in this case, the attacker doesn't want to leave any trace :P

As its name implies, the August Smart Lock is a lock with Bluetooth capabilities, Bluetooth Low Energy (BLE) to be more specific. You just need to attach it to the deadbolt and control the access with a mobile application. It provides many features as guest keys, auto-lock and remote access. Most of these types of devices are promoted as secure.

In the following sections I'm going to show the approach I followed in order to study the lock, the tools I used and the wins and fails during the process.

The August Smart Lock ecosystem

Here is a picture from the Security Analysis of the August Smart Lock paper to illustrate the August Smart Lock ecosystem:

The first thing we have is the lock (a), which is powered by a battery. The smartphone (b) connects to the lock via BLE and to the August's web servers (c) via wireless (WiFi/4G). The smartphone is used to lock/unlock the smart lock withing the range of the BLE signal. The smartphone also connects, via WiFi, to the August's web servers that are used to manage the user accesses to the lock and track all the events that happen in it. They also maintain an history of all the lock and unlock events.

Finally, we have the August Connect device (d) which it's an accessory used to remotely control the smart lock. It connects to the August's web servers via WiFi and allows the homeowner to control user access from any place in the World.

There are other accessories than can be added to the smart lock like the August Smart Keypad to unlock the door with entry codes and even additional products like the August View Doorbell Camera that can be combined with the smart lock to expand its functionalities.

As said at the beginning, the August Smart Lock provides BLE capabilities which is the main focus of my research.

Bluetooth LE was introduced in the Bluetooth 4.0 core specification. Its main goal is to be a smaller, highly optimized version of its big brother, classic Bluetooth. BLE was designed to have the lowest possible power consumption, specifically optimized for low cost, low bandwidth, low power, and low complexity.

Regarding the BLE security, the standard provides some ways to secure a connection between devices during the pairing process. However, August uses its own communication protocol in order to provide security to the lock. As explained in the Analyzing the mobile app section, it has its own handshake in order to authorize a communication. Also, it introduces different types of users having their own level of privilege.

After reading the Security Analysis of the August Smart Lock paper, I got some understanding about the different types of users that the lock can handle.

There are TWO types of users: OWNER and GUEST. The OWNER is the administrator of the system, effectively the resident of the house. OWNER implies a high degree of trust. An OWNER can grant OWNER privileges to other users.

The GUEST is someone that the OWNER wants to grant temporary access to the house. The GUEST is only allowed to lock/unlock the door at the times specified by the OWNER.

Another important information is that, in the authentication phase, the OWNER can handle this access offline while the GUEST MUST have Internet connection to do it because it must contact the August remote web servers in order to check for permissions. This is interesting because, in my attack scenario, it means that a potential attacker would be represented as a GUEST user and it must escalate to the OWNER type in order to be able to perform the unlock without the need to contact the August web servers.

In the BLE communication between the mobile app and the lock (without counting with Internet connection), the OWNER communicates using an OFFLINE key. It means that these keys, or the FIRMWARE KEY, must be obtained in order to conduct an attack. The FIRMWARE KEY never changes. This is a unique key and the most privileged that is used, basically, to get access to the lock, even if you lost your status of OWNER.

Attack surface

Generally speaking, you can find a lot of publications about hacking different types of BLE capable devices. If we talk about smart locks, these are just a few examples I found in order to start my research:

Then, several publications were done focusing only on the (in)security of the August Smart Lock, these are the ones I found:

  • Backdooring the Frontdoor by Jmaxxz: in this presentation from DEFCON 24, Jmaxxz showed multiple vulnerabilities he found in the 1st and 2nd generations of the August Smart Lock.

    He put the focus of his research on the communication between the mobile application and the August web server, as can be seen in his wiki. Basically, he performed a MitM attack bypassing the certificate pinning of the app and was able to map the entire REST API. This way, he was able to manipulate the data on the requests and alter the functionality of the lock in many ways such as an escalation from GUEST to OWNER to allow a GUEST user to change the lock settings.

    Also, he was able to collect debug and log information from the BLE traffic and find out that the logs of the mobile application contained sensitive information such as the necessary keys to authenticate a user. He also inspected the hardware of the lock and found the UART and JTAG interfaces.

  • The paper A Case Study of August Smart Lock by Mengmei Ye, Nan Jiang, Hao Yang and Qiben Yan from the IEEE Conference on Computer Communications Workshops in 2017, which describes four attacks:

    • Handshake Key Leakage Attack: this attack is based on the fact that the attacker is able to steal the keys used to perform the online and offline handshake stored on the smartphone.
    • Owner Account Leakage Attack: this attack is based on the fact that an attacker is able to steal the files related to the user account information stored on the smartphone and is able to disguise himself/herself to be the owner of the lock.
    • Personal Information Leakage: this attack is based on the fact that the attacker is able to get the personal information associated with a user account stored on the smartphone and thus, infer other type of personal information such as bank accounts numbers and family member information.
    • Denial-of-service Attack: this attack is based on the the fact that the attacker is able to interfere with the signal (BLE) received by the smart lock. Basically, when multiple connections from different applications are performed against the lock, it suspends the application and no user can lock or unlock it for a certain amount of time. This allows an attacker to continuously send a signal to the lock to avoid other users to connect to it.

    The first three attacks rely on the fact that the lock and user account information, as well as the keys used to communicate with the lock, can be leaked from a jailbroken device. The authors of the papers set the stage in which a potential attacker is able to gain access to a jailbroken device and steal the keys, user and lock info and use them to impersonate the real owner of the lock.

    As a side note, jailbreaking the devide is a necessity since it is not possible to access the folders where the aforementioned information resides without this.

    In my opinion, the scenario is a no-brainer. If an attacker can compromise a phone and escalate privileges to the state needed for these attacks (jailbroken which means root) then the lock is one of your least worries. Apart from the keys to impersonate the lock's owner, the attacker will have access to personal information such as bank accounts, credit card numbers, contact numbers, mail and social media accounts, etc. Game over. Besides, the attacks rely on the mobile security model and not the lock security model itself.

  • Security Analysis of the August Smart Lock by Megan Fuller, Madeline Jenkins and Katrine Tjolsen: this paper from 2017 published by the Massachusetts Institute of Technology is more focused on the lock security. It describes how the whole lock ecosystem interacts (lock, mobile app and lock remote server), what's needed for an actual attacker to compromise the lock from different attack vectors and describes some real attack scenarios:

    • Password attack: this attack relies on the fact that a logged user remains in logged state in the mobile application. If an attacker has access to the phone for some minutes, he/she can change the password. No password verification is needed for this task. However, this attack fails when the adversary tries to log in as the user on another device because the app requires email/SMS verification of the new device.

    • "I'm not listening" attacks

      • Owner-Level Access Not Revoked: In this attack scenario, the communication with the lock is made offline (without contacting the August web server). A user, Bob, was granted OWNER privileges in the lock by Alice and Bob remains with the Bluetooth functionality turned on and in the lock's signal range. Then, Alice gets out from the Bluetooth signal range and revokes Bob's access. However, as Alice is not able to communicate with the lock nor with the August's web server, Bob remains with OWNER privileges in the lock and is able to lock and unlock it.
      • Stolen Phones: In this attack, there are two possible scenarios. In the first case, the attacker is able to unlock the stolen phone and due to the lack of password protection and auto-logout in the August mobile app, the attacker would be able to have access to the user's home.

      In the second case, the attacker is not able to unlock the stolen phone but if the owner has the auto-unlock feature turned on in the August mobile app in the phone, the attacker would be able to have access to the user's home.

    • Changing date and time settings: In this attack scenario, a GUEST user was authorized to unlock the door at certain times. The goal is to allow access outside the one given at the beginning. The authors of the paper tried to trick the mobile app by changing the date and time settings in the phone but the attack wasn't successful.

    • Snooping Bluetooth packages: This attack relies on the possibility of sniffing the BLE traffic and being able to perform a replay-like attack. However, according to the authors of the research, this kind of attack is not possible due to several countermeasures implemented in the lock such as a session key used during the handshake, the handshake itself and the necessary packets needed for each command.

There are other interesting additions to the August Smart Lock that can be studied. For example, the August Connect also sounds interesting. It's mainly used to add WiFi capabilities to the lock by creating a bridge in between the smartphone, the August web servers and the lock. I'm not sure if this device only forwards packets or stores other kind of information. In my understanding, the smartphone connects to the August web servers via the mobile app, the user performs an action such as locking the door, so the mobile app sends the command to lock the door to the August Connect device and this forwards the packet via BLE to the lock. How is the communication with the bridge done from the web servers? is it encrypted? Are the keys needed to perform the handshake with the lock also stored in the bridge device?. Unfortunately, I didn't have access to one of these devices so I cannot say too much about it but it clearly is another vector to attack.

What I did so far

Analyzing the mobile app

My first step with the lock involved the decompilation of the August Smart Lock mobile application (Android). I used the evozi online APK downloader service in order to get the official APK from the Google Play Store. The APKCombo works great too.

During the research, I looked at different versions of the mobile app, the latest was v10.1.0.

As I was mainly interested in the BLE attack vector, I decided to start looking at the com.august.ble2.AugustBluetoothManager. Inside that package, there are several classes that make calls to a run() method. There's one in particular that calls the securityHandshake method from the AugustEncryption class:

public void run() {
    try {
        AugustBluetoothManager.K.debug("The LockInitializationTask is running");
        AugustBluetoothManager.this.a(State.Connected_ReadingDeviceInfo, new State[]{State.Connected_StartingInitializationTask});
        BluetoothGattService a2 = AugustBluetoothManager.this.a(BluetoothUUIDs.DEVICE_INFORMATION_SERVICE);
        if (a2 != null) {
            String a3 = AugustBluetoothManager.this.a(a2);
            LockInfo lockInfo = PeripheralInfoCache.getInstance().getLockInfo(AugustBluetoothManager.this.u);
            lockInfo.setBluetoothFirmwareVersion(a3);
            lockInfo.setSerialNumber(AugustBluetoothManager.this.b(a2));
            AugustBluetoothManager.this.a(State.Connected_Subscribing, new State[]{State.Connected_ReadingDeviceInfo});
            a();
            AugustBluetoothManager.this.a(State.Connected_Authorizing, new State[]{State.Connected_Subscribing});
            AugustBluetoothManager.this.f4149f.encryption.securityHandshake(AugustBluetoothManager.this.f4149f, AugustBluetoothManager.this.u);
                                [..]

Here's the code for the securityHandshake method:

public void securityHandshake(AugustBluetoothConnection augustBluetoothConnection, String str) throws BluetoothException, BackendServerException, InterruptedException {
    f4567f.info("Starting the August Security Handshake to authorize the Bluetooth connection to lockId {}", (Object) str);
    LockInfo lockInfo = PeripheralInfoCache.getInstance().getLockInfo(str);
    HandshakeType handshakeType = DEFAULT_HANDSHAKE_TYPE;
    HandshakeSelector handshakeSelector = f4568g;
    if (handshakeSelector != null) {
        handshakeType = handshakeSelector.getHandshakeStrategy(str);
        f4567f.debug("Selected handshake type {}", (Object) handshakeType);
        if (handshakeType == null) {
            throw new BluetoothMessagingException("AugustEncryption.handshakeSelector returned handshakeType = null. This is not allowed. Please select one of the defined HandshakeTypes");
        }
    }
    int i = C2066a.f4576a[handshakeType.ordinal()];
    if (i == 1) {
        doOnlineHandshake(augustBluetoothConnection, str);
    } else if (i == 2) {
        doOfflineHandshake(augustBluetoothConnection, lockInfo);
    } else if (i != 3) {
        throw new BluetoothMessagingException("Unrecognized handshakeType %s", handshakeType);
    } else if (lockInfo.getHandshakeKey() != null) {
        doOfflineHandshake(augustBluetoothConnection, lockInfo);
    } else {
        f4567f.debug("lockInfo.handshakeKey = null, so we'll use an online handshake");
        doOnlineHandshakeWrapper(augustBluetoothConnection, str, "wrong_offline_key");
    }
}

I have renamed the methods for clarity.

Even though DEFAULT_HANDSHAKE_TYPE is set to HandshakeType.PreferOffline, there's a call to the getHandshakeStrategy method in order to define the type of handshake to use in the communication. The handshakeType variable may have three different values:

public enum HandshakeType {
    OnlineOnly,
    OfflineOnly,
    PreferOffline
}

The doOnlineHandshake method uses HTTP/JSON to perform the handshake against the August web servers. But I was mostly interested in the doOfflineHandshake:

public final void doOfflineHandshake(AugustBluetoothConnection augustBluetoothConnection, LockInfo lockInfo) throws BluetoothException, InterruptedException {
     LockInfo lockInfo2 = lockInfo;
     if (lockInfo2 != null) {
         byte[] handshakeKey = lockInfo.getHandshakeKey();
         int handshakeKeyIndex = lockInfo.getHandshakeKeyIndex();
         if (handshakeKey == null) {
             throw new IllegalArgumentException("handshakeKey is null inside handshakeOffline");
         } else if (handshakeKey.length != 16) {
             throw new IllegalArgumentException(String.format("handshakeKey is only %d bytes. It should be %d bytes", new Object[]{Integer.valueOf(handshakeKey.length), 16}));
         } else if (handshakeKeyIndex < 0 || handshakeKeyIndex > 255) {
             throw new IllegalArgumentException(String.format("Can't perform a handshake with key at slot %d. Our handshake protocol only supports handshake keys in the range 0-%d", new Object[]{Integer.valueOf(handshakeKeyIndex), 255}));
         } else {
             f4567f.info("Attempting handshakeOffline with handshake key {} at index {}", (Object) getLoggableHandshakeKey(handshakeKey), (Object) Integer.valueOf(handshakeKeyIndex));
             f4567f.debug("Generating offline handshake command");
             int nextInt = Random.secureRandom.nextInt();
             int nextInt2 = Random.secureRandom.nextInt();
             ByteBuffer prepareCommandBuffer = AugustLockProtocol.prepareCommandBuffer();
             byte b = (byte) handshakeKeyIndex;
             AugustLockProtocol.validateResult(AugustLockProtocol.augLockCmdSecuritySendMobileKeyWithIndex(prepareCommandBuffer, nextInt, nextInt2, b));
             f4567f.debug("Generated the handshake packet successfully. Now encrypting it and sending it to the peripheral");
             mo17937a(handshakeKey);
             try {
                 JSONObject enqueueCommandAndWait = augustBluetoothConnection.enqueueCommandAndWait("HandshakeBegin", encryptSecurityPacket(prepareCommandBuffer), AugustBluetoothConnection.TransmitChannel.SECURITY_CHANNEL, false, (ResponseWatcher) new ResponseWatcher.AnyResponse());
                 try {
                     int i = enqueueCommandAndWait.getInt("rand1");
                     int i2 = enqueueCommandAndWait.getInt("rand2");
                     f4567f.info("Creating session key [{}, {}, {}, {}]", Integer.valueOf(nextInt), Integer.valueOf(nextInt2), Integer.valueOf(i), Integer.valueOf(i2));
                     ByteBuffer allocate = ByteBuffer.allocate(16);
                     allocate.order(ByteOrder.LITTLE_ENDIAN);
                     allocate.putInt(nextInt);
                     allocate.putInt(nextInt2);
                     allocate.putInt(i);
                     allocate.putInt(i2);
                     mo17937a(allocate.array());
                     f4567f.info("Sending initialization command to the peripheral");
                     int nextInt3 = Random.secureRandom.nextInt();
                     int nextInt4 = Random.secureRandom.nextInt();
                     ByteBuffer prepareCommandBuffer2 = AugustLockProtocol.prepareCommandBuffer();
                     AugustLockProtocol.validateResult(AugustLockProtocol.augLockCmdSecurityInitializationCommandWithIndex(prepareCommandBuffer2, nextInt3, nextInt4, b));
                     JSONObject enqueueCommandAndWait2 = augustBluetoothConnection.enqueueCommandAndWait("HandshakeConfirm", encryptSecurityPacket(prepareCommandBuffer2), AugustBluetoothConnection.TransmitChannel.SECURITY_CHANNEL, false, (ResponseWatcher) new ResponseWatcher.AnyResponse());
                     f4567f.info("Validating the response to initialization command");
                     try {
                         int i3 = enqueueCommandAndWait2.getInt("rand1");
                         int i4 = enqueueCommandAndWait2.getInt("rand2");
                         if (i3 == nextInt3 && i4 == nextInt4) {
                             f4567f.info("August Security handshake was successful.  Reinitializing the ciphers.");
                             mo17937a(allocate.array());
                             mo17933a(handshakeKeyIndex);
                             return;
                         }
                         throw new BluetoothMessagingException("iRands from response (%d, %d) don't match iRands from command (%d, %d).", Integer.valueOf(i3), Integer.valueOf(i4), Integer.valueOf(nextInt3), Integer.valueOf(nextInt4));
                     } catch (JSONException e) {
                         throw new BluetoothMessagingException(e);
                     }
                 } catch (JSONException e2) {
                     throw new BluetoothMessagingException(e2);
                 }
             } catch (BluetoothMessagingException e3) {
                 f4567f.warn("Exception during Authorization usually means that the handshake key {} at slot {} is bad.  Deleting it so that we won't repeat this error", (Object) getLoggableHandshakeKey(handshakeKey), (Object) Integer.valueOf(handshakeKeyIndex));
                 lockInfo2.setHandshakeKey((byte[]) null, handshakeKeyIndex);
                 PeripheralInfoCache.getInstance().putPeripheralInfo(lockInfo2);
                 throw e3;
             }
         }
     } else {
         throw new IllegalArgumentException("lockInfo is null inside handshakeOffline");
     }
 }

It can be seen that a handshakeKey and a handshakeKeyIndex values are needed in order to correctly perform the authorization. These keys are stored within the August application and you need to be root in order to extract them from the phone (this is not entirely true, there is a method described by Jmaxxz to extract the keys without rooting the phone).

First, two int values are created using the Random.secureRandom.nextInt method and put together with the handshakeKeyIndex value in order to create a command buffer within the AugustLockProtocol.augLockCmdSecuritySendMobileKeyWithIndex native method. Then, the handshakeKey string is used to create an AES (CBC/NoPadding mode) key using the SecretKeySpec Java function. Before sending the built buffer, it is encrypted using the AugustEncryption::encryptSecurityPacket which is just a wrapper for the AugustEncryption::encrypt method.

Package com.august.ble2.proto.AugustLockProtocol has the list of all the available commands that can be sent to the lock. In the AugustLockProtocol::prepareCommandBuffer method one can see that the command buffer length is 18 bytes. However, it seems that not all the code is Java. The class loads a native library named augustLockComm.

With the response from the augLockCmdSecuritySendMobileKeyWithIndex, a session key is built using the two previously generated int values. Then, two new int values are generated using the Random.secureRandom.nextInt function and sent together with the handshakeKeyIndex value in the AugustLockProtocol.augLockCmdSecurityInitializationCommandWithIndex function. Finally, the response is validated using the previously generated int values.

The following diagram from Jmaxxz, illustrates the offline handshake process:

At this point, it can be seen that the August Smart Lock doesn't rely on the BLE security layer to protect it from attacks. It's implementing its own communication protocol.

Exploring the services and characteristics

In order to explore the services and characteristics of the lock, I mainly used BlueZ GATT tool.

I first used hcitool to scan and search for the lock address:

pi@zulmalobato:~ $ sudo hcitool lescan
sudo: unable to resolve host zulmalobato
LE Scan ...
78:9C:85:03:22:7A (unknown)
E5:09:DF:73:FB:75 (unknown)
E5:09:DF:73:FB:75 Amazfit Bip Watch
53:55:5A:9F:FD:E3 (unknown)
78:9C:85:03:22:7A L4005R4
38:F9:D3:4A:E0:44 (unknown)
5A:2B:53:C7:6F:39 (unknown)
5A:2B:53:C7:6F:39 (unknown)
53:55:5A:9F:FD:E3 (unknown)
64:7B:11:7D:37:65 (unknown)
64:7B:11:7D:37:65 (unknown)

The L4005R4 device is the lock so its address is 78:9C:85:03:22:7A. It was possible to determine this because the label L4005R4 is related to the lock serial number:

Once connected to the lock, the primary services can be listed:

[78:9C:85:03:22:7A][LE]> primary
attr handle: 0x0001, end grp handle: 0x0005 uuid: 00001800-0000-1000-8000-00805f9b34fb --> Generic Access
attr handle: 0x0006, end grp handle: 0x0006 uuid: 00001801-0000-1000-8000-00805f9b34fb --> Generic Attribute
attr handle: 0x0007, end grp handle: 0x000f uuid: 0000180a-0000-1000-8000-00805f9b34fb --> DEVICE_INFORMATION_SERVICE
attr handle: 0x0010, end grp handle: 0x0020 uuid: 0000fe24-0000-1000-8000-00805f9b34fb --> August Lock services

Handle 0x0020 corresponds to the custom characteristics implemented by the lock.

Then, we can list all the available characteristics:

[78:9C:85:03:22:7A][LE]> characteristics
handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb --> Device name
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb --> Appearance
handle: 0x0008, char properties: 0x02, char value handle: 0x0009, uuid: 00002a29-0000-1000-8000-00805f9b34fb --> Manufacturer Name String
handle: 0x000a, char properties: 0x02, char value handle: 0x000b, uuid: 00002a24-0000-1000-8000-00805f9b34fb --> Model Number String
handle: 0x000c, char properties: 0x02, char value handle: 0x000d, uuid: 00002a25-0000-1000-8000-00805f9b34fb --> Serial Number String
handle: 0x000e, char properties: 0x02, char value handle: 0x000f, uuid: 00002a26-0000-1000-8000-00805f9b34fb --> Firmware Revision String
handle: 0x0011, char properties: 0x0c, char value handle: 0x0012, uuid: bd4ac611-0b45-11e3-8ffd-0800200c9a66 --> write
handle: 0x0013, char properties: 0x20, char value handle: 0x0014, uuid: bd4ac612-0b45-11e3-8ffd-0800200c9a66 --> read
handle: 0x0016, char properties: 0x0c, char value handle: 0x0017, uuid: bd4ac613-0b45-11e3-8ffd-0800200c9a66 --> secure write
handle: 0x0018, char properties: 0x20, char value handle: 0x0019, uuid: bd4ac614-0b45-11e3-8ffd-0800200c9a66 --> secure read
handle: 0x001b, char properties: 0x14, char value handle: 0x001c, uuid: bd4ac615-0b45-11e3-8ffd-0800200c9a66 --> ota command | related to firmware updates
handle: 0x001e, char properties: 0x14, char value handle: 0x001f, uuid: bd4ac616-0b45-11e3-8ffd-0800200c9a66 --> ota data    | related to firmware updates

Curiously, some services and characteristics listed in the code of the mobile app are apparently not used.

Example in com.august.ble2.proto.BluetoothUUIDs:

HARDWARE_VERSION_CHARACTERISTIC = 00002A27-0000-1000-8000-00805f9b34fb
SOFTWARE_VERSION_CHARACTERISTIC = 00002A28-0000-1000-8000-00805f9b34fb

The same goes for the no.nordicsemi.android.ble.BleManager package:

public static final UUID B = UUID.fromString(AugustLockCommConstants.UUID_DESCRIPTOR_CLIENT_CHARACTERISTIC_CONFIGURATION);
public static final UUID C = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb"); --> Battery Service
public static final UUID D = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb"); --> Battery Level characteristic
public static final UUID E = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb"); --> Generic Attribute
public static final UUID F = UUID.fromString("00002A05-0000-1000-8000-00805f9b34fb"); --> Service Changed

Anyway, by looking at com.august.ble2.proto.AugustLockCommConstants, it can be seen that the August lock service, handle 0x0020, provides four characteristics:

  • read
  • write
  • secure read
  • secure write

The read and write characteristics are used to perform tasks that are not strictly related to secure protocol communication while the secure read and secure write characteristics are used to perform the security handshake.

As I wanted to see how these characteristics are used during the security handshake in live, I tried to capture some BLE traffic using different approaches.

Capturing BLE traffic

One of the approaches to capture or sniff BLE traffic is by retrieving the btsnoop_hci.log file from the mobile phone.

It's supposed that the location of this file is indicated in the bt_stack.conf file located in /etc/bluetooth. However, in my case (Samsung Galaxy S8+), the bt_stack.conf file doesn't contain anything about it.

Some phones require to be root in order to extract the btsnoop_hci.log but there is a way to do it without such privileges.

These are the steps I followed:

  1. Turn on Developer options in the phone.
  2. Turn on Bluetooth HCI snoop logging.
  3. Restart the phone.
  4. Turn on Bluetooth.
  5. Interact with the lock using the August application.
  6. Use the Android Platform tools to generate a bug report (including the btsnoop_hci.log file):
adb shell dumpsys bluetooth_manager
adb bugreport > BUG_REPORT.txt

The previous commands generate a .txt and a .zip file. The btsnoop_hci.log is located in the FS\data\log\bt\ directory in the generated .zip file. The btsnoop_hci.log file can be loaded using Wireshark.

The second approach I followed to capture some traffic was using a Ubertooth One device.

However, after getting the traffic loaded in Wireshark, it was not that helpful. Basically, as the data packets that are sent between the lock and the mobile phone are encrypted there isn't too much we can see.

I decided to go for another approach in order to have a better understanding of how the data packets are exchanged between the lock and the phone. In the first place, I thought about using Frida to instrument the mobile application but I ended up using another tool.

Dynamic tests using augustpy

When searching for information, I found several tools written in different languages that allow to control the August Smart Lock using the HTTP/S API. Some of these projects are:

I used augustpy to do some tests on the lock and try to understand the flow of the data packets. One required step to use any of these tools is to configure them with the offline keys that are stored in the mobile app. So, let's see how to extract these keys from the phone.

Extracting the offline keys

As I already mentioned in the Analyzing the mobile app section, you must have access to a rooted phone in order to extract the offline keys needed to control the lock.

I Installed August Home in a rooted OnePlus 5T and extracted the handshakeKey and handshakeKeyIndex from the PeripheralInfoCache.xml file located in data/data/com.august.luna/shared_prefs:

OnePlus5T:/data/data/com.august.luna/shared_prefs # cat PeripheralInfoCache.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
        <string name="5A554C4D414C4F4241544F383D3D3D44">{&quot;lockId&quot;:&quot;5A554C4D414C4F4241544F383D3D3D44&quot;,&quot;bluetoothAddress&quot;:&quot;78:9C:85:03:22:7A&quot;,&quot;handshakeKey&quot;:&quot;54524156455354494450414C45524D4F&quot;,&quot;handshakeKeyIndex&quot;:1,&quot;lastUpdatedMillis&quot;:1589388053677,&quot;serialNumber&quot;:&quot;L4FTJ005R4&quot;,&quot;chip1FirmwareVersion&quot;:&quot;unknown&quot;,&quot;chip2FirmwareVersion&quot;:&quot;1.12.6&quot;,&quot;peripheralType&quot;:&quot;Lock&quot;}</string>
</map>

The information was used to feed the config.json file from the augustpy tool. In order to make the tool work in the right way with the August Smart Lock 3rd Generation, a patch must be added.

Just for the records, there is a way to extract the offline keys without rooting the phone described by Jmaxxz but I didn't test it.

Device setup and test

I used a Bluetooth adapter (HCI dongle CSR 4.0) and a Raspberry Pi 3 B to perform the tests.

The following is a picture of my setup:

The big white box is the Raspberry Pi connected to a power outlet (black wire) and a router (white wire). The small black device is the Bluetooth adapter.

Most of the tests consisted in changing parts of the code and data in packets to see how the lock behaved. As you can imagine, most of the times the lock refused the connections and discarded the packets. Anyway, as already mentioned, augustpy was very useful to understand the handshake process and the flow of the packets.

The following are some outputs from the tool:

  • status command:
poxyran@poxyran-virtual-machine:~/augustpy$ sudo python3 cli.py front --status
Subscription SEC request response:  {'rsp': ['wr']}
Subscription MCU request response:  {'rsp': ['wr']}
Writing command: 01000000ec8e352e8b1764e9885966e80f01 --> SEC_LOCK_TO_MOBILE_KEY_EXCHANGE command packet
Encrypted command: db0b7b97d6416c853b208e9827cf3af20f01
Receiving response: a4bb7553e6c3fda65e6b4d0c16bc4f7e0f01
Decrypted response: 020000005c86bc42285af7227a1f4c9a0f01
Response security checksum: 2588680058
Response message checksum: 2588680058
Writing command: 03000000cdb6199e911cd4e89f2c12790f01 --> SEC_INITIALIZATION_COMMAND command packet
Encrypted command: 2e2e9c5f698d8c8ad33366e0aabe8ea10f01
Receiving response: c57bdd17d4995caf2755f1d7a12c43700f01
Decrypted response: 04000000cdb6199e911cd4e89e2c12790f01
Response security checksum: 2031234206
Response message checksum: 2031234206
Writing command: ee02000c0200000000000000000000000200 --> get status command packet
Encrypted command: ced4d3c1a31371c04991b8a57d8541540200
Receiving response: 21727886b95ea23327c82f78d1f8c30a0200
Decrypted response: bb02003a0200000005000000000000000200
Response simple checksum: 0
locked
  • unlock command:
poxyran@poxyran-virtual-machine:~/augustpy$ sudo python3 cli.py front --unlock
Subscription SEC request response:  {'rsp': ['wr']}
Subscription MCU request response:  {'rsp': ['wr']}
Writing command: 010000007b9c0eb5e960b41c9b023d2e0f01 --> SEC_LOCK_TO_MOBILE_KEY_EXCHANGE command packet
Encrypted command: f980a0cf2eb4e66ebf0be1c8e323beef0f01
Receiving response: c25c1e3b7aeeb5a821bbc6bffc23ad420f01
Decrypted response: 020000006df1937efd36350594d7367c0f01
Response security checksum: 2083968916
Response message checksum: 2083968916
Writing command: 030000002d3f05d699e544d437dbb5550f01 --> SEC_INITIALIZATION_COMMAND command packet
Encrypted command: ab720c4aee99e588870ee117f264fc760f01
Receiving response: 6d7dc46d41ab2bed9e412bcc6893fd4d0f01
Decrypted response: 040000002d3f05d699e544d436dbb5550f01
Response security checksum: 1437981494
Response message checksum: 1437981494
Writing command: ee02000c0200000000000000000000000200 --> get status command packet
Encrypted command: d7a26088e5f6c163366d406282fdb60c0200
Receiving response: e699a4f614495624fbb12454de854d820200
Decrypted response: bb02003a0200000005000000000000000200
Response simple checksum: 0
Writing command: ee0a00060000000000000000000000000200 --> unlock command packet
Encrypted command: 71a665ab5b8c8f1e82dcb57b2053f2910200
Receiving response: a9ab67e48bb2255c5cf059bbe07f26100200
Decrypted response: aa0a004a0000000000000000000000000200
Response simple checksum: 0
unlocked
  • lock command:
poxyran@poxyran-virtual-machine:~/augustpy$ sudo python3 cli.py front --lock
Subscription SEC request response:  {'rsp': ['wr']}
Subscription MCU request response:  {'rsp': ['wr']}
Writing command: 0100000031c636f19d6fe13f31cae7ce0f01 --> SEC_LOCK_TO_MOBILE_KEY_EXCHANGE command packet
Encrypted command: fc10d6c43dbe6c918025981400dd6f2b0f01
Receiving response: 62052dab1523ba2ed63eb45b3d4f3ca80f01
Decrypted response: 020000008cea78ff8d9e423de57644c30f01
Response security checksum: 3276044005
Response message checksum: 3276044005
Writing command: 03000000fadbc5f1bd6b3ea246b8fb6b0f01 --> SEC_INITIALIZATION_COMMAND command packet
Encrypted command: ed8e516207b37a578e095140e11d13660f01
Receiving response: b0a08c579b9f60b83e164cefcc68c21b0f01
Decrypted response: 04000000fadbc5f1bd6b3ea245b8fb6b0f01
Response security checksum: 1811658821
Response message checksum: 1811658821
Writing command: ee02000c0200000000000000000000000200 --> get status command packet
Encrypted command: 119f5d93c939fc093e3c7a829072c30b0200
Receiving response: fbbf360f21b9b1a5c06cba38901cfcc30200
Decrypted response: bb02003c0200000003000000000000000200
Response simple checksum: 0
Writing command: ee0b00050000000000000000000000000200 --> lock command packet
Encrypted command: f30435b7f655b21495bc6505318470490200
Receiving response: d18577f6634d80f7fb68e9d1fabd9e160200
Decrypted response: aa0b00490000000000000000000000000200
Response simple checksum: 0
locked

As can be seen, there is a lot of information to learn about it. For example, we can see that before each command packet, there are two packets, SEC_LOCK_TO_MOBILE_KEY_EXCHANGE and SEC_INITIALIZATION_COMMAND, which are used to initialize the communication with the lock. Then, the status command is also used before sending either the lock or unlock commands in order to know the state of the lock before proceeding.

The good thing is that I could see the packets in an encrypted and decrypted manner and all the messages required to perform the different actions over the lock so it was very helpful to better understand what was going on under the hood.

MitM attack with Mirage

After reading all the available material, after looking at the August mobile application code and doing my own tests using augustpy, I was more than sure that establishing a communication with the lock without having the offline keys wasn't possible. But, what about performing a MitM attack against the BLE communication? The MitM attack that Jmaxxz performed against the web servers was more than successful, would I have the same luck?.

Currently, several frameworks exist that allow to perform a MitM attack against Bluetooth devices.

GATTacker was my first choice. My experience with it was not good. I had several troubles installing the dependencies (noble and bleno). At some point, I gave up trying to make it run and discarded it.

My second option was BtleJuice. The installation was very straightforward and it's even more simple to run it. In this case, I used a VMware virtual machine as the central and a Raspberry Pi 3 B as a proxy. The bad thing was that I wasn't able to intercept any packet, the lock was continuously disconnecting, the proxy was shutting down and I had to restart the services (central and proxy) from time to time. To be honest, I don't know about working with other devices, but regarding the August Smart Lock, the things weren't too stable.

When I was about to throw in the towel, I recalled a framework presented at SSTIC 2019, called Mirage. In my understanding, this framework works a little bit differently than the previously mentioned tools. Instead of cloning the devices it just forwards the packets between the device and the mobile phone. It's really simple to install, really simple to use, stable and extendable through modules and scenarios.

Mirage is not only a framework to work with Bluetooth Low Energy but also with other protocols like ShockBurst, Mosart, Zigbee and 802.11.

I set up Mirage in the same Raspberry 3 B I used for augustpy and used the raspi internal Bluetooth device plus another HCI 4.0 Bluetooth dongle. But it also supports many others devices such as Ubertooth, BTLEJack and NRFSniffer.

Once I had everything set up, I launched Mirage and conducted the MitM attack using the ble_mitm module. The logs obtained when performing the MitM attack can be seen here.

What now? I was able to intercept the traffic between the lock and the mobile phone but what to do with this? Well, when playing with augustpy I noted that the last two bytes of the data packets were the same when encrypted and unencrypted. For example, for the status/lock/unlock commands, these bytes are \x00\x20 and for SEC_LOCK_TO_MOBILE_KEY_EXCHANGE and SEC_INITIALIZATION_COMMAND, their values are \x0f\x01. So, the question was, is it possible to identify these packets? What to do next? If the lock packet can be identified, then, a potential attacker may perform a MitM attack against the lock so that when the owner of the house sends the lock command using the mobile app, the attacker may identify that packet and discard it, avoiding the door to be locked. Game over!.

In order to test that theory, I wrote a scenario for the ble_mitm module from Mirage.

Identifying a packet using a scenario

A scenario is just a plugin to extend or change the behavior of a module, in this case, the ble_mitm module. Here's the code I used:

from mirage.core import scenario
from mirage.libs import io,ble,esb,utils

class august_ble_mitm(scenario.Scenario):

        def onStart(self):
                # This signal is triggered when the module starts
                self.a2sEmitter = self.module.a2sEmitter # Attacker to Slave emitter
                self.a2sReceiver = self.module.a2sReceiver # Attacker to Slave receiver
                self.a2mEmitter = self.module.a2mEmitter # Attacker to Master emitter
                self.a2mReceiver = self.module.a2mReceiver # Attacker to Master receiver
                return True # The default behaviour is executed

        def onMasterWriteCommand(self, packet):
                io.info("XXX: Intercepting packet data on MasterWriteCommand.")
                packet.show()
                io.info("Packet handle: %x" % packet.handle)
                io.info("Packet data: %s" % packet.value)
                if packet.handle == 0x12:
                        io.info("Possible lock/unlock command received..")
                        if packet.value[-2:] == b"\x02\x00":
                                io.info("--> Yep! Lock/Unlock command received!. Dropping packet.")
                                return False
                io.info("XXX: DONE!")

                return True

        def onEnd(self):
                return True

        def onKey(self,key):
                return True

As you can see, I use the handle of the characteristic plus the last two unencrypted bytes in order to perform the identification. If I find a packet with these two conditions, then I discard it.

However, by doing this, the mobile app is alerted that something unusual is happening:

and the mobile app gets locked in a lock/unlock state until the MitM is shutdown:

Event though the two bytes of each packet are unencrypted, these bytes are common for the three types of packets (status/lock/unlock) so it is difficult to unequivocally establish which packet I'm looking at. You can't tell the difference between the status/lock/unlock and the SEC_LOCK_TO_MOBILE_KEY_EXCHANGE/SEC_INITIALIZATION_COMMAND packet. Neither can the size of the packets be used as a indicator because all the packets have a size of 18 bytes.

So, the best thing so far is a DoS against the lock :(. In the mentioned conditions, dropping the lock and unlock packets every time we receive them, the MitM attack can be used as a DoS (Denial of Service) attack against the lock because the lock becomes unresponsive.

Any other thought?

Another idea was to use a replay attack. The scenario is the following: the home owner arrives at the door, the attacker starts performing a MitM, the home owner unlocks the door, enters the house and locks it again. The attacker was capable to capture the packets in the communication. Then, can the attacker use the captured packets to unlock the door? The paper titled Security Analysis of the August Smart Lock has a section dedicated to this topic in which the authors claim that a replay-like attacks is not possible due to several reasons:

  • The sequence of bytes used by the unlock command can't be identified unequivocally.
  • The unlock command is not a single command (is composed by an exchange of requests and answers between the smartphone and the lock).
  • A session key is used during the exchange.

Conclusion

This blog post was used to show how the August Smart Lock works, how it is protected and what attacks were tried against it so far.

Regarding my particular approach, my idea was to study the BLE communication protocol and the handshake process between the lock and mobile application. I showed that establishing a communication is not as simple as it looks and that the August developers implemented some mechanisms in order to avoid attacks such as MitM, brute-forcing and replay attacks.

Even though I didn't discover any vulnerability, having now gained some experience, I would try another way to attack the lock in a more global way, let's say. For example, trying to attack several components at a time instead of concentrating in just one piece.

The good thing is that the BLE communication between the lock and the mobile application looks solid. It's difficult, almost impossible, to build a successful attack against it without having the offline keys.

I hope you enjoyed the reading and it was helpful to you! Feel free to leave a comment below!

Comments