Pwn the ESP32 crypto-core

A crypto-core (also called crypto-accelerator) is a dedicated piece of hardware inside the System-on-Chip. Its main role is to ‘accelerate’ cryptographic primitives and to perform keys management.

This post presents several vulnerabilities and fault injection exploits targeting the crypto-core implementation, allowing an attacker to:

  • Bypass the HW-AES encryption
  • Control the AES key value

The vulnerabilities have a direct impact in ARM MbedTLS because AES acceleration is set by default on ESP32. Of course, Espressif’s crypto-library called hwcrypto is impacted as well.

Other vulnerabilities on SHA functions were also reported.

Espressif and I decided to go to responsible disclosure process.

Focus on ESP32 system

The block diagram

ESP32 is a nice little piece of hardware

According to this diagram, the Cryptographic HW accelerator is a dedicated block, which could be seen as a peripheral. It can provide HW acceleration to perform SHA, AES, RSA (bignum) and RNG.

About the System Clock

Let’s talk about the System clock.

CPU Clock (CPU_CLK) can be set to 80MHz, 160MHz (default) or even 240MHz, by setting the sdkconfig.

Inside the system clock diagram providing by manufacturer, tow signals are interesting, the CPU_CLK (CPU Clock) and also by the APB_CLK (Advanced Peripheral Bus Clock):

ESP32 System Clock

But according to the Peripheral Clock Usage, it seems not be used by the hardware Crypto block:

Crypto Accelerator not present in the peripheral list?

Consequently, I assume the HW crypto is using the CPU_CLK as main clock.

I decide to not modify the default CPU clock frequency during these tests (160MHz by default).

Target n*1: AES accelerator

According to the ESP technical reference manual here, the AES accelerator speeds up AES operations significantly (compared to AES algorithms done in software).

The first important information is the following:

11 to 15 cycles for an AES encryption, nice.

I am not a crypto-expert but 11 to 15 cycles to encrypt a message means one cycle by AES round (depending on the AES key size) + one extra cycle to unload the cipher text on the bus. It means also the AES key schedule is achieved in parallel.

Doing simple math, the HW AES-128 encryption process is 68.75ns (@160MHz).

Effectively that’s speed up the computation! (Software AES encryption was estimated at 50us approximately in the previous post).

Design weakness identification

The different steps to perform a single HW AES operation are explained below:

AES_MODE_REG register sets the AES mode (128 192 or 256, encryption/decryption), AES_KEY_n_REG registers store the key, AES_TEXT_m_REG registers store the plaintext message…but also the encrypted results. Interesting…

Confirmation in the ESP-IDF v4.0-dev code

The code is open source and can be found on the espressif GitHub here.

Reviewing the code (when it is available) is a nice source of information, especially when you are looking for bugs into crypto libs.

If we take a look to the dedicated aes_esp_block() in aes.c, the code is matching perfectly on what we saw earlier. It means the array mem_block in memory defines the input buffer but also the output buffer:

esp_aes_block() in esp-idf v4.0-dev-141

Here, I see a potential HW weakness to exploit. If I can avoid the while loop to be executed, I will skip the AES encryption. I can also try to skip the accelearator activation DPORT_REG_WRITE(AES_START_REG, 1).

Let’s see if I can prove that during the testing phase.

The Setup

Same setup than described previously. Full description here.

Full Diagram

Full attack setup

PCB modification

I modified VDD_CPU to connect the output of my limited voltage glitcher:

VDD_CPU connected to the glitcher’s output

Flashing the board

It is time to write a dirty code and compile with AES acceleration enabled (CONFIG_MBEDTLS_HARDWARE_AES=Y) and flash the ESP32.

The Results

Bypass of AES accelerator Encryption

Here is a view of the scope during the fault session:

CH1 =UART, CH2 = power consumption, CH3= command, CH4=GPIO (trigger)

Note: I cannot really see on the scope where the HW AES starts, my scope bandwidth is too narrow 🙁

In case of successful bypass of HW encryption, the log displays:

...
----- Cipher 2 ----- 
 key   : 61616161616161616161616161616161
 plain : 30303030303030303030303030303030
 cipher: c7fa6283f707ec9e55b6dd900bdb0bc1
----- Cipher 3 ----- 
 key   : 61616161616161616161616161616161
 plain : 30303030303030303030303030303030
 cipher: 30303030303030303030303030303030
!!!! AES core Pwned !!!!

That’s all. AES encryption is bypassed and plaintext is returned as output.

Dirty PoC

Original PoC

The Patch from Espressif

The vulnerability was fixed pretty quickly by Espressif …maybe even too quick 🙂…Nevertheless, it is interesting to take a look to the patch:

