ESPILON-CTF-2026-Writeups/IoT/Lain_Br34kC0r3_V2
2026-03-26 17:33:32 +00:00
..
README.md write-up: IoT/Lain_Br34kC0r3_V2/README.md 2026-03-26 17:33:32 +00:00

Lain_Br34kC0r3 V2

Field Value
Category IoT
Difficulty Hard
Points 500
Author Eun0us
CTF Espilon 2026

Description

Chapter 2 — Core Analysis

After exploring the WIRED-MED therapy module through its UART interface, you successfully dumped the complete flash of the device. This dump contains the bootloader, the partition table, the NVS and the application firmware.

Your mission: reverse engineer the ESP32 firmware to extract the encryption keys used to protect patient data, then decrypt the flag stored in the NVS partition.

"Close your eyes. Open The Wired. Analyze the core."

  • TX (read): nc CHALLENGE_HOST 1111
  • RX (write): nc CHALLENGE_HOST 2222

TL;DR

Issue dump_flash over the RX UART port to receive a base64-encoded full flash dump on TX. Decode and use binwalk to locate the app firmware at offset 0x10000. Run strings on the binary to find the AES-256-CBC key (W1R3D_M3D_TH3R4PY_K3Y_2024_L41N!) and IV (L41N_WIRED_IV_01). Get the encrypted flag via the encrypted_data command, then AES-256-CBC decrypt it.


Tools

Tool Purpose
nc Split UART connection
Python 3 Base64 decode, data parsing
binwalk / esptool.py Parse the flash dump
strings Extract AES key and IV
Ghidra (Xtensa) Full reverse engineering if needed
pycryptodome AES-256-CBC decryption

Solution

Step 1 — Dump the flash

# Terminal 1 — TX (read)
nc <host> 1111 | tee flash_output.txt

# Terminal 2 — RX (write)
echo "dump_flash" | nc <host> 2222

The dump is sent as base64 between markers:

=== BEGIN FLASH DUMP ===
<base64 data>
=== END FLASH DUMP ===

Decode:

import base64

with open("flash_output.txt") as f:
    lines = f.readlines()

b64_data = ""
capture = False
for line in lines:
    if "BEGIN FLASH DUMP" in line:
        capture = True
        continue
    if "END FLASH DUMP" in line:
        break
    if capture:
        b64_data += line.strip()

flash = base64.b64decode(b64_data)
with open("flash_dump.bin", "wb") as f:
    f.write(flash)

📸 [screenshot: TX terminal showing the base64 flash dump streaming out]

Step 2 — Identify the flash structure

binwalk flash_dump.bin

Expected structure:

0x0000  Padding (0xFF)
0x1000  ESP32 bootloader (magic 0xE9)
0x8000  Partition table
0x9000  NVS partition (24 KiB)
0xF000  PHY init data
0x10000 Application firmware (magic 0xE9)

Step 3 — Extract and analyze the application firmware

dd if=flash_dump.bin of=app_firmware.bin bs=1 skip=$((0x10000))
strings -n 10 app_firmware.bin | grep -i "key\|aes\|iv\|wired\|therapy"

Expected results:

W1R3D_M3D_TH3R4PY_K3Y_2024_L41N!    # AES-256 key (32 bytes)
L41N_WIRED_IV_01                      # AES IV (16 bytes)
WIRED-MED Therapy Module

📸 [screenshot: strings output identifying the AES key and IV]

For full confirmation: open in Ghidra with Xtensa:LE:32:default architecture, find app_main()wired_med_crypto_init()mbedtls_aes_setkey_enc(). The key and IV are passed as arguments pointing into .rodata.

Step 4 — Get the encrypted flag ciphertext

On the RX port:

encrypted_data

Returns the ciphertext as a hex string on TX.

Alternatively, extract from the NVS partition (namespace wired_med, key encrypted_flag, blob type) using nvs_tool.py from ESP-IDF.

📸 [screenshot: encrypted_data command returning the hex ciphertext]

Step 5 — Decrypt AES-256-CBC

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

key = b"W1R3D_M3D_TH3R4PY_K3Y_2024_L41N!"  # 32 bytes
iv  = b"L41N_WIRED_IV_01"                   # 16 bytes

ciphertext = bytes.fromhex("...")  # from encrypted_data output

cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
print(plaintext.decode())
# ESPILON{3sp32_fl4sh_dump_r3v3rs3d}

📸 [screenshot: Python decryption script printing the flag]

Attack chain summary

dump_flash → base64 decode → binwalk → extract app @ 0x10000
    → strings/Ghidra (Xtensa RE) → find AES key + IV in .rodata
    → encrypted_data (or NVS parse) → AES-256-CBC decrypt → FLAG

Flag

ESPILON{3sp32_fl4sh_dump_r3v3rs3d}