Back


Widevine (Part 4) - Security Levels and Keyboxes

Posted by Amir Mazzarella on March 30, 2019


Throughout my blog posts on Widevine, there have been talks on this concept of a “security level.” What even is a security level anyways? Well, in terms of Widevine, there’s three. Level 3, Level 2, and Level 1, ordered from least secure to most secure. So, what’s the difference between each security level? Well…

Level 3:

It’s a good chance the computer you’re reading this on is running Widevine L3. Widevine L3 is the most “insecure” security level, and that’s because it only runs in the software. Since it only runs in the software, it can only handle the two most insecure license policies. As a side note, the difference between CRYPTO and DECODE is that the CRYPTO license policy forces the Widevine CDM to decrypt the protected content, but gives those decrypted frames to the native video decoder for playback. The DECODE policy forces the Widevine CDM to both decrypt and decode the protected content, handing the decoded frames directly to the display. SW_SECURE_DECODE, because it’s purely software, is a bit more taxing than SW_SECURE_CRYPTO because DECODE can’t take advantage of hardware-accelerated decoders since it has to run in a software sandbox. I actually mentioned this as being the reason for why frames can drop when using Netflix-1080p. So, technically SW_SECURE_DECODE is more secure, but besides Amazon, Netflix, and Google Play, I haven’t seen really anyone use it over CRYPTO.

Level 2:

The weird thing about L2 is that I’ve never seen a device have it. Usually, if a device has a trusted environment capable of housing hardware DRM, it’s just going to go all the way and have L1 security. I actually don’t know what would warrant just having decryption/decoding done in a TEE and not everything else. Perhaps if the TEE is not fully trusted?

Level 1:

Level 1, the pinnacle of Widevine security. It’s entire security, though, is based on how secure the trusted environment is. And, as you’ll see later in the post, they’re not always secure. L1 is usually the level content providers require to stream 4K content, which is why you can only stream 4K on specific devices. The way L1 works is by having what’s called a “root of trust.” In terms of Widevine, that root of trust is called a “keybox.” The keyboxes are provisioned onto devices at the factory and stored in the trustzone. No matter how many times you format a device, the keybox will always remain. The first time you play a Widevine protected video on a level 1 device, the device will “provision” the keybox. Provisioning is the process of contacting Widevine’s servers with the keybox asking for a private RSA keypair and client ID. It then takes the private key, encrypts it, stores the encryption key in the trustzone, and stores the encrypted keypair and client ID in userland. As a reminder, all of these operations are done in the trustzone. After provisioning, you are free to play any Widevine video you wish.

So, how do we break it?

To break level 1, we need to break the trustzone. Luckily, people have done this before. In fact, someone already wrote an exploit to obtain a Widevine keybox from the trustzone off a Nexus 6: https://bits-please.blogspot.com/2016/05/qsee-privilege-escalation-vulnerability.html. It involves exploiting “trustlets.” Trustlets, on an android, are userland executables that are able to communicate with the trustzone. Gal, the author of the post, exploited the trustlets to force the QSEE API to do whatever he wanted it to do, so he asked it to give him the Widevine keybox. He then reported the vulnerability to Google, who promptly updated the trustlets. However, since Google never implemented a form of trustlet revocation, you could just throw the old vulnerable trustlets on an Android and use the exploit: https://bugs.chromium.org/p/project-zero/issues/detail?id=1167. Google couldn’t fix it. If you have a rooted Nexus 6, go ahead and compile the exploit, put the vulnerable trustlets on your device, ADB into your device, and run it! You’ll get a keybox output (remember to convert each line from little-endian to big-endian). If you don’t have one, here’s a keybox I dumped off a friend’s Nexus 6 (base64 encoded):

TU1JX0VGRkYwRkRCMkZCOAAAAAAAAAAAAAAAAAAAAAAiN1X8/UFUWlu8rAeSjW2eAAAAAgAAFmJI4Gn+fRydxTBSdW/uThlGcUI5DPLLbjGaQleQ7wvao6EUHT3M6jQSwQZWG4CbwRRW4ufn51x7/2Img12bjXnxa2JveFHqjThMVkwx

It may look like just a bunch of bytes, but it actually has a structure. Here’s a Python struct for the keybox:

KEYBOX_STRUCT = Struct(
    'DeviceID' / String(32),
    'Key' / Bytes(16),
    'ID' / Bytes(72),
    'Magic' / String(4),
    'CRC32' / Bytes(4),
    'Tag' / String(4)
)

Printing the keybox I provided with that struct yields:

Container: 
    DeviceID = b'MMI_EFFF0FDB2FB8'
    Key = b'"7U\xfc\xfdATZ[\xbc\xac\x07\x92\x8dm\x9e'
    ID = b'\x00\x00\x00\x02\x00\x00\x16bH\xe0i\xfe}\x1c\x9d\xc50Ruo\xeeN\x19FqB9\x0c\xf2\xcbn1\x9aBW\x90\xef\x0b\xda\xa3\xa1\x14\x1d=\xcc\xea4\x12\xc1\x06V\x1b\x80\x9b\xc1\x14V\xe2\xe7\xe7\xe7\\{\xffb&\x83]\x9b\x8dy\xf1'
    Magic = b'kbox'
    CRC32 = b'Q\xea\x8d8'
    Tag = b'LVL1'

The “Key” part of the struct is actually the secret key. How provisioning works, is that the device sends a POST request to a provisioning server with the ID of the keybox in the request body. The server sends back an encrypted blob, encrypted against that keybox secret key.

That’s going to end this blog post, in the next one, I’ll actually go into provisioning more in-depth and show you how to provision that keybox I provided. Thanks for reading!


   WIDEVINE    KEYBOX    DRM   

 Share on: Twitter / Facebook / Google+ / Email