bdring / FluidNC

The next generation of motion control firmware
Other
1.59k stars 382 forks source link

Prevent flashing (bricking) ESP32 if secureboot and/or flashencryption is enabled #149

Closed m-sja closed 2 years ago

m-sja commented 2 years ago

Hi, i just bricked a voidmicro.com laserenvraver board by flashing fuidnc using the windows batch script. After uploading the esp stuck in a boot loop:

ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x37 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371

The upload went flawlessly and i was able to verify the flash contents by using esptool.exe.

The issue is caused by secureboot and flashencryption set by the manufacturer. These options do not prevent the user from flashing the device. Flashing unencrypted binarys will render the device useless. These security options cannot be disabled once activated.

Please refer to: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/secure-boot-v1.html https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html https://limitedresults.com/2019/11/pwn-the-esp32-forever-flash-encryption-and-sec-boot-keys-extraction/

After flashing the esp32 the original flash-contents are no longer present. Using the described attack vector to obtain the AES Keys is no longer possible (and far to complicated).

I end up replacing the esp32 on that board.

The flash-scripts should be extended to use expfuse.exe prior to flashing the device. If security options are set, inform the User: This chip is not able to run any other firmware than the original due to security options set by the manufacturer! Using other flash methods may seem to work, but will brick the device! Don't do it, SERIOUSLY!

Here is a espfuse-summary of my bricked esp32:

C:\Users\m-sja\Downloads\fluidnc-v3.2.5\win64>espefuse.exe -p com7 -b 115200 -c esp32 summary
Connecting...
Device PID identification is only supported on COM and /dev/ serial ports.
...........
espefuse.py v3.2
EFUSE_NAME (Block) Description  = [Meaningful Value] [Readable/Writeable] (Hex Value)
----------------------------------------------------------------------------------------
Calibration fuses:
BLK3_PART_RESERVE (BLOCK0):                        BLOCK3 partially served for ADC calibration data   = False R/W (0b0)
ADC_VREF (BLOCK0):                                 Voltage reference calibration                      = 1100 R/W (0b10000)

Config fuses:
XPD_SDIO_FORCE (BLOCK0):                           Ignore MTDI pin (GPIO12) for VDD_SDIO on reset     = False R/W (0b0)
XPD_SDIO_REG (BLOCK0):                             If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset    = False R/W (0b0)
XPD_SDIO_TIEH (BLOCK0):                            If XPD_SDIO_FORCE & XPD_SDIO_REG                   = 1.8V R/W (0b0)
CLK8M_FREQ (BLOCK0):                               8MHz clock freq override                           = 50 R/W (0x32)
SPI_PAD_CONFIG_CLK (BLOCK0):                       Override SD_CLK pad (GPIO6/SPICLK)                 = 0 R/W (0b00000)
SPI_PAD_CONFIG_Q (BLOCK0):                         Override SD_DATA_0 pad (GPIO7/SPIQ)                = 0 R/W (0b00000)
SPI_PAD_CONFIG_D (BLOCK0):                         Override SD_DATA_1 pad (GPIO8/SPID)                = 0 R/W (0b00000)
SPI_PAD_CONFIG_HD (BLOCK0):                        Override SD_DATA_2 pad (GPIO9/SPIHD)               = 0 R/W (0b00000)
SPI_PAD_CONFIG_CS0 (BLOCK0):                       Override SD_CMD pad (GPIO11/SPICS0)                = 0 R/W (0b00000)
DISABLE_SDIO_HOST (BLOCK0):                        Disable SDIO host                                  = False R/W (0b0)

Efuse fuses:
WR_DIS (BLOCK0):                                   Efuse write disable mask                           = 388 R/W (0x0184)
RD_DIS (BLOCK0):                                   Efuse read disable mask                            = 3 R/W (0x3)
CODING_SCHEME (BLOCK0):                            Efuse variable block length scheme
   = NONE (BLK1-3 len=256 bits) R/W (0b00)
KEY_STATUS (BLOCK0):                               Usage of efuse block 3 (reserved)                  = False R/W (0b0)

Identity fuses:
MAC (BLOCK0):                                      Factory MAC Address
   = 40:f5:20:81:b4:04 (CRC 0xe0 OK) R/W
