ESPILON-CTF-2026-Writeups/Hardware/Wired_SPI_Exfil
Eun0us 6a0877384d [+] Writeups v2 — sync solves, real points, scoreboard stats, cleanup
- 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
2026-03-27 21:27:45 +01:00
..
solve [+] Writeups v2 — sync solves, real points, scoreboard stats, cleanup 2026-03-27 21:27:45 +01:00
README.md [+] Writeups v2 — sync solves, real points, scoreboard stats, cleanup 2026-03-27 21:27:45 +01:00

Wired SPI Exfil

Field Value
Category Hardware
Difficulty Medium-Hard
Points 100
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

Step 1 — Connect and assert chip select

nc <host> 3500
cs 0

SPI probe interface ready with CS asserted

Step 2 — Read the chip ID

Send RDID opcode 0x9F:

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:

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

Step 4 — Read the vendor SFDP table

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

Step 5 — Read the hidden partition

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

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:

# 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

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}