write-up: Hardware/Wired_SPI_Exfil/README.md
This commit is contained in:
parent
3388b2a7d6
commit
8bb0532b22
@ -1,53 +1,140 @@
|
||||
# Wired SPI Exfil — Solution
|
||||
# Wired SPI Exfil
|
||||
|
||||
## Overview
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Category | Hardware |
|
||||
| Difficulty | Medium-Hard |
|
||||
| Points | 500 |
|
||||
| Author | Eun0us |
|
||||
| CTF | Espilon 2026 |
|
||||
|
||||
Simulated SPI flash chip from a WIRED-MED module. Standard SPI flash commands are used to read chip contents. A hidden partition not listed in the normal partition table contains the XOR-encrypted flag. The SFDP table has vendor-specific parameters that reveal the hidden sector.
|
||||
---
|
||||
|
||||
## Steps
|
||||
## Description
|
||||
|
||||
1. Connect and assert CS:
|
||||
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
|
||||
|
||||
### Step 1 — Connect and assert chip select
|
||||
|
||||
```bash
|
||||
nc <host> 3500
|
||||
```
|
||||
|
||||
```text
|
||||
cs 0
|
||||
```
|
||||
|
||||
2. Read chip ID:
|
||||
> 📸 `[screenshot: SPI probe interface ready with CS asserted]`
|
||||
|
||||
```
|
||||
### Step 2 — Read the chip ID
|
||||
|
||||
Send RDID opcode 0x9F:
|
||||
|
||||
```text
|
||||
tx 9F
|
||||
```
|
||||
|
||||
Returns `EF 40 18` = Winbond W25Q128.
|
||||
Response: `EF 40 18` — Winbond W25Q128 (128 Mbit SPI flash).
|
||||
|
||||
3. Read the SFDP table to discover hidden sectors:
|
||||
### 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
|
||||
```
|
||||
|
||||
SFDP header shows 2 parameter tables. Read vendor table at offset 0x80:
|
||||
The SFDP header shows 2 parameter tables. The first is the standard JEDEC table;
|
||||
the second is a vendor-specific table at offset 0x80.
|
||||
|
||||
```
|
||||
> 📸 `[screenshot: SFDP header output showing two parameter table entries]`
|
||||
|
||||
### Step 4 — Read the vendor SFDP table
|
||||
|
||||
```text
|
||||
tx 5A 00 00 80 00
|
||||
```
|
||||
|
||||
Vendor data shows a hidden partition at `0x030000` labeled "HIDDEN".
|
||||
|
||||
4. Read the hidden partition:
|
||||
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.
|
||||
|
||||
> 📸 `[screenshot: vendor SFDP data revealing hidden partition at 0x030000]`
|
||||
|
||||
### Step 5 — Read the hidden partition
|
||||
|
||||
Use the READ opcode 0x03 with 3-byte address:
|
||||
|
||||
```text
|
||||
tx 03 03 00 00
|
||||
```
|
||||
|
||||
Data starts with `WIRED_HIDDEN_PARTITION` header, followed by encrypted bytes.
|
||||
The response starts with the ASCII header `WIRED_HIDDEN_PARTITION` followed by
|
||||
XOR-encrypted bytes.
|
||||
|
||||
5. XOR the encrypted data with key `WIRED_SPI` to get the flag.
|
||||
### Step 6 — Decrypt the flag
|
||||
|
||||
## Key Concepts
|
||||
The data is XOR'd with the repeating key `WIRED_SPI`:
|
||||
|
||||
- **SPI flash commands**: Standard opcodes (RDID, READ, SFDP) work across most flash chips
|
||||
- **SFDP**: Serial Flash Discoverable Parameters — a standardized way to query flash capabilities. Vendor extensions can hide extra information
|
||||
- **Hidden partitions**: Not all storage areas appear in standard partition tables — manual probing or SFDP analysis reveals them
|
||||
- **Data at rest encryption**: Simple XOR protection on stored secrets
|
||||
```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())
|
||||
```
|
||||
|
||||
> 📸 `[screenshot: Python decryption script printing the recovered flag]`
|
||||
|
||||
### 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}`
|
||||
|
||||
Loading…
Reference in New Issue
Block a user