MAC_CRC (BLOCK0):                                  CRC8 for factory MAC address                       = 224 R/W (0xe0)
CHIP_VER_REV1 (BLOCK0):                            Silicon Revision 1                                 = True R/W (0b1)
CHIP_VER_REV2 (BLOCK0):                            Silicon Revision 2                                 = False R/W (0b0)
CHIP_VERSION (BLOCK0):                             Reserved for future chip versions                  = 2 R/W (0b10)
CHIP_PACKAGE (BLOCK0):                             Chip package identifier                            = 1 R/W (0b001)
MAC_VERSION (BLOCK3):                              Version of the MAC field                           = 0 R/W (0x00)

Security fuses:
FLASH_CRYPT_CNT (BLOCK0):                          Flash encryption mode counter                      = 1 R/- (0b0000001)
UART_DOWNLOAD_DIS (BLOCK0):                        Disable UART download mode (ESP32 rev3 only)       = False R/- (0b0)
FLASH_CRYPT_CONFIG (BLOCK0):                       Flash encryption config (key tweak bits)           = 15 R/W (0xf)
CONSOLE_DEBUG_DISABLE (BLOCK0):                    Disable ROM BASIC interpreter fallback             = True R/W (0b1)
ABS_DONE_0 (BLOCK0):                               Secure boot V1 is enabled for bootloader image     = True R/W (0b1)
ABS_DONE_1 (BLOCK0):                               Secure boot V2 is enabled for bootloader image     = False R/W (0b0)
JTAG_DISABLE (BLOCK0):                             Disable JTAG                                       = True R/W (0b1)
DISABLE_DL_ENCRYPT (BLOCK0):                       Disable flash encryption in UART bootloader        = True R/W (0b1)
DISABLE_DL_DECRYPT (BLOCK0):                       Disable flash decryption in UART bootloader        = True R/W (0b1)
DISABLE_DL_CACHE (BLOCK0):                         Disable flash cache in UART bootloader             = True R/W (0b1)
BLOCK1 (BLOCK1):                                   Flash encryption key
   = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-
BLOCK2 (BLOCK2):                                   Secure boot key
   = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-
BLOCK3 (BLOCK3):                                   Variable 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 R/W

Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).

and a espfuses-dump of my bricked esp32:

C:\Users\m-sja\Downloads\fluidnc-v3.2.5\win64>espefuse.exe -p com7 -b 115200 -c esp32 dump fuse.hex
Connecting...
Device PID identification is only supported on COM and /dev/ serial ports.

