Spacemark
Last updated
Was this helpful?
Last updated
Was this helpful?
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 :