//Dis-connec-ted
>Summary
This writeup documents the full solution to the CTF challenge "Dis-connec-ted" (category: cryptography). The challenge provided three WAV files that contain modem-like audio. The goal was to demodulate them, extract binary artifacts, and recover an encrypted message containing the flag in the format deadface{...}.
Files provided in the challenge (extracted):
DialedIn1200.wav— main payload (1200 baud)Key200.wav— ASCII key material (200 baud)Deets10.wav— hint / metadata (10 baud)
Files produced in this solution (in solution/):
001_data.bin— demodulated data fromDialedIn1200.wav(base64 ciphertext)002_key.bin— demodulated data fromKey200.wav(ASCII key)003_deets.bin— demodulated data fromDeets10.wav(human-readable hint)004_solve.py— automated solver used (included below)005_flag.txt— extracted flag006_writeup.md— this writeup
>Tools used
- minimodem (for demodulation)
- Python 3 (virtualenv)
- pycryptodome (AES decryption)
- pwntools (only used previously; not required in final script)
Install dependencies (example):
sudo apt-get update
sudo apt-get install -y minimodem python3-venv python3-pip
python3 -m venv .venv
. .venv/bin/activate
pip install pycryptodome(You may already have a Python virtual environment in the workspace; adapt accordingly.)
>Reproducible steps and important commands
- Demodulate the WAV files with
minimodem. Example commands (one-liners):
# Demodulate the main payload (1200 baud)
minimodem --rx 1200 -f DialedIn1200.wav > solution/001_data.bin
# Demodulate the key (200 baud)
minimodem --rx 200 -f Key200.wav > solution/002_key.bin
# Demodulate the hint (10 baud)
minimodem --rx 10 -f Deets10.wav > solution/003_deets.bin- Inspect the produced files. Example quick inspection (Python or plain
cat/xxd). The results in this run were:
solution/001_data.bin contents (base64 cipher):
Eswf9G+Vm6xxJg9MrPmuz2Ar9VGyWUDKSR0/rFoVfcoNT12NA1Nk59sBJbOE8li7btNC82vX0KINmSEvK9hp1A==
solution/002_key.bin contents (ASCII key):
O1WXfra0lbr84OrUsARr2xf5mDDCnJ1S
solution/003_deets.bin contents (hint):
CBC PKCS5Padding
- Use Python to decode and attempt decryption. The payload looked like base64-encoded bytes. The
Deets10hint indicatedCBC PKCS5Padding, so AES-CBC with PKCS#5/PKCS#7 padding was tried. The key file was ASCII text; its length suggested AES-256 (32 bytes) when encoded as ASCII.
We attempted two reasonable IV strategies:
- Use a zero IV (16 null bytes).
- Treat the first 16 bytes of the decoded payload as an IV and decrypt using the remaining bytes as ciphertext.
In this case the zero IV approach produced readable plaintext containing the flag.
>The solver script (solution/004_solve.py)
Below is the full 004_solve.py used to automate demodulation and decryption. It logs progress and writes 005_flag.txt on success.
#!/usr/bin/env python3
"""Automated solver for the Dis-connec-ted CTF challenge."""
from __future__ import annotations
import base64
import subprocess
from pathlib import Path
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
# Paths
BASE_DIR = Path(__file__).resolve().parent
ROOT_DIR = BASE_DIR.parent
# Input WAV metadata: (friendly label, baud rate, input wav name, output bin name)
TARGETS = (
("DialedIn1200", 1200, "DialedIn1200.wav", "001_data.bin", "📡 Demodulating DialedIn1200.wav at 1200 baud..."),
("Key200", 200, "Key200.wav", "002_key.bin", "🔑 Demodulating Key200.wav at 200 baud..."),
("Deets10", 10, "Deets10.wav", "003_deets.bin", "🧭 Demodulating Deets10.wav at 10 baud..."),
)
FLAG_TOKEN = b"deadface{"
def run_minimodem(label: str, baud: int, wav_name: str, out_name: str, message: str) -> Path:
"""Demodulate a WAV file using minimodem and return the output path."""
wav_path = ROOT_DIR / wav_name
out_path = BASE_DIR / out_name
print(message)
with out_path.open("wb") as outfile:
subprocess.run(
["minimodem", "--rx", str(baud), "-f", str(wav_path)],
check=True,
stdout=outfile,
stderr=subprocess.DEVNULL,
)
print(f"✅ Saved {label} demodulation to {out_path.name} ({out_path.stat().st_size} bytes)")
return out_path
def load_bytes(path: Path) -> bytes:
return path.read_bytes()
def attempt_aes(payload_text: str, key_text: str) -> bytes | None:
"""Attempt AES-CBC decryption using heuristics and return plaintext if it looks right."""
cipher_bytes = base64.b64decode(payload_text.strip())
key_ascii = key_text.strip().encode()
attempts: list[tuple[str, bytes, bytes]] = []
# Hypothesis 1: No IV stored, zero IV used during encryption.
attempts.append(("zero_iv", key_ascii, b"\x00" * AES.block_size))
# Hypothesis 2: First block is IV.
attempts.append(("embedded_iv", key_ascii, cipher_bytes[: AES.block_size]))
for label, key_bytes, iv in attempts:
print(f"🧪 Trying AES-CBC with {label} (key len={len(key_bytes)})")
try:
cipher = AES.new(key_bytes, AES.MODE_CBC, iv)
if label == "embedded_iv":
plaintext = cipher.decrypt(cipher_bytes[AES.block_size :])
else:
plaintext = cipher.decrypt(cipher_bytes)
candidate = unpad(plaintext, AES.block_size)
except Exception as error:
print(f" ⚠️ {label} failed: {error}")
continue
print(f" ✅ {label} produced: {candidate!r}")
if FLAG_TOKEN in candidate:
return candidate
return None
def save_flag(blob: bytes) -> None:
flag_path = BASE_DIR / "005_flag.txt"
flag_text = blob.decode("utf-8", errors="ignore")
flag_path.write_text(flag_text)
print(f"🏁 Flag written to {flag_path.name} -> {flag_text.strip()}")
if __name__ == "__main__":
print("🚀 Starting automated solve...")
outputs: dict[str, bytes] = {}
# Demodulate each WAV file
for label, baud, wav, outfile, message in TARGETS:
out_path = run_minimodem(label, baud, wav, outfile, message)
outputs[label] = load_bytes(out_path)
payload_text = outputs["DialedIn1200"].decode("utf-8", errors="ignore").strip()
key_text = outputs["Key200"].decode("utf-8", errors="ignore").strip()
hint_text = outputs["Deets10"].decode("utf-8", errors="ignore").strip()
print(f"🧾 Payload snippet: {payload_text[:60]}...")
print(f"🧾 Key hint: {key_text}")
print(f"🧾 Mode hint from Deets10: {hint_text}")
flag_bytes = attempt_aes(payload_text, key_text)
if flag_bytes:
save_flag(flag_bytes)
else:
print("❌ AES attempts unsuccessful. Inspect binary outputs for alternative paths.")>Analysis — how it worked and rationale
- The demodulated
001_data.bincontains a Base64 string. Base64-decoding produced 64 bytes. - The demodulated
002_key.bincontains ASCII key text of length 24 characters in the demo, but when encoded produced a 32-byte key. In this specific run the ASCII key encoded to 32 bytes (AES-256 key length), so AES with a 256-bit key was used. - The
003_deets.binfile contained a textual hint:CBC PKCS5Padding, telling us the cipher mode and padding scheme.
From these facts we reasoned AES-CBC with PKCS#5/#7 padding was used. Two usual IV strategies when seeing base64-encoded binary are:
- IV is prepended to the ciphertext (16 bytes at start).
- IV is not transmitted and may be implicitly a known value (often all-zero IV in simple CTF setups).
Both were tried. The zero IV approach decrypted the base64-decoded bytes directly and, after removing PKCS#7 padding, produced the plaintext with the flag.
Result (plaintext):
deadface{Y0urCallCannot^be*connected-As(Dialed)}
This is the flag.
>Artifacts (explicit contents)
solution/001_data.bin (base64 ciphertext):
Eswf9G+Vm6xxJg9MrPmuz2Ar9VGyWUDKSR0/rFoVfcoNT12NA1Nk59sBJbOE8li7btNC82vX0KINmSEvK9hp1A==
solution/002_key.bin (ASCII key):
O1WXfra0lbr84OrUsARr2xf5mDDCnJ1S
solution/003_deets.bin (hint):
CBC PKCS5Padding
solution/005_flag.txt:
deadface{Y0urCallCannot^be*connected-As(Dialed)}
>How to reproduce locally (copy/paste)
# 1. Install minimodem if you don't have it
sudo apt-get update
sudo apt-get install -y minimodem
# 2. Create a virtualenv and install pycryptodome
python3 -m venv .venv
. .venv/bin/activate
pip install pycryptodome
# 3. Demodulate
minimodem --rx 1200 -f DialedIn1200.wav > solution/001_data.bin
minimodem --rx 200 -f Key200.wav > solution/002_key.bin
minimodem --rx 10 -f Deets10.wav > solution/003_deets.bin
# 4. Run the provided solver (or run a quick Python snippet):
python3 solution/004_solve.py
# 5. The flag will be written to solution/005_flag.txt
cat solution/005_flag.txt>Notes and follow-ups
- The solver includes a small set of heuristics. For other CTFs, the IV/key extraction rules might differ (e.g., key derived via KDF, key transmitted in different encoding, or XOR obfuscation). The script can be extended with more heuristics.
- If
minimodemfails due to sound card or sample-rate issues, consider converting WAV sample rates or usingsoxto resample to 44.1 kHz or 48 kHz before demodulation. - The deets file explicitly mentioned CBC and padding; it’s always good to look for textual hints in challenge artifacts.
>Conclusion
The three audio files were demodulated to produce a base64-encoded AES-CBC ciphertext, an ASCII key, and a plaintext hint. AES-CBC decryption with a zero IV and PKCS#5 padding yielded the plaintext containing the flag.
Flag (final):
deadface{Y0urCallCannot^be*connected-As(Dialed)}