Security Embedded is 12+ years of experience in building secure systems. Learn more about how we can help you by exploring Phil's blog or contacting us.

Plaintext Symmetric Keys, fixed IVs, oh my!

Switching gears, let's talk about block ciphers and communications protocols. IoT devices are resource constrained. Oftentimes vendors will go to great lengths to avoid using TCP. This is not a bad decision on its own: UDP is stateless. But that means you're rolling your own resiliency in.

This is just a design decision. But what if you're trying to bake in some level of security?

TLS over UDP is a thing. That's a good option, but you need a reasonable amount of RAM to manage that. Maybe this is where your architects could step in and pick a different, beefier part?

Let's go back to our 88MC200-based device. The control application for this device runs on your Android or iOS device. There are now two challenges to deal with:

  • How do we provision the IoT device with with a target BSSID, as well as WPA credentials?
  • How do you keep communications secure once the device is on the network?

These guys screwed up both of these problems. Let's focus on the second problem, as the first one is worthy of a blog post on its own.

Block Cipher Modes 101

We've discussed in the past that a symmetric key is only as secure as the mechanism used to store it. This much is obvious, but we've glazed over some important implementation details in the past. When applying block ciphers, you could break the plaintext into single block-size chunks. So for AES, we could take our plaintext, break it into 128-bit blocks, then use our key to encrypt each block. We refer to this block cipher mode as Electronic Code Book, or ECB. A visual will help us understand why this is a bad idea, though.

Here is our secret message (an image in this case):

Let's use some OpenSSL-fu (sorry!) to encrypt this using AES-128-CBC, with the key testtest12341234:

openssl enc -aes-128-ecb -nosalt -in SecretMessage.pnm -out SecretMessageEncrypted.pnm -k testtest12341234

Here is our secret message after encrypting it with AES-128-ECB:

Here's the thing - each ECB block gets separate treatment. So that means there's no way to carry some entropy through the encryption process. This leaves the encrypted output block highly correlated with the input block.

The next mode most people will encounter is Cipher Block Chaining, or CBC. With CBC you take the ciphertext of the prior block and XOR it with the plaintext of the block you're encrypting. As you can imagine, the first block needs some sort of value to be its "prior block" in order for this to work. This is where an Initialization Vector, or IV, comes into play. An IV is a nonce (a number used once), unique to a session. When encrypting our above message with AES-128-CBC with the IV 0xdeadbeefcafebabe (a poor example, admittedly), our secret message looks a little more random:

So it's obvious that AES-128-CBC gives us better security for our message. Now where can this go wrong?

Fixing the IV: It's Easy, but Wrong

Remember: when a device or software is in the physical possession of an attacker, there's not much you can do. A resourceful and wealthy (or unemployed and motivated) attacker will find flaws in your system and exploit them.

Protocols that rely on AES to provide security are everywhere. The hard part is swapping keys between two devices. Various rigorous key exchange protocols have come up over the years, like Diffie-Hellman. This relies on public-key cryptography to exchange symmetric keys (and other nonces) to use during a session. But if you don't have a concept of a session, how do you exchange those keys?

One common and tragic mistake is to fix the IV for AES-CBC. If your entire cryptosystem uses the same IV for all communications, you're lowering the bar for attack. This makes device class attacks a lot easier: all you have to do is steal the key when the devices swap them. Finding IVs using static analysis can be tricky - often they look like random data. However, when you use polarSSL's AES functions and neglect to strip symbols from your Android shared object file:

Then looking at the first function that calls aes_crypt_cbc:

Then following the trail of breadcrumbs (AES_IV_2 is my name for it):

Bingo, we have the IV. This is in .rodata in the ELF image, so we can be fairly sure this isn't changing. Analyzing the calls to the aes_* functions, this is definitely the case. 

So how about getting at the keys...?

Unencrypted Keys? In MY smartphone?!

For the 88MC200-based device, once paired with your smart device, they swap a key. They will then use that key to encrypt all further communications. Rooting around in the device's directory in /data on our rooted tablet, we find three SQLite3 databases:

com.XCompany.XXX/databases> ls -l
total 704
-rwxrwxrwx  1 philippe  staff   24576 Jul 10 20:21 rep.db
-rwxrwxrwx  1 philippe  staff    8720 Jul 10 20:21 rep.db-journal
-rwxrwxrwx  1 philippe  staff  172032 Jul 10 20:21 XXX.db
-rwxrwxrwx  1 philippe  staff   12824 Jul 10 20:21 XXX.db-journal
-rwxrwxrwx  1 philippe  staff   24576 Jul 10 20:21 service.db
-rwxrwxrwx  1 philippe  staff    8720 Jul 10 20:21 service.db-journal

Let's fire up our sqlite client and look at the schema:

> sqlite3 XXX.db
SQLite version 2015-05-20 18:17:19
Enter ".help" for usage hints.
sqlite> .schema
-- snip
CREATE TABLE `deviceTable` (`deviceMac` VARCHAR , `deviceName` VARCHAR , `publicKey` BLOB , `id` BIGINT , `longitude` DOUBLE PRECISION , `latitude` DOUBLE PRECISION , `news` SMALLINT , `deviceType` SMALLINT , `subDevice` INTEGER , `devicePassword` INTEGER , `deviceLock` INTEGER , `terminalId` INTEGER , PRIMARY KEY (`id`) );
-- snip

We've registered our device with the app, so presumably it will show up in the deviceTable, right?

sqlite> select * from deviceTable;
xxxxxxdc8cfb|DeviceType|??g (e?_?T?K?n|1|0.0|0.0|0|10039|0|133942289|0|1

Yup. That first field is the MAC address, the second is a device name (altered to protect the company). What about that third field 'publicKey'? This seems too short to be a public key crypto key. How long is it exactly?

sqlite> select length(publicKey) from deviceTable;

A 16 byte blob. This could well be an AES-128 key - too short to be a real public key. Let's dump it for some further research:


The next step is to use this information to decrypt some data going over the air. We'll need a better mechanism to retrieve the device AES key for a broader attack. But, knowing the device stores keys in plaintext is going to make things easier.

Next time we'll discuss analyzing network traffic using these keys. Hope you have your rogue access point ready to go!

How Trustworthy is that App with those Capabilities?

What's in a Firmware Load?