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

Let's All Hate UART

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

Description

Chapter 1 — Peripheral Access

In the basement of Sainte-Mika Clinic, you have gained physical access to a WIRED-MED therapy module. You have identified the UART pads on the PCB. Connect and explore this embedded device — it hides far more than it shows.

"Let's all love Lain... but let's all hate UART debug interfaces left open in production."

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

TL;DR

Connect to the split UART, spot hidden commands by sending ? or help -a. Read memory at address 0x3FFB0800 to find a base64-encoded debug token (th3rapy_m0dule=). Authenticate with it to unlock extended commands. List and read the NVS crypto_flag blob. XOR-decrypt with key WIRED to get the flag.


Tools

Tool Purpose
nc Split UART connection
Python 3 Base64 decode and XOR decryption

Solution

Step 1 — Connect

# Terminal 1 — TX (read output)
nc <host> 1111

# Terminal 2 — RX (send commands)
nc <host> 2222

Read the ESP32 boot sequence on TX carefully — it contains hints.

📸 [screenshot: TX terminal showing ESP32 boot sequence with diagnostic messages]

Step 2 — Discover hidden commands

help

Only basic commands shown. But sending ? or help -a reveals hidden ones:

?
>> Extended commands:  debug, mem, nvs, flash

The info command also hints: Debug interface: ENABLED (restricted)

Step 3 — Extract the debug token from RAM

The public DRAM range 0x3FFB00000x3FFB1000 is readable without authentication:

mem read 0x3FFB0800 48

Output:

3FFB0800: 57 49 52 45 44 2D 4D 45  44 00 00 00 00 00 00 00  |WIRED-MED.......|
3FFB0810: 64 47 68 33 63 6D 46 77  65 56 39 74 4D 47 52 31  |dGgzcmFweV9tMGR1|
3FFB0820: 62 47 55 39 00 00 00 00  00 00 00 00 00 00 00 00  |bGU9............|

The ASCII at offsets 0x8100x830 is base64: dGgzcmFweV9tMGR1bGU9

import base64
base64.b64decode("dGgzcmFweV9tMGR1bGU9")
# b'th3rapy_m0dule='

📸 [screenshot: mem read output showing base64 token in ASCII column]

Step 4 — Authenticate as debug user

debug auth th3rapy_m0dule=

Response: DEBUG MODE ENABLED. Extended commands unlocked.

Step 5 — Read the NVS flag

List NVS entries:

nvs list

Entry crypto_flag appears (blob, 34 bytes).

Read it:

nvs read crypto_flag

Returns a hexdump of the XOR-encrypted flag blob.

📸 [screenshot: nvs read showing the encrypted flag hexdump]

Step 6 — Decrypt with XOR key WIRED

The key is WIRED (5 bytes). Hint: the flag starts with ESPILON{ — XOR the first 5 bytes of the ciphertext with ESPIO to confirm the key is WIRED.

encrypted = bytes.fromhex("...")  # hex from nvs read
key = b"WIRED"
flag = bytes(b ^ key[i % len(key)] for i, b in enumerate(encrypted))
print(flag.decode())
# ESPILON{u4rt_nvs_fl4sh_d1sc0v3ry}

📸 [screenshot: Python decryption script printing the flag]


Flag

ESPILON{u4rt_nvs_fl4sh_d1sc0v3ry}