# Wired SPI Exfil

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

---

## Description

A WIRED-MED module's SPI flash chip is accessible via a probe station.
Issue standard SPI commands to read the flash contents.

- SPI Probe: `tcp/<host>:3500`

Not all partitions are listed. Dig deeper.

Format: **ESPILON{...}**

---

## TL;DR

Use standard SPI flash opcodes (RDID, SFDP, READ) to identify the chip, discover a hidden
partition at 0x030000 via vendor-specific SFDP parameters, read the hidden partition contents,
and XOR-decrypt the data with key `WIRED_SPI` to recover the flag.

---

## Tools

| Tool | Purpose |
|------|---------|
| `nc` | Connect to the SPI probe interface |
| Python 3 | XOR decryption |
| SPI flash opcode reference | RDID=0x9F, READ=0x03, SFDP=0x5A |

---

## Solution
![flashrom SPI dump and strings extraction](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/spi_flash.png)


### Step 1 — Connect and assert chip select

```bash
nc <host> 3500
```

```text
cs 0
```

![SPI probe interface ready with CS asserted](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/spi_probe.png)

### Step 2 — Read the chip ID

Send RDID opcode 0x9F:

```text
tx 9F
```

Response: `EF 40 18` — Winbond W25Q128 (128 Mbit SPI flash).

### Step 3 — Read the SFDP header

SFDP (Serial Flash Discoverable Parameters) reveals flash capabilities.
Send SFDP opcode 0x5A with 3-byte address and 1 dummy byte:

```text
tx 5A 00 00 00 00
```

The SFDP header shows 2 parameter tables. The first is the standard JEDEC table;
the second is a vendor-specific table at offset 0x80.

![SFDP header output showing two parameter table entries](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/spi_sfdp.png)

### Step 4 — Read the vendor SFDP table

```text
tx 5A 00 00 80 00
```

The vendor table data includes a hidden partition entry:

```
Offset: 0x030000
Label:  HIDDEN
Size:   4096 bytes
```

This partition does not appear in the normal partition table.

![vendor SFDP data revealing hidden partition at 0x030000](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/spi_vendor.png)

### Step 5 — Read the hidden partition

Use the READ opcode 0x03 with 3-byte address:

```text
tx 03 03 00 00
```

The response starts with the ASCII header `WIRED_HIDDEN_PARTITION` followed by
XOR-encrypted bytes.

### Step 6 — Decrypt the flag

The data is XOR'd with the repeating key `WIRED_SPI`:

```python
# data_hex = hex bytes from the tx read response after the header
key = b"WIRED_SPI"
encrypted = bytes.fromhex("...")  # from response
flag = bytes(b ^ key[i % len(key)] for i, b in enumerate(encrypted))
print(flag.rstrip(b'\x00').decode())
```

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

### Key concepts

- **SPI flash commands**: Standard opcodes work across most vendors:
  `0x9F` = RDID, `0x03` = READ, `0x5A` = SFDP
- **SFDP**: Serial Flash Discoverable Parameters standardises capability discovery;
  vendor-specific extensions can hide extra metadata, including non-standard partition info
- **Hidden partitions**: Not all sectors appear in standard partition tables — manual
  probing or SFDP analysis is required to find them
- **Data at rest XOR**: Simple XOR protection on stored secrets is common in
  embedded firmware; knowing the plaintext structure (header magic bytes) allows key recovery

---

## Flag

`ESPILON{sp1_fl4sh_3xf1ltr4t3d}`
