The Samsung Phone Vulnerability Isn’t Just “An Old Attack”, it is Extremely Bad Coding ...

It’s like WEP all over again

Photo by Clément Hélardot on Unsplash

The Samsung Phone Vulnerability Isn’t Just “An Old Attack”, it is Extremely Bad Coding ... (or a Back Door!)

It’s like WEP all over again

I wrote up an article on the latest Samsung vulnerability [here], and one comment said … “it's an old bug, reuse of IV seem a very basic problem”. On the face of it, the comment perhaps doesn’t go into enough detail, so I’ll try and explain the “bug” and hopefully show that it is shockingly bad coding … almost negligent in terms of protection, and could even be seen as an intentional backdoor. And for a “very basic problem”, it should perhaps be “extremely bad coding”, and this “bug” should never, ever be seen within trusted environments. It shows an almost complete lack of knowledge in how cryptography works, with a novice vulnerability. The paper is here [1]:

In fact, it’s like WEP all over again, and where the WEP Wifi method had a small IV (Initialisation Vector), and when it rolled out, it was possible to just XOR cipher streams, and discover the plaintext. The asleep program could crack any Cisco access point in less than a day. Luckily we now use WPA-2, and which does not have the reuse of the IV.

I hope to show that we should be worried if code such as this ever gets near a user’s device. In fact, if there was ever a back door in a mobile phone, it could be this one.

If you want to read about the “bug”, try here:

A bad “bug”

Now, I will explain how bad this “bug” is. If you are into cybersecurity, you should hopefully know that AES GCM is a stream cipher. With this, we take a secret key value and a salt value (an IV — Initialisation Vector) and generate a pseudo infinite keystream. Our plaintext is then simply XOR-ed with the keystream to produce our ciphertext:

The salt value should then always be random, as a fixed salt value will always produce the same keystream for the same plaintext, and where we can reveal the keystream by XOR-ing cipher streams, and eventually revealing the plaintext. In the case of the key wrapping, the plaintext is an encryption key, and thus the encryption key used by the TEE will be revealed.

If we reuse IVs, Eve will be able to XOR cipher streams together and reveal the keystream (K). From there she can decrypt every cipher stream, but simply XOR-ing the cipher stream with K.

Coding

AES GCM (Galois Counter Mode) is a stream cipher mode for AES. It is based on the CTR mode but converts to a stream cipher. This provides low latency in the encryption/decryption process and is fast to process. Along with this, it integrates AEAD mode for authentication. But as GCM is a stream cipher mode it is open to a reuse IV attack. With this, the IV (Initialization Vector) of the cipher is the same for two cipher messages. We can then XOR to the two cipher streams together to reveal the cipher stream key (K). We can then reveal the plaintext by XOR-ing any cipher stream with K.

So, let’s try some code to do this. In this case, I will use Golang to show the basic principles of the method. I will use a static key in this case (as this would not change within the TEE) of “0123456789ABCDEF” (16 bytes — 128-bit key), and a static nonce of “0123456789AB” (12 bytes — 96 bits) [here]:

package main

import (
"crypto/aes"
"crypto/cipher"
"fmt"
"os"
)

func xor(a, b []byte, length int) []byte {
c := make([]byte, len(a))
for i := 0; i < length; i++ {
c[i] = a[i] ^ b[i]
}
return (c)

}

func main() {

nonce := []byte("0123456789AB")

key := []byte("0123456789ABCDEF")

block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}

msg1 := "hello"
msg2 := "Hello"

argCount := len(os.Args[1:])
if argCount > 0 {
msg1 = (os.Args[1])
}
if argCount > 1 {
msg2 = (os.Args[2])
}

plaintext1 := []byte(msg1)
plaintext2 := []byte(msg2)

aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}

ciphertext1 := aesgcm.Seal(nil, nonce, plaintext1, nil)
ciphertext2 := aesgcm.Seal(nil, nonce, plaintext2, nil)

xor_length := len(ciphertext1)

if len(ciphertext1) > len(ciphertext2) {
xor_length = len(ciphertext2)
}

ciphertext_res := xor(ciphertext1, ciphertext2, xor_length)

fmt.Printf("Message 1:\t%s\n", msg1)
fmt.Printf("Message 2:\t%s\n", msg2)
fmt.Printf("Cipher 1:\t%x\n", ciphertext1)
fmt.Printf("Cipher 2:\t%x\n", ciphertext2)
fmt.Printf("Key:\t\t%x\n", key)
fmt.Printf("Nonce:\t\t%x\n", nonce)
fmt.Printf("XOR:\t\t%x\n", ciphertext_res)

plain1, _ := aesgcm.Open(nil, nonce, ciphertext1, nil)
plain2, _ := aesgcm.Open(nil, nonce, ciphertext2, nil)

fmt.Printf("Decrypted:\t%s\n", plain1)
fmt.Printf("Decrypted:\t%s\n", plain2)

}

When we run with “hello” and “Hello” we get [here]:

Message 1:	hello
Message 2: Hello
Cipher 1: 7fcbe7378c2b87a5dfb2803d4fcaca8d5cde86dbfa
Cipher 2: 5fcbe7378cf8c68b82a2b8d705354e8d6c0502cef2
Key: 30313233343536373839414243444546
Nonce: 303132333435363738394142
XOR: 2000000000d3412e5d1038ea4aff840030db841508
Decrypted: hello
Decrypted: Hello

If we try “hello” and “Cello”, we can see that there’s a variation in the first byte of the cipher stream:

Message 1: hello
Message 2: Cello
Cipher 1: 7fcbe7378c2b87a5dfb2803d4fcaca8d5cde86dbfa
Cipher 2: 54cbe7378c5638db82df34a46172abed62b887aa48
Key: 30313233343536373839414243444546
Nonce: 303132333435363738394142
XOR: 2b000000007dbf7e5d6db4992eb861603e660171b2
Decrypted: hello
Decrypted: Cello

The “bug” allowed a user program to request their own IV, which meant that a cracker would continually request the same IV, and then break the cipher, and reveal the encryption key used. This is because the TEE (Trusted Execution Environment) uses key wrapping to encrypt the encryption key with AES GCM, so a cracker can request these wrapped keys, but with their own IV. This then reveals the key that the TEE is using. This is extremely bad coding!

Conclusion

This is extremely bad coding, and I would not expect this level of implementation from a new graduate. If a development team creating code within a TEE doesn’t understand a reuse IV attack, they need to go on a secure coding training course before they ever touch any more trusted code. If this was an intentional backdoor, that’s a whole other story. I hope, it was just a bug, but we really need to improve our knowledge in the creation of TEEs, as these also run within Cloud-based systems.

Reference

[1] Shakevsky, A., Ronen, E., & Wool, A. (2022). Trust Dies in Darkness: Shedding Light on Samsung’s TrustZone Keymaster Design. Cryptology ePrint Archive.