Dérive dans l'espace
Last updated
Was this helpful?
Last updated
Was this helpful?
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 :