Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
CryptoMedium

Dis Connec Ted

CTF writeup for Dis Connec Ted from deadface

//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 from DialedIn1200.wav (base64 ciphertext)
  • 002_key.bin — demodulated data from Key200.wav (ASCII key)
  • 003_deets.bin — demodulated data from Deets10.wav (human-readable hint)
  • 004_solve.py — automated solver used (included below)
  • 005_flag.txt — extracted flag
  • 006_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):

bash
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

  1. Demodulate the WAV files with minimodem. Example commands (one-liners):
bash
# 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
  1. 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
  1. Use Python to decode and attempt decryption. The payload looked like base64-encoded bytes. The Deets10 hint indicated CBC 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.

python
#!/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

  1. The demodulated 001_data.bin contains a Base64 string. Base64-decoding produced 64 bytes.
  2. The demodulated 002_key.bin contains 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.
  3. The 003_deets.bin file 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)

bash
# 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 minimodem fails due to sound card or sample-rate issues, consider converting WAV sample rates or using sox to 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)}