diff --git a/Misc/Accela_Signal/README.md b/Misc/Accela_Signal/README.md index 3118594..77d4bf8 100644 --- a/Misc/Accela_Signal/README.md +++ b/Misc/Accela_Signal/README.md @@ -1,65 +1,124 @@ -# Accela Signal -- Solution +# Accela Signal -## Overview -A LoRa-like Chirp Spread Spectrum (CSS) IQ stream containing two types of frames: -beacon (cleartext) and data (XOR-encrypted flag). Players must implement CSS -demodulation from scratch to decode the frames. +| Field | Value | +|-------|-------| +| Category | Misc | +| Difficulty | Hard | +| Points | 500 | +| Author | Eun0us | +| CTF | Espilon 2026 | -## Steps +--- + +## Description + +During the KIDS experiment, Tachibana Labs deployed a covert LoRa-like mesh network +throughout the hospital infrastructure. Code-named "Accela Signal", these transmissions +use Chirp Spread Spectrum modulation to carry data fragments below the detection threshold +of conventional receivers. + +Your NAVI has intercepted the baseband IQ stream from one of these nodes. The signal +contains chirp-modulated frames with encoded data. Analyze the modulation, demodulate +the chirps, decode the protocol, and extract the hidden message. + +Hint: The stream banner tells you the basics. The rest is signal processing. + +- IQ Stream: `tcp/:9002` + +Format: **ESPILON{flag}** + +--- + +## TL;DR + +Capture an 8 kSps int16 LE IQ stream. Identify Chirp Spread Spectrum (LoRa-like) modulation +(N=128, SF=7). Implement dechirp + FFT demodulation. Detect frames by preamble (8x symbol-0 +chirps) and sync (2x downchirps). Gray-decode symbols. Unpack 7-bit symbols to bytes. Find +the data frame (type=0x02), XOR with key `L41N`, decode the flag. + +--- + +## Tools + +| Tool | Purpose | +|------|---------| +| `nc` | Capture IQ stream | +| Python 3 + numpy + scipy | CSS demodulation pipeline | +| `inspectrum` / matplotlib | Visual spectrogram analysis | + +--- + +## Solution + +### Step 1 — Capture and read the banner -### 1. Capture IQ Stream -Connect to TCP port 9002. A text banner appears first, followed by raw IQ data. ```bash -nc HOST 9002 > capture.raw -# Or use the solve script +nc 9002 > capture.raw ``` -The banner tells you: `IQ baseband, 8000 sps, int16 LE interleaved`. +First bytes are a text banner before the binary IQ data: -### 2. Analyze the Signal -Open the IQ data in a spectrogram tool (e.g., inspectrum, Python matplotlib, or -GNU Radio). You'll see: -- Characteristic **chirp** patterns: frequency sweeps from low to high -- Repeating preambles (identical chirps) -- Gaps of noise between transmissions +``` +IQ baseband, 8000 sps, int16 LE interleaved +Chirp Spread Spectrum detected. N=128. +``` -This is **Chirp Spread Spectrum (CSS)**, the modulation used by LoRa. +> šŸ“ø `[screenshot: nc output showing the IQ stream banner]` + +### Step 2 — Analyze the spectrogram + +Load the IQ data in inspectrum or plot with matplotlib. You see: + +- Characteristic **chirps**: frequency sweeps from low to high +- Repeating groups of identical chirps (preamble) +- Occasional downchirps (sync) + +This is **Chirp Spread Spectrum (CSS)**, identical in principle to LoRa. + +> šŸ“ø `[screenshot: spectrogram showing upchirp preamble and downchirp sync patterns]` + +### Step 3 — Determine parameters -### 3. Determine Parameters - Each chirp spans 128 samples → **N = 128** -- Since N = 2^SF → **SF = 7** (spreading factor) -- Bandwidth = sample rate = 8000 Hz (baseband at Nyquist) -- 7 bits per symbol +- `N = 2^SF` → **SF = 7** (spreading factor) +- Each symbol encodes 7 bits → 128 possible symbols +- Baseband sample rate = 8000 Hz -### 4. Implement Dechirping -The key to CSS demodulation is **dechirping**: +### Step 4 — Implement dechirping -1. Generate the base upchirp (symbol 0): - ``` - x0[n] = exp(j * Ļ€ * n²/N) for n = 0..127 - ``` +```python +import numpy as np -2. To decode a received chirp, multiply by the **conjugate** of the base chirp: - ``` - dechirped[n] = received[n] * conj(x0[n]) - ``` +# Read raw int16 LE interleaved IQ +raw = np.fromfile("capture.raw", dtype=">= 1 val ^= mask return val + +def symbols_to_bytes(symbols): + """Pack 7-bit symbols (SF=7) into 8-bit bytes""" + bits = "" + for s in symbols: + bits += f"{s:07b}" + return bytes(int(bits[i:i+8], 2) for i in range(0, len(bits) - 7, 8)) ``` -### 7. Symbol-to-Byte Unpacking -Each symbol carries 7 bits (SF=7). Concatenate all bits from decoded symbols, -then group into 8-bit bytes. +> šŸ“ø `[screenshot: Python decoder output showing decoded symbols and CRC16 validation pass]` + +### Step 7 — Parse frame payload and decrypt -### 8. Parse Frame Payload Payload format: `[type:1] [data:L] [crc16:2]` -- Type 0x01 = beacon (ASCII text, for verification) -- Type 0x02 = data (XOR-encrypted flag) -- CRC-16 CCITT validates the payload - -### 9. Decrypt Flag -The data frame's content is XOR'd with the repeating key `"L41N"` (4 bytes). +- Type `0x01` = beacon (cleartext ASCII, for verification) +- Type `0x02` = data frame (XOR-encrypted flag) ```python -xor_key = b"L41N" -flag = bytes(b ^ xor_key[i % 4] for i, b in enumerate(encrypted_data)) +import crcmod + +crc16_fn = crcmod.predefined.mkCrcFun('crc-ccitt-false') + +for frame_type, data, crc in decoded_frames: + if crc16_fn(bytes([frame_type]) + data) != crc: + continue # invalid frame + + if frame_type == 0x02: + key = b"L41N" + flag = bytes(b ^ key[i % 4] for i, b in enumerate(data)) + print(flag.rstrip(b'\x00').decode()) ``` -## Key Insights -- CSS/LoRa modulation encodes data as cyclic frequency shifts of a chirp signal -- The dechirp + FFT technique converts the frequency-domain problem into a simple peak detection -- Gray coding ensures that adjacent symbols (close FFT bins) differ by only 1 bit, reducing errors -- The 7-bit symbol → 8-bit byte packing is standard for non-byte-aligned symbol sizes -- The banner hints at CSS ("Chirp Spread Spectrum detected") to point players in the right direction +> šŸ“ø `[screenshot: script printing the decrypted flag from the data frame]` + +### Key insights + +- CSS encodes data as a cyclic frequency shift of a chirp signal +- The dechirp + FFT converts frequency offset into a bin index (symbol value) +- Gray coding ensures adjacent symbols differ by 1 bit, reducing BER on noisy channels +- The beacon frame (type 0x01) provides a known-plaintext verification step +- The stream banner hint `"N=128"` directly gives the spreading factor + +--- ## Flag -`ESPILON{4cc3l4_ch1rp_spr34d_w1r3d}` -## Author -Eun0us +`ESPILON{4cc3l4_ch1rp_spr34d_w1r3d}`