# Dérive dans l'espace

**Catégorie:** Cryptanalyse - **Difficulté:** Moyen

{% file src="<https://4219205392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fybn4btwQnvitOl9uXz9p%2Fuploads%2FAvWKDhcJrubuzNowu95w%2Fchallenge.pcapng?alt=media&token=4d64e3f3-2ddc-451f-a01e-d45a609e5608>" %}

{% file src="<https://4219205392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fybn4btwQnvitOl9uXz9p%2Fuploads%2FA3p6qCQ1dN1ehUML6x0q%2Fsrc.tar.gz?alt=media&token=f28d009a-d2c9-4142-a2dd-55a962b52d9d>" %}

**Description:**

<figure><img src="https://4219205392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fybn4btwQnvitOl9uXz9p%2Fuploads%2Fft09qkEiitYHhF4VbBjv%2Fimage.png?alt=media&#x26;token=df9306dc-7179-4e63-b86b-081439aabd6d" alt=""><figcaption></figcaption></figure>

Solution:

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

```python
"""
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 :&#x20;

<figure><img src="https://4219205392-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fybn4btwQnvitOl9uXz9p%2Fuploads%2FsrM0f17D5V7wRqvDyxGn%2Fflag_extracted.png?alt=media&#x26;token=ff2f5fce-9760-48bf-8caf-cf0036f283b2" alt=""><figcaption></figcaption></figure>

<details>

<summary>🚩FLAG</summary>

`404CTF{5bf7010cba2fbc29}`

</details>
