ESPILON-CTF-2026-Writeups/IoT/Lets_All_Hate_UART/README.md
Eun0us 6a0877384d [+] Writeups v2 — sync solves, real points, scoreboard stats, cleanup
- Remove undeployed challenges: Phantom_Byte, Cr4cK_w1f1, Lain_Br34kC0r3 V1,
  Lain_VS_Knights, Lets_All_Love_UART, AETHER_NET, Last_Train_451, Web3/
- Sync 24 solve/ files from main CTF-Espilon repo
- Update all READMEs with real CTFd final scores at freeze
- Add git-header.png banner
- Rewrite README: scoreboard top 10, edition stats (1410 users, 264 boards,
  1344 solves), correct freeze date March 26 2026
2026-03-27 21:27:45 +01:00

3.5 KiB
Raw Permalink Blame History

Let's All Hate UART

Field Value
Category IoT
Difficulty Medium-Hard
Points 100
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.

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='

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.

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}

Python decryption script printing the flag


Flag

ESPILON{u4rt_nvs_fl4sh_d1sc0v3ry}