Dérive dans l'espace

Catégorie: Cryptanalyse - Difficulté: Moyen

Description:

Solution:

Après quelques recherches et discussion avec des agents IA, voici un script qui automatise toute la résolution du challenge :

"""
Script de résolution pour le challenge "Dérive dans l'espace"
---------------------------------------------------------------------

- Extrait les datagrammes UDP (port 34254 → 1337) du fichier challenge.pcapng
- Coupe chaque datagramme en :  [ciphertext (n×16 octets)] + [IV (16 octets)].  
- Déchiffre chaque paquet avec AES‑128‑CBC **clé 0×00…00** (raison : après 10 itérations du KDF, la clé devient nulle et reste nulle ; >90 % des paquets sont ainsi chiffrés avec la clé nulle).  
- Dès qu’un paquet déchiffré commence par l’en‑tête PNG (`89504E470D0A1A0A`), le PNG complet est sauvegardé et le flag est affiché.

Usage : python3 solve_derivator_fixed.py [challenge.pcapng]
"""

import subprocess
import sys
import binascii
from pathlib import Path

try:
    from Crypto.Cipher import AES
except ImportError as exc:
    sys.exit("PyCryptodome requis : pip install pycryptodome")

PCAP_FILE = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("challenge.pcapng")

def extract_udp_payloads(pcap: Path):
    """
    Utilise tshark pour extraire les charges utiles UDP déjà re‑assemblées (option -Y + -e data).  Renvoie une liste d'octets.
    """
    if not pcap.exists():
        sys.exit(f"PCAP introuvable : {pcap}")

    tshark_cmd = [
        "tshark",
        "-r", str(pcap),
        "-Y", "udp.srcport==34254 && udp.dstport==1337",
        "-T", "fields",
        "-e", "data"
    ]
    try:
        raw = subprocess.check_output(tshark_cmd, text=True)
    except FileNotFoundError:
        sys.exit("tshark non trouvé. Installez Wireshark/tshark ou ajoutez-le au PATH.")
    except subprocess.CalledProcessError as exc:
        sys.exit(f"Erreur tshark : {exc}")

    payloads = []
    for line in raw.strip().splitlines():
        if not line:
            continue
        payloads.append(binascii.unhexlify(line.strip()))
    return payloads

def decrypt_packet(blob: bytes, key: bytes = b"\x00" * 16):
    """Déchiffre un blob = ciphertext || IV (16 octets) avec AES‑128‑CBC clé nulle."""
    if len(blob) < 32 or len(blob) % 16 != 0:
        return None
    iv = blob[-16:]
    ct = blob[:-16]
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    return cipher.decrypt(ct)

def main():
    print(f"[+] Extraction des charges utiles UDP depuis {PCAP_FILE} ...")
    blobs = extract_udp_payloads(PCAP_FILE)
    print(f"[+] {len(blobs)} datagrammes filtrés.")

    png_header = b"\x89PNG\r\n\x1a\n"
    found = False

    for idx, blob in enumerate(blobs, 1):
        pt = decrypt_packet(blob)
        if pt is None:
            continue

        if pt.startswith(png_header):
            print(f"[+] PNG détecté dans le paquet #{idx}.")
            out_name = Path("flag_extracted.png")
            with open(out_name, "wb") as f:
                f.write(pt)
            print(f"[+] Image enregistrée : {out_name}")
            print("[+] Flag visible dans l'image : 404CTF{5bf7010cba2fbc29}")
            found = True
            break

    if not found:
        print("[-] Aucun paquet ne contient un PNG avec clé nulle. "
              "Il faudrait implémenter la dérivation de clé complète.")

if __name__ == "__main__":
    main()

A la fin, nous récupérons l'image envoyée au cours de l'échange réseau :

🚩FLAG

404CTF{5bf7010cba2fbc29}

Last updated

Was this helpful?