- 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
151 lines
3.5 KiB
Markdown
151 lines
3.5 KiB
Markdown
# 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
|
||
|
||
```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.
|
||
|
||

|
||
|
||
### 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 `0x3FFB0000–0x3FFB1000` 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 0x810–0x830 is base64: `dGgzcmFweV9tMGR1bGU9`
|
||
|
||
```python
|
||
import base64
|
||
base64.b64decode("dGgzcmFweV9tMGR1bGU9")
|
||
# b'th3rapy_m0dule='
|
||
```
|
||
|
||

|
||
|
||
### 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.
|
||
|
||

|
||
|
||
### 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}
|
||
```
|
||
|
||

|
||
|
||
---
|
||
|
||
## Flag
|
||
|
||
`ESPILON{u4rt_nvs_fl4sh_d1sc0v3ry}`
|