import socket, re, sys from typing import List, Optional HOST = "45.55.60.238" PORT = 1337 KNOWN_PREFIX = b"NICC{https://ww" PROMPT = b"Enter p (hexadecimal): " BLOCK_BYTES = 16 SOCKET_TIMEOUT = 10.0 def recv_until_prompt(s: socket.socket) -> bytes: buf = b"" s.settimeout(SOCKET_TIMEOUT) while True: try: part = s.recv(4096) if not part: break buf += part if PROMPT in buf: break except socket.timeout: break return buf def send_and_get_ciphertext(hexpayload: str) -> str: with socket.create_connection((HOST, PORT), timeout=SOCKET_TIMEOUT) as s: recv_until_prompt(s) s.sendall((hexpayload + "\n").encode()) resp = recv_until_prompt(s) text = resp.decode(errors="ignore") if "Enter p (hexadecimal):" in text: text = text.split("Enter p (hexadecimal):")[0] hex_cands = re.findall(r"[0-9A-Fa-f]+", text) if not hex_cands: raise RuntimeError("No hex-like substring in server response. Raw (truncated):\n" + text[:2000]) return max(hex_cands, key=len).lower() def split_blocks(hexstr: str) -> List[str]: blen = BLOCK_BYTES * 2 return [hexstr[i:i+blen] for i in range(0, len(hexstr), blen)] def recover_flag(seed: Optional[bytes] = None, max_bytes: int = 512) -> bytes: recovered = bytearray() if seed: recovered.extend(seed) print(f"[+] Seeded with known prefix ({len(seed)} bytes): {seed!r}") # iterate until max_bytes or until we find a closing brace i = len(recovered) while i < max_bytes: # compute filler so that the unknown byte becomes the last byte of a chosen block: r = (BLOCK_BYTES - 1) - (i % BLOCK_BYTES) # filler length p_tail = b"A" * r # known window: the bytes immediately preceding the unknown byte within the flag block # this is recovered[i - (BLOCK_BYTES - 1) : i] (may be shorter for early bytes) start_index = max(0, i - (BLOCK_BYTES - 1)) known_window = bytes(recovered[start_index:i]) # Build dictionary blocks: for each candidate c, create a 16-byte block that ends with c. # Construct raw = p_tail + known_window + c, but take the LAST 16 bytes of that to ensure correct alignment dict_blocks = [] for c in range(256): raw = p_tail + known_window + bytes([c]) block = raw[-BLOCK_BYTES:] # ensure exactly one 16-byte block aligned to the right dict_blocks.append(block) # final payload: dictionary blocks concatenated, then p_tail (so the flag follows) payload = b"".join(dict_blocks) + p_tail hexpayload = payload.hex() try: C = send_and_get_ciphertext(hexpayload) except Exception as e: print("[!] Network or parsing error:", e) break blocks = split_blocks(C) dict_len = len(dict_blocks) # target block index: dictionary occupies dict_len blocks; the block containing the unknown byte # will be at offset dict_len + (i // BLOCK_BYTES) target_index = dict_len + (i // BLOCK_BYTES) if target_index >= len(blocks): print("[!] Unexpected response: insufficient blocks. Received", len(blocks), "needed", target_index + 1) break target_block = blocks[target_index] # search match in the dictionary region (first dict_len blocks) found = False for idx in range(dict_len): if blocks[idx] == target_block: recovered.append(idx) printable = chr(idx) if 32 <= idx < 127 else '.' print(f"[+] Recovered byte {i}: 0x{idx:02x} ({printable})") found = True break if not found: print(f"[!] No match for byte index {i}. Diagnostic: returned {len(blocks)} blocks; sample target (first 32 hex): {target_block[:32]}") break i += 1 # stop if we recovered the closing brace if recovered.endswith(b"}"): print("[+] Closing brace encountered; finishing.") break return bytes(recovered) if __name__ == "__main__": print(f"Starting recovery against {HOST}:{PORT}\n") flag_bytes = recover_flag(KNOWN_PREFIX, max_bytes=512) print("\nRecovered (raw):", flag_bytes) try: print("Recovered (utf-8):", flag_bytes.decode()) except Exception: print("Recovered contains non-UTF8 bytes.")