Spacemark

Catégorie: Cryptanalyse - Difficulté: Moyen

Description:

Solution:

Voici le script pour répondre correctement aux attentes du système tournant derrière la connexion nc :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re, socket, zlib, json, sys, textwrap

HOST, PORT = "challenges.404ctf.fr", 31207
SIZE, BLOCKS = 4096, 128                        # paramètres fixes

def recv_until(sock: socket.socket, sentinel: bytes = b'>>>') -> bytes:
    data = b""
    while sentinel not in data:
        part = sock.recv(4096)
        if not part:
            break
        data += part
    return data

def extract_hex_banner(text: str) -> str:
    """Concatène toutes les lignes composées uniquement d'hexadécimal (≥32 c.)."""
    hex_lines = [l.strip() for l in text.splitlines()
                 if len(l.strip()) >= 32 and re.fullmatch(r"[0-9a-f]+", l.strip())]
    if not hex_lines:
        raise ValueError("Aucun fragment hex trouvé.")
    hx = "".join(hex_lines)
    if len(hx) % 2:                        # robustesse
        hx = hx[:-1]
    return hx

def first_pass_estimate(bits):
    """Reconstruit les 128 bits par le comptage des uns."""
    return ''.join(
        '1' if sum(bits[i*SIZE:(i+1)*SIZE]) != SIZE//2 else '0'
        for i in range(BLOCKS)
    )

def attempt_once(answer: str) -> str:
    """Ouvre une connexion, renvoie `answer`, retourne la réponse complète."""
    with socket.create_connection((HOST, PORT)) as s:
        banner = recv_until(s)             # bannière + hex + prompt
        s.sendall((answer + '\n').encode())
        return banner.decode(errors="ignore") + s.recv(4096).decode(errors="ignore")

def solve_spacemark():
    # --- 1ʳᵉ connexion : récupération du watermark compressé ---
    with socket.create_connection((HOST, PORT)) as s:
        raw = recv_until(s)                # jusqu’au prompt
        text = raw.decode(errors="ignore").lower()
        hex_clean = extract_hex_banner(text)
        bits_json = zlib.decompress(bytes.fromhex(hex_clean))
        bits = json.loads(bits_json.decode())
        if len(bits) != BLOCKS*SIZE:
            print("Watermark inattendu :", len(bits))
            sys.exit(1)

        # --- estimation puis envoi ---
        guess = first_pass_estimate(bits)
        s.sendall((guess + '\n').encode())
        reply = s.recv(4096).decode(errors="ignore")

    # --- analyse de la réponse ---
    if "Congratz" in reply:
        print(reply)                       # flag déjà obtenu
        return

    # Sinon, le serveur imprime : "Nope...\n<watermark>"
    m = re.search(r'\b[01]{128}\b', reply)
    if not m:
        print("Échec mais watermark non trouvé :\n", reply)
        sys.exit(1)

    real_mark = m.group(0)
    # --- 2ᵉ connexion, envoi de la vraie chaîne ---
    final_reply = attempt_once(real_mark)
    print(final_reply)

if __name__ == "__main__":
    solve_spacemark()

Le script tourne quelques secondes et nous retourne la réponse directement dans le terminal :

🚩FLAG

404CTF{LGC_4r3_no_G0od}

Last updated

Was this helpful?