esp_aes_block() in esp-idf-v4.0-dev-667

A check has been added at the end, doing zeroisation of the AES output (in the case of an AES bypass of course).

Note: not the best way to protect…just my opinion.

Others Vulns on AES SetKey()

Here, I review a little bit the esp_aes_setkey() function, used by ESP hwcrypto library in esp-idf-v4.0-dev-667 but also by MbedTLS (version 2.13.1).

Vuln N*1: unprotected memcpy()

esp_aes_setkey() in esp-idf-v4.0-dev-667

The memcpy() function is not protected.

Vuln N*2: esp_aes_setkey_hardware() copy loop

Here is the function in charge to load the key in the crypto-core:

esp_aes_setkey_hardware()

If the loop is skipped or bypassed by fault injection effect, the key will not be loaded into the dedicated AES_KEY registers.

Consequently, I identify potential weaknesses by faulting the memcpy in aes_esp_setkey() or by faulting the copy loop in aes_esp_setkey_hardware(). For example, if I set the entire value of key to 0x00*16, then I can retrieve the plaintext data (because I know this key will be used for the next AES computations).

More Results

Here is a view of the scope during the fault session, where I focus on the esp_aes_setkey_hardware():

CH1 = UART, CH2 = GPIO (trigger), CH3 = command, CH4 = power consumption.

In case of successful control of the Key value, the UART log displays:

...
----- Cipher 339 ----- 
- key   : 61616161616161616161616161616161
- plain : 30303030303030303030303030303030
- cipher: c7fa6283f707ec9e55b6dd900bdb0bc1
----- Cipher 340 ----- 
- key   : 61616161616161616161616161616161
- plain : 30303030303030303030303030303030
- cipher: e08682be5f2b18a6e8437a15b110d418
!!!! Set key Pwned !!!!

The PoC

Setkey set to all 0x00

The key value is now 0x00*16. It means the all the AES ciphertexts can now be decrypted/forged because the attacker control the key value.

A python AES script confirms that:

$ python analyze_setkey_faulted.py 
key:00000000000000000000000000000000
enc:e08682be5f2b18a6e8437a15b110d418
dec:30303030303030303030303030303030

Done. That’s all for AES crypto-core.

Conclusion

This post just showed why using a crypto-accelerator is not always good idea, at least to increase the security.

First, a vulnerability into the AES core design have been identified and exploited by fault injection, due to naive implementation of drivers managing the crypto-core.

Then, two vulnerabilities into the setkey function have been identified and one PoC has been demonstrated using also fault injection.

These vulns allows an attacker to bypass the AES encryption or to set the AES key to a know value.

The MbedTLS library and esp-idf v4.0 are impacted.

Other vulnerabilities on SHA algorithms were also reported.

Stay tuned, more hacks to come! (Serious stuffs about Secure boot & Flash Encryption my friends…)

Timeline disclosure

20/04/2019: First e-mail to Espressif to announce critical vulnerabilities.

21/05/2019: Second e-mail to Espressif and ARM MBed to list all the vulnerabilities.

09/06/2019: Third e-mail to ARM (the real ARM).

10/06/2019: ARM MBedTLS answer “unfortunately, we don’t have a bounty program anymore”… so this website is fake, isn’t it? unbelievable…

17/06/2019: Full report on AES bypass + PoC sent to Espressif.

18/06/2019: Espressif acknowledges receiving the report.

28/06/2019: Full report on AES setkey + PoC sent to Espressif.

05/07/2019: Espressif acknowledges receiving the report.

11/07/2019: Full report on SHA + PoCs sent to Espressif.

12/07/2019: Espressif acknowledges receiving the report. Agree for 30-days disclosure.

11/08/2019: Posted.

The story continues…

2 Replies to “Pwn the ESP32 crypto-core”

  1. Hi, great post. This is very valuable learning material. I just wanted to point out that Espressif seems to have taken a corrective action with regards to Vuln N#2: esp_aes_setkey_hardware

    Here is a link to that function as of today’s master branch. (I intentionally linked to the commit rather than ‘master’ to avoid it changing over time):

    https://github.com/espressif/esp-idf/blob/c41b2b04154c9fa36dbbd6282b3657a3a480e068/components/mbedtls/port/esp32/aes.c#L111

    It uses a counter to ensure that every iteration of the foor loop was properly executed. I’m not affiliated to anything, just thought I’d work through your examples as a learning experience and saw this update in their code.

    1. Hi, thanks for your message.
      It is generally what happens once you report vulns, vendors patch them 😉
      Regards
      LR

Leave a Reply

Your email address will not be published. Required fields are marked *