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

![TX terminal showing ESP32 boot sequence with diagnostic messages](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/hate_uart_boot.png)

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

![mem read output showing base64 token in ASCII column](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/hate_uart_mem.png)

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

![nvs read showing the encrypted flag hexdump](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/hate_uart_nvs.png)

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

![Python decryption script printing the flag](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/hate_uart_decrypt.png)

---

## Flag

`ESPILON{u4rt_nvs_fl4sh_d1sc0v3ry}`