BLOCK0          (                ) [0 ] read_regs: 00130184 2081b404 00e040f5 0000a200 00001032 f0000000 000003d4
BLOCK1          (flash_encryption) [1 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK2          (secure_boot_v1 s) [2 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK3          (                ) [3 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

EFUSE_REG_DEC_STATUS        0x00000000

Here is a espfuse-dump of the new esp32:

C:\Users\m-sja\Downloads\fluidnc-v3.2.5\win64>espefuse.exe -p COM7 -b 115200 dump
Connecting.....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting...
Detecting chip type... ESP32
BLOCK0          (                ) [0 ] read_regs: 00000000 a49b4224 007a30ae 00008200 00000036 00000000 00000004
BLOCK1          (flash_encryption) [1 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK2          (secure_boot_v1 s) [2 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
BLOCK3          (                ) [3 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

EFUSE_REG_DEC_STATUS        0x00000000

The first section of Block0 is the interesting one.

As one of your projects goals is lo lower the barrier to for new users, it should be obvious also protect them from bricking their devices wherever possible.

Thank you very much for all the effort you put in these cnc-projects!!! If you have any questions, i'll try to help as best as i can.

Best regards Marian

MitchBradley commented 2 years ago

Thanks for the very detailed report with extremely useful data. I'll look into it. Note that this will be tricky/expensive to test - as you have found. Ideally, the fix would be in esptool, but we will try to paper over it.

bdring commented 2 years ago

voidmicro an appropriate name.

We have not heard about that controller.

Do you have a picture of it?

m-sja commented 2 years ago

Sure, i also reverse engineered the pinout. voidmicro_singlearm_engraver.pdf My plan was it use it on some small 2-axis builds. There is also a "frame engraver" version with a 3rd 4988 driver for the second y-motor.

I like these, because of the small footprint and the onboard 8mb flash. As there is no SD interface, i think they used the extra space to buffer gcode. But due to the fact that esp32 is secured, it is no option for "buy'n'use".

Maybe you can add a section of known incompatible devices and use my 3rd picture to allow other users identify their controller.

IMG_20211128_231956 IMG_20211128_232304 IMG_20211128_232234 t IMG_20211128_232115 IMG_20211128_232123 IMG_20211128_231956

IMG_20211128_232039

MitchBradley commented 2 years ago

Seems like they won't be easy to repurpose with soldered down modules and locked firmware.

MitchBradley commented 2 years ago

Is it possible to put a factory-signed binary back in the bricked module?

m-sja commented 2 years ago

No, i would not recommend those due to my experience. The esp32 is easy to swap if you have the right tools and some experience, but it is not worth the hassle. I came across these boards on ali. They are sold as replacemant parts and i thought it would be fun to play with. Unfortunately they are not, but i learned a lot. So its still a win for me.

I agree with you that the best fix would be a warning or a -f force option in esptool to prevent this kind of issue, as esptool is used in nearly any other flash-software.

On the reflash... it depends. As far as i understand there are to methods of encryption. a: flash factory-keys to the esp32 and upload pre-encrypted binarys and signed bootloader b: the esp32 can generate a random key and encrypt the flash on the first boot (in this case the key is unknown)

I have no backup or factory-binary for the controller. I've also not seen any firmware updates to download. It could be an option to create a backup before flashing, in this case you should be able to reflash the backup (if there is no readout protection, which my module has not).

Btw. i already swapped the esp32 and flashed fluidnc successfully. I will use it anyway somehow ;-P

MitchBradley commented 2 years ago

I am trying to figure out how to incorporate the check in the installer without spending so much time that I end up resenting it. It potentially involves adding espefuse.exe in three OS versions, some scripting to decode the output, and a buttload of testing that I cannot do easily without possibly destroying some hardware. And something is bound to go wrong down the line that prevents somebody who would otherwise not be affected from successfully installing. I hate security work.

bdring commented 2 years ago

What firmware was on there before?

m-sja commented 2 years ago

It was some stock firmware using an smarthone-app. No Web-Interface. Nothing i really want to use. I prefer not to install android apks from unknown sources. So in theory the perfect candidate for excellent open source projects like yours. :-) You can checkout the app here if you are interested: http://voidmicro.com/Download/downloadApp-en.html?page=page_download

MitchBradley commented 2 years ago

Adding espefuse binaries for the three architectures increases the size of the .zip release download from 30MB to 70MB, without increasing utility at all for the typical user.

MitchBradley commented 2 years ago

I am starting to get the "cost/benefit much greater than 1" vibe.

m-sja commented 2 years ago

i understand and i share the feeling. Maybe its more easy to add a "unsupported devices list" in the wiki. Also creating an issue on espressif esptool about this issue would be more beneficial to the rest of the world. (Sadly i am not experienced enough to do it and discuss this issue with them on appropriate level)

MitchBradley commented 2 years ago

So in theory the perfect candidate for excellent open source projects like yours. :-)

Except for the fact that open source projects generate a lot of work for the developers with no obvious source of compensation. If you sell hardware, as Bart does, then you can recoup some of it, but then when people repurpose Chinese hardware instead of buying the developer's product, then have problems that come back not to the Chinese vendor but instead to the developer, well, you see where I am going...

MitchBradley commented 2 years ago

i understand and i share the feeling. Maybe its more easy to add a "unsupported devices list" in the wiki. Also creating an issue on espressif esptool about this issue would be more beneficial to the rest of the world. (Sadly i am not experienced enough to do it and discuss this issue with them on appropriate level)

Your discussion with us has been entirely appropriate so I encourage you to do it. I need to get back to doing productive work.

m-sja commented 2 years ago

I totally agree with you in terms of the compensation part. Keep on the good work and thanks for all the innovations you have brought to us.

Your discussion with us has been entirely appropriate so I encourage you to do it. I need to get back to doing productive work.

Thanks!

Feel free to close this issue, but i would still suggest an "incompatible hardware list" at least so save you work and frustration to other users.

MitchBradley commented 2 years ago

i would still suggest an "incompatible hardware list"

The point is well taken, but I do not know how we would maintain such a list, given the rate at which China creates and obsoletes difficult-to-identify products. I think the best we can do is describe the potential problem in general terms - and even that is of questionable value given the fact that people don't read the wiki until after they have had a problem. You would be amazed at the number of people who do not realize that FluidNC does not usually require recompiling, despite the fact that it is in in the README.md on the first screenful of text.

bdring commented 2 years ago

How about.

Be careful uploading firmware to an existing controller with firmware other than Grbl_ESP32 or FluidNC. You need to make sure that the existing firmware is not encrypted or it may brick the device. If in doubt ask on Discord.

m-sja commented 2 years ago

Thanks for the encouragement: https://github.com/espressif/esptool/issues/699

MitchBradley commented 2 years ago

That's a very good bug report. It clearly states the problem and the desired solution.

MitchBradley commented 2 years ago

I found a way to read fuses with esptool; you can use read_mem with the address of the appropriate efuse word. Using that technique, I added a check to the install scripts. There is a test version of those scripts at https://github.com/bdring/FluidNC/releases/tag/WMB_Test9 . It would be great if you could check them against your bricked chip.

m-sja commented 2 years ago

@MitchBradley awesome Work, Thanks! image

Side note: As the stdout from esptool is now piped to findstr, the dots appearing while connecting as well as other outputs are not visible in the batch-terminal anymore. This may be confusing and may cause difficulties while debugging other issues. I tried to modify the batch script, and found a solution. Maybe this can be translated to mac and linux as well.

I used 'dump_mem' instead of read_mem. But this resulted in a binary written file. I was not able to compare it using your method. So i needed to add a reference file 'SecurityFusesOK' to compare against. The dumped file gets deleted to prevent accidentally execution (not sure if this is could happen)

@echo off

set BuildType=wifi
set EsptoolPath=win64\esptool.exe

set BaseArgs=--chip esp32 --baud 921600

rem Read the security fuses
%EsptoolPath% %BaseArgs% dump_mem 0x3ff5a018 8 SecurityFuses

if not %ErrorLevel% equ 0 (
   echo esptool failed
   pause
   exit
)

fc /b SecurityFuses SecurityFusesOK > nul 2>&1
if not %Errorlevel% equ 0 (
   echo *******************************************
   echo *  Secure boot is enabled on this ESP32   *
   echo * Loading FluidNC would probably brick it *
   echo *    !ABORTED! Read Wiki for more Info    *
   echo *******************************************
   del SecurityFuses
   pause
   exit
)
del SecurityFuses

set SetupArgs=--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect

set Bootloader=0x1000 common\bootloader_dio_80m.bin
set Bootapp=0xe000 common\boot_app0.bin
set Firmware=0x10000 %BuildType%\firmware.bin
set Partitions=0x8000 %BuildType%\partitions.bin

echo %EsptoolPath% %BaseArgs %SetupArgs% %Bootloader% %Bootapp% %Firmware% %Partitions%
%EsptoolPath% %BaseArgs% %SetupArgs% %Bootloader% %Bootapp% %Firmware% %Partitions%

echo Starting fluidterm
win64\fluidterm.exe

pause

image Please find my modifications in the attached zip. install_wifi_secureboot.zip

MitchBradley commented 2 years ago

Why dump 8 bytes? As far as I can tell, there are only 6 fuses in block 0, so 8 bytes gets you a nonexistent fuse.

MitchBradley commented 2 years ago

"Read Wiki for more Info". Please supply the text for said wiki page. I have done about 10 times more work on this front that I can stand.

m-sja commented 2 years ago

I admit i do not unterstand how the efuse controller and efuse program register shuffle around the bits. I tried to understand the "esp32 technical reference manual", but i have to many missing links to understand completely how it works. So i thought 8 bytes should be correct because of the 8x zero in the last section of BLOCK0 😇

I will provide the text for the wiki page shortly 👍

arhuaco commented 2 years ago

Hi there!

I bypassed the warning and I got it to work! I checked and I think this is false positive. I'm attaching the report and it seems the board is not encrypted and that it doesn't have restrictions.

I'm using the esptool that can be installed with Ubuntu 22.04, should I be testing with another one?

The text for the FAQ could be:

While flashing the firmware I get the message
   Secure boot is enabled on this ESP32
   Loading FluidNC would probably brick it

ESP32 supports secure boot, which allows restricting how software runs in the controller. We don't provide assistance for secure boot issues. We try to determine whether your device can be flashed with heuristics and those are not perfect. Users have reported that their controllers work properly after being flashed when this warning was present, but your device could be bricked as well. More details in this [GitHub issue](https://github.com/bdring/FluidNC/issues/149).

espefuse_report.txt esp32_board