ESPILON-CTF-2026-Writeups/IoT/Lets_All_Hate_UART/README.md

151 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```bash
# 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
```text
help
```
Only basic commands shown. But sending `?` or `help -a` reveals hidden ones:
```text
?
>> 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:
```text
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`
```python
import base64
base64.b64decode("dGgzcmFweV9tMGR1bGU9")
# b'th3rapy_m0dule='
```
> 📸 `[screenshot: mem read output showing base64 token in ASCII column]`
### Step 4 — Authenticate as debug user
```text
debug auth th3rapy_m0dule=
```
Response: `DEBUG MODE ENABLED. Extended commands unlocked.`
### Step 5 — Read the NVS flag
List NVS entries:
```text
nvs list
```
Entry `crypto_flag` appears (blob, 34 bytes).
Read it:
```text
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`.
```python
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}`