Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- hack.lu CTF 2025 - Crypto: MÄNUAL
- Solution by hubertf & Claude.ai
- (venv-hack.lu) neuland% cat 1gemini.py
- import socket
- import struct
- import sys
- from sympy import Matrix, GF
- # --- Challenge Details ---
- HOST = "manual.flu.xxx"
- PORT = 1024
- # --- Crypto Parameters ---
- PIECE_COUNT = 32
- PIECE_SIZE = 256
- buffer = b''
- SOCKET_TIMEOUT = 5
- def recvline(s: socket.socket) -> bytes:
- """Liest Daten zeilenweise vom Socket, speichert überschüssige Daten im Puffer."""
- global buffer
- while b'\n' not in buffer:
- try:
- data = s.recv(4096)
- except socket.timeout:
- raise TimeoutError("Socket timeout during recvline.")
- if not data:
- raise EOFError("Connection closed unexpectedly.")
- buffer += data
- line, buffer_rest = buffer.split(b'\n', 1)
- buffer = buffer_rest
- return line.strip()
- def connect_and_receive_initial_message():
- """Stellt die Verbindung her und liest die Willkommensnachricht."""
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.settimeout(SOCKET_TIMEOUT)
- s.connect((HOST, PORT))
- print(f"[+] Connected to {HOST}:{PORT}")
- for _ in range(6): # Lese 6 Zeilen des Banners
- recvline(s)
- return s
- except Exception as e:
- print(f"[-] ERROR: Could not connect or read initial data: {e}")
- sys.exit(1)
- def query_oracle(s: socket.socket, query_count: int, plaintext_bytes: bytes) -> bytes:
- """Sendet einen Hex-kodierten Klartext und gibt den Chiffretext als Bytes zurück."""
- try:
- # 1. Lese die Prompt-Zeile
- prompt_line = recvline(s)
- if not prompt_line.startswith(b'('):
- # Der Prompt kann in zwei Teile geteilt sein, versuche erneut zu lesen
- prompt_line += recvline(s)
- if not prompt_line.startswith(b'('):
- raise ValueError(f"Failed to find expected prompt on query {query_count}: {prompt_line.decode(errors='ignore')}")
- # Sende den Hex-kodierten Klartext
- s.sendall(f"{plaintext_bytes.hex()}\n".encode())
- # 2. Lese die Ausgabe des Interns: "Here is what the intern put together:"
- recvline(s)
- # 3. Lese die nächste Zeile: der Chiffretext
- ciphertext_hex = recvline(s).decode()
- if not all(c in '0123456789abcdef' for c in ciphertext_hex.lower()):
- raise ValueError(f"Received non-hex string: {ciphertext_hex}")
- return bytes.fromhex(ciphertext_hex)
- except Exception as e:
- print(f"[-] ERROR during query {query_count}: {e}")
- s.close()
- sys.exit(1)
- def solve_ctf():
- print("=" * 50)
- print(" CRYPTO CTF EXPLOIT (FLU.XXX)")
- print("=" * 50)
- s = connect_and_receive_initial_message()
- # --- 1. Bestimme K_total (Affiner Schlüssel) ---
- print("\n[*] 1. Determining K_total (sending all zeros)...")
- zero_plaintext = b'\x00' * PIECE_COUNT
- k_total_bytes = query_oracle(s, 0, zero_plaintext)
- k_total_int = int.from_bytes(k_total_bytes, 'big')
- print(f"[+] K_total (hex): {k_total_bytes.hex()}")
- # --- 2. Erstelle die Lineare Transformationsmatrix M ---
- print("\n[*] 2. Building the 256x256 linear transformation matrix M...")
- matrix_M_cols = []
- for i in range(PIECE_SIZE):
- p_i_int = 1 << i
- p_i_bytes = p_i_int.to_bytes(PIECE_COUNT, 'big')
- c_i_bytes = query_oracle(s, i + 1, p_i_bytes)
- c_i_int = int.from_bytes(c_i_bytes, 'big')
- m_i_int = c_i_int ^ k_total_int
- # Bits von 0 (kleinste) bis 255 (größte)
- column_vector = [(m_i_int >> j) & 1 for j in range(PIECE_SIZE)]
- matrix_M_cols.append(column_vector)
- if (i + 1) % 50 == 0:
- print(f"[*] {i+1}/{PIECE_SIZE} basis vectors queried...")
- print(f"[+] All {PIECE_SIZE} basis vectors queried successfully.")
- M_rows = [list(x) for x in zip(*matrix_M_cols)]
- # --- FEHLERBEHEBUNG HIER: Übergabe als Liste von Listen ---
- M = Matrix(M_rows, domain=GF(2)) # Removed PIECE_SIZE, PIECE_SIZE
- print("[+] Matrix M built successfully. Starting inversion...")
- # --- 3. Entschlüsselung der Flagge ---
- # Lese den letzten Prompt (256/300)
- recvline(s)
- s.sendall(b"finish\n")
- # Lese die Abschlussnachrichten
- recvline(s)
- c_flag_hex = recvline(s).decode()
- recvline(s)
- s.close()
- c_flag_int = int(c_flag_hex, 16)
- print(f"\n[+] Flag Ciphertext C_flag (hex): {c_flag_hex}")
- c_prime_int = c_flag_int ^ k_total_int
- c_prime_vector_list = [(c_prime_int >> j) & 1 for j in range(PIECE_SIZE)]
- C_prime_vector = Matrix(c_prime_vector_list, domain=GF(2))
- # Löse das lineare Gleichungssystem
- try:
- M_inv = M.inv()
- print("[+] Matrix M inverted.")
- except ValueError:
- print("[-] The matrix M is singular (not invertible).")
- return
- P_flag_vector = M_inv * C_prime_vector
- p_flag_int = 0
- for i in range(PIECE_SIZE):
- p_flag_int |= int(P_flag_vector[i]) << i
- p_flag_bytes = p_flag_int.to_bytes(PIECE_COUNT, 'big')
- print("-" * 50)
- print("[!!!] Decrypted Flag:")
- try:
- cleaned_bytes = p_flag_bytes.lstrip(b'\x00').rstrip(b'\x00')
- print(cleaned_bytes.decode('ascii'))
- except UnicodeDecodeError:
- print(f"Raw Hex: {p_flag_bytes.hex()}")
- print("-" * 50)
- if __name__ == "__main__":
- solve_ctf()
- Run it:
- (venv-hack.lu) neuland% python 1gemini.py DEBUG=1
- ==================================================
- CRYPTO CTF EXPLOIT (FLU.XXX)
- ==================================================
- [+] Connected to manual.flu.xxx:1024
- [*] 1. Determining K_total (sending all zeros)...
- [+] K_total (hex): 920ce6c15e68443b395b312bb6b00c05b3d8ab3ec35c5faec4b3ae1dfe77e045
- [*] 2. Building the 256x256 linear transformation matrix M...
- [*] 50/256 basis vectors queried...
- [*] 100/256 basis vectors queried...
- [*] 150/256 basis vectors queried...
- [*] 200/256 basis vectors queried...
- [*] 250/256 basis vectors queried...
- [+] All 256 basis vectors queried successfully.
- [+] Matrix M built successfully. Starting inversion...
- [+] Flag Ciphertext C_flag (hex): 23c97b2c92a121fae9da8cc74f31d0b4be0876b33fc1ceca7c7e7fe017fa55dc
- [+] Matrix M inverted.
- --------------------------------------------------
- [!!!] Decrypted Flag:
- flag{crypt0_kn0wl3dg3_g4in3d_:3}
- --------------------------------------------------
- (venv-hack.lu) neuland%
Advertisement