write-up: Hardware/Phantom_JTAG/README.md
This commit is contained in:
parent
127a1aafc6
commit
c483eefa6e
@ -1,61 +1,133 @@
|
|||||||
# Phantom JTAG — Solution
|
# Phantom JTAG
|
||||||
|
|
||||||
## Overview
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Category | Hardware |
|
||||||
|
| Difficulty | Medium-Hard |
|
||||||
|
| Points | 500 |
|
||||||
|
| Author | Eun0us |
|
||||||
|
| CTF | Espilon 2026 |
|
||||||
|
|
||||||
Simulated JTAG debug port with IEEE 1149.1 TAP state machine. The debug interface is locked and requires a key to unlock. Once unlocked, memory can be read to extract the flag.
|
---
|
||||||
|
|
||||||
## Steps
|
## Description
|
||||||
|
|
||||||
1. Connect:
|
A JTAG debug port on a WIRED-MED module is partially accessible.
|
||||||
|
The debug interface is locked, but the TAP controller still responds to state transitions.
|
||||||
|
|
||||||
|
Navigate the IEEE 1149.1 state machine, read the device IDCODE, unlock the debug interface,
|
||||||
|
and extract secrets from memory.
|
||||||
|
|
||||||
|
- JTAG Port: `tcp/<host>:3400`
|
||||||
|
|
||||||
|
Format: **ESPILON{...}**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
Interact with a simulated JTAG TAP controller. Reset the state machine, read the IDCODE via
|
||||||
|
standard IR instruction 0x1, then send the proprietary unlock instruction (IR=0x5) with key
|
||||||
|
`0xDEAD`. Once debug is unlocked, use MEM_READ (IR=0x8) to dump memory at 0x1000 and
|
||||||
|
reassemble the flag.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
| Tool | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `nc` | Connect to JTAG port |
|
||||||
|
| Python 3 | Automate IR/DR sequences and decode memory |
|
||||||
|
| Knowledge of IEEE 1149.1 | Understand TAP state machine |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
### Step 1 — Connect
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nc <host> 3400
|
nc <host> 3400
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Reset the TAP controller:
|
> 📸 `[screenshot: JTAG port banner showing TAP controller information]`
|
||||||
|
|
||||||
```
|
### Step 2 — Reset the TAP controller
|
||||||
|
|
||||||
|
```text
|
||||||
reset
|
reset
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Read device IDCODE:
|
The TAP state machine enters Test-Logic-Reset, then shifts to Run-Test/Idle.
|
||||||
|
|
||||||
```
|
### Step 3 — Read device IDCODE
|
||||||
|
|
||||||
|
Load IR instruction 0x1 (IDCODE), then shift out the 32-bit DR:
|
||||||
|
|
||||||
|
```text
|
||||||
ir 1
|
ir 1
|
||||||
dr 00000000 32
|
dr 00000000 32
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns `0x4BA00477` (ARM Cortex-M like device).
|
Returns `0x4BA00477` — an ARM Cortex-M style IDCODE.
|
||||||
|
|
||||||
4. Unlock debug interface — load IR instruction 0x5 and send key `0xDEAD`:
|
> 📸 `[screenshot: IDCODE read returning 0x4BA00477]`
|
||||||
|
|
||||||
```
|
### Step 4 — Unlock the debug interface
|
||||||
|
|
||||||
|
Load the proprietary lock control instruction (IR=0x5) and send the 16-bit unlock key `0xDEAD`:
|
||||||
|
|
||||||
|
```text
|
||||||
ir 5
|
ir 5
|
||||||
dr DEAD 16
|
dr DEAD 16
|
||||||
```
|
```
|
||||||
|
|
||||||
Check with `state` — should show "Debug: UNLOCKED".
|
Verify:
|
||||||
|
|
||||||
5. Read memory — load MEM_READ instruction (IR 0x8):
|
|
||||||
|
|
||||||
|
```text
|
||||||
|
state
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Output should now show: `Debug: UNLOCKED`
|
||||||
|
|
||||||
|
> 📸 `[screenshot: state command showing Debug: UNLOCKED]`
|
||||||
|
|
||||||
|
### Step 5 — Load the memory read instruction
|
||||||
|
|
||||||
|
```text
|
||||||
ir 8
|
ir 8
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Dump flag from memory at 0x1000:
|
### Step 6 — Dump flag from memory
|
||||||
|
|
||||||
```
|
Send address 0x1000 as the first DR, then read back the 32-bit word:
|
||||||
|
|
||||||
|
```text
|
||||||
dr 1000 16
|
dr 1000 16
|
||||||
dr 00000000 32
|
dr 00000000 32
|
||||||
```
|
```
|
||||||
|
|
||||||
The first `dr` sends the address, the second reads the 32-bit word at that address. Repeat for addresses 0x1000, 0x1004, 0x1008... until the full flag is recovered.
|
The second `dr` reads the 32-bit little-endian word at address 0x1000.
|
||||||
|
Repeat for addresses 0x1004, 0x1008, etc. until the flag is complete.
|
||||||
|
|
||||||
7. Convert the 32-bit little-endian words back to ASCII to reconstruct the flag.
|
Convert each little-endian 32-bit word to 4 ASCII bytes and concatenate.
|
||||||
|
|
||||||
## Key Concepts
|
> 📸 `[screenshot: dr reads returning flag bytes as 32-bit little-endian words]`
|
||||||
|
|
||||||
- **JTAG TAP state machine**: IEEE 1149.1 defines a 16-state FSM controlled by TMS signal
|
### Key concepts
|
||||||
- **IR/DR registers**: Instruction Register selects the operation, Data Register carries parameters/results
|
|
||||||
- **Debug port locking**: Many chips have a lock mechanism requiring a key to enable debug access
|
- **JTAG TAP state machine**: IEEE 1149.1 defines a 16-state FSM controlled by the TMS signal.
|
||||||
- **Memory dump via JTAG**: Once debug is unlocked, arbitrary memory reads are possible
|
TDI/TDO carry shift register data; TCK clocks transitions.
|
||||||
|
- **IR/DR registers**: The Instruction Register selects the active operation; the Data Register
|
||||||
|
carries parameters and results for that operation.
|
||||||
|
- **Debug port locking**: Many chips implement a proprietary lock mechanism requiring a secret
|
||||||
|
key to enable debug memory access.
|
||||||
|
- **Memory dump via JTAG**: Once debug is unlocked, arbitrary memory reads become possible —
|
||||||
|
a common firmware extraction technique used in hardware security research.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flag
|
||||||
|
|
||||||
|
`ESPILON{jt4g_d3bug_unl0ck3d}`
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user