230 lines
5.4 KiB
Markdown
Executable File
230 lines
5.4 KiB
Markdown
Executable File
# Lain VS Knights
|
|
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| Category | IoT |
|
|
| Difficulty | Hard |
|
|
| Points | — |
|
|
| Author | Eun0us |
|
|
| CTF | Espilon 2026 |
|
|
|
|
---
|
|
|
|
## Description
|
|
|
|
Welcome to The Wired: a digital labyrinth of over a thousand mysterious nodes, where only
|
|
seven are protected by the legendary Knights.
|
|
|
|
You are **Lain**. Your mission: navigate this cryptic UART interface, explore The Wired,
|
|
and uncover the locations of the hidden Knights. Purge each Knight.
|
|
|
|
Each defeated Knight grants you a fragment. Collect all seven to unlock the final gateway:
|
|
**EIRI MASAMI** — the master of The Wired.
|
|
|
|
Seize root access. Become the new legend of The Wired.
|
|
|
|
- **TX (port 1111)**: Read only
|
|
- **RX (port 2222)**: Write only
|
|
|
|
Format: **ESPILON{flag}**
|
|
|
|
---
|
|
|
|
## TL;DR
|
|
|
|
1200 nodes, 7 Knights each at a random node, 1 Founder (Eiri). Scan all nodes to find them.
|
|
Each Knight presents a different hardware/protocol puzzle (I2C collision, CAN CRC, SPI parity,
|
|
SRAM write, logic AND, fuse bits, fault injection). Collect the 7 fragments, concatenate them
|
|
in the order Lain specifies, SHA-256 hash, take first 24 hex chars, submit to the Founder for
|
|
root access and the flag.
|
|
|
|
---
|
|
|
|
## Tools
|
|
|
|
| Tool | Purpose |
|
|
|------|---------|
|
|
| `nc` | Split UART connection |
|
|
| Python 3 | Node scanner, puzzle solvers, exploit script |
|
|
| `hashlib` | SHA-256 for exploit hash computation |
|
|
|
|
---
|
|
|
|
## Solution
|
|
|
|
### Step 1 — Connect and map the network
|
|
|
|
```bash
|
|
# Terminal 1 — TX
|
|
nc <host> 1111
|
|
|
|
# Terminal 2 — RX
|
|
nc <host> 2222
|
|
```
|
|
|
|
```text
|
|
bus.map
|
|
```
|
|
|
|
Returns: `1200 nodes total — 7 knights active.`
|
|
|
|
Scan all nodes to locate the Knights and Founder (automated script recommended):
|
|
|
|
```python
|
|
import socket, time, re
|
|
|
|
def scan_nodes(host, tx_port=1111, rx_port=2222, node_min=1, node_max=1200):
|
|
tx = socket.socket(); tx.connect((host, tx_port))
|
|
rx = socket.socket(); rx.connect((host, rx_port))
|
|
found = []
|
|
for nid in range(node_min, node_max + 1):
|
|
rx.sendall(f"bus.connect @node:{nid:04d}\n".encode())
|
|
time.sleep(0.08)
|
|
out = tx.recv(4096).decode(errors="ignore")
|
|
if re.search(r"KNIGHT|FOUNDER|EIRI", out, re.I):
|
|
kind = "knight" if "KNIGHT" in out.upper() else "founder"
|
|
found.append((nid, kind))
|
|
rx.sendall(b"bus.disconnect\n")
|
|
return found
|
|
```
|
|
|
|
Example result: Knights at nodes 0067, 0113, 0391, 0529, 0619, 0901, 0906. Founder at 0311.
|
|
|
|
> 📸 `[screenshot: scanner output listing discovered Knight and Founder node IDs]`
|
|
|
|
### Step 2 — Get the assembly order from Lain nodes
|
|
|
|
Connect to any ordinary Lain node and call `node.truth` repeatedly until you get the order:
|
|
|
|
```text
|
|
Order matters. Use: i2c_mirror, can_checksum, spi_parity, sram_write,
|
|
logic_and, fuse_bits, fault_injection.
|
|
Assemble fragments in this order as a single string, with no separators.
|
|
Hash this string using SHA256. Take the first 24 hex digits.
|
|
```
|
|
|
|
### Step 3 — Defeat each Knight
|
|
|
|
**Knight 1 — I2C_MIRROR**: Find two messages with the same byte-sum mod N.
|
|
|
|
```text
|
|
node.i2c_write "0000"
|
|
node.i2c_write "7600"
|
|
node.submit_pair "0000" "7600"
|
|
# fragment i2c_mirror=0000_7600
|
|
```
|
|
|
|
**Knight 2 — CAN_CHECKSUM**: Find a CAN frame whose CRC8(poly=0x2F) = target.
|
|
|
|
```python
|
|
def can_crc8(data, poly=0x2F):
|
|
c = 0
|
|
for b in data:
|
|
c ^= b
|
|
for _ in range(8):
|
|
c = ((c << 1) ^ poly) & 0xFF if c & 0x80 else (c << 1) & 0xFF
|
|
return c
|
|
# Target 0x91 → frame bytes 0x00 0x26
|
|
```
|
|
|
|
```text
|
|
node.can_send "0026"
|
|
# fragment can_checksum=0026
|
|
```
|
|
|
|
**Knight 3 — SPI_PARITY**: Find a byte with exactly 5 bit-transitions.
|
|
|
|
```python
|
|
# 0x15 = 00010101 → transitions: 5
|
|
```
|
|
|
|
```text
|
|
node.spi_write 15
|
|
# fragment spi_parity=15
|
|
```
|
|
|
|
**Knight 4 — SRAM_WRITE**: Write a value to the required address.
|
|
|
|
```text
|
|
node.write 0x8b 0x89
|
|
# fragment sram_write=8b_89
|
|
```
|
|
|
|
**Knight 5 — LOGIC_AND**: Find `(a, b)` where `a & b == secret`.
|
|
|
|
```text
|
|
node.and_probe 0xff 0xff # reveals secret 0xa8
|
|
node.submit_and 0xa8 0xff
|
|
# fragment logic_and=a8_ff
|
|
```
|
|
|
|
**Knight 6 — FUSE_BITS**: Probe with full mask to reveal the fuse value.
|
|
|
|
```text
|
|
node.fuse_probe 0xff # reveals 0x30
|
|
node.submit_fuse 0x30
|
|
# fragment fuse_bits=30
|
|
```
|
|
|
|
**Knight 7 — FAULT_INJECTION**: Try all offset/mask combinations.
|
|
|
|
```text
|
|
node.inject 2 0x08
|
|
# fragment fault_injection=2_08
|
|
```
|
|
|
|
> 📸 `[screenshot: each Knight node returning its fragment after successful puzzle]`
|
|
|
|
### Step 4 — Verify fragment collection
|
|
|
|
```text
|
|
fragments
|
|
# i2c_mirror = 0000_7600
|
|
# can_checksum = 0026
|
|
# spi_parity = 15
|
|
# sram_write = 8b_89
|
|
# logic_and = a8_ff
|
|
# fuse_bits = 30
|
|
# fault_injection = 2_08
|
|
```
|
|
|
|
### Step 5 — Build the exploit hash
|
|
|
|
```python
|
|
import hashlib
|
|
|
|
fragments = {
|
|
"i2c_mirror": "0000_7600",
|
|
"can_checksum": "0026",
|
|
"spi_parity": "15",
|
|
"sram_write": "8b_89",
|
|
"logic_and": "a8_ff",
|
|
"fuse_bits": "30",
|
|
"fault_injection": "2_08",
|
|
}
|
|
order = ["i2c_mirror", "can_checksum", "spi_parity",
|
|
"sram_write", "logic_and", "fuse_bits", "fault_injection"]
|
|
|
|
payload = "".join(fragments[k] for k in order)
|
|
exploit = hashlib.sha256(payload.encode()).hexdigest()[:24]
|
|
print(exploit) # e.g. 69b4a17e33b0cdace34b7610
|
|
```
|
|
|
|
### Step 6 — Submit to the Founder and get root
|
|
|
|
```text
|
|
bus.connect @node:0311
|
|
node.exploit 69b4a17e33b0cdace34b7610
|
|
# [ROOT] Exploit accepted! You are now root.
|
|
node.flag
|
|
# ESPILON{0nlY_L41N_C4N_S0lv3}
|
|
```
|
|
|
|
> 📸 `[screenshot: Founder node accepting the exploit and printing the flag]`
|
|
|
|
---
|
|
|
|
## Flag
|
|
|
|
`ESPILON{0nlY_L41N_C4N_S0lv3}`
|