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.")