# LAIN vs Knights — Solution **Difficulty:** Hard | **Category:** IoT | **Flag:** `ESPILON{0nlY_L41N_C4N_S0lv3}` ## Overview A UART interface exposes a simulated "Wired" network with 1200 nodes, 7 protected by Knights and 1 by the Founder (Eiri Masami). Defeat all 7 Knights to collect fragments, combine them into an exploit hash, and present it to the Founder. - **TX (port 1111)**: Read only - **RX (port 2222)**: Write only ## Step 1 — Enumerate nodes ```text bus.map ``` Returns: `1200 nodes total — 7 knights active.` Scan all nodes to find the 7 Knights and the Founder: ```python import socket, time, re HOST = "" TX_PORT, RX_PORT = 1111, 2222 def recv_until(sock, expect=">", timeout=2.0): data = b"" sock.settimeout(timeout) while True: try: c = sock.recv(4096) if not c: break data += c if expect.encode() in data: break except socket.timeout: break return data.decode(errors="ignore") def scan_nodes(node_min=1, node_max=1200): tx = socket.socket(); tx.connect((HOST, TX_PORT)) rx = socket.socket(); rx.connect((HOST, RX_PORT)) recv_until(tx) # flush banner found = [] for nid in range(node_min, node_max + 1): rx.sendall(f"bus.connect @node:{nid:04d}\n".encode()) time.sleep(0.08) out = recv_until(tx) if re.search(r"KNIGHT|FOUNDER|EIRI", out, re.I): kind = "knight" if "KNIGHT" in out.upper() else "founder" print(f"[+] Node {nid:04d}: {kind}") found.append((nid, kind, out)) rx.sendall(b"bus.disconnect\n") recv_until(tx) tx.close(); rx.close() return found ``` Knights found at (example run): `0067, 0113, 0391, 0529, 0619, 0901, 0906` Founder found at: `0311` ## Step 2 — Get hints from Lain nodes Connect to any Lain node and use `node.truth` repeatedly until you get: ```text Order matters. Use: i2c_mirror, can_checksum, spi_parity, sram_write, logic_and, fuse_bits, fault_injection. Assemble fragments in this order as a single string, with no separators. Hash this string using SHA256. Take the first 24 hex digits of the hash. ``` ## Step 3 — Defeat each Knight ### Knight 1 — I2C_MIRROR Find two distinct messages with the same byte sum mod N. ```python def find_i2c_pair(modulo): for a in range(256): for b in range(256): for c in range(256): if a == c: continue if (a + b) % modulo == (c + b) % modulo: return bytes([a, b]).hex(), bytes([c, b]).hex() ``` ```text node.i2c_write "0000" node.i2c_write "7600" node.submit_pair "0000" "7600" # → fragment i2c_mirror=0000_7600 ``` ### Knight 2 — CAN_CHECKSUM Find a CAN frame whose CRC8 (poly=0x2F) equals the target byte. ```python def can_crc8(data, poly=0x2F): c = 0 for b in data: c ^= b for _ in range(8): c = ((c << 1) ^ poly) & 0xFF if c & 0x80 else (c << 1) & 0xFF return c target = 0x91 for i in range(256): for j in range(256): if can_crc8(bytes([i, j])) == target: print(f"Found: {bytes([i,j]).hex()}") # → 0026 break ``` ```text node.can_send "0026" # → fragment can_checksum=0026 ``` ### Knight 3 — SPI_PARITY Find a byte with exactly 5 bit transitions (0↔1 between adjacent bits). ```python def count_transitions(b): bits = f"{b:08b}" return sum(1 for i in range(7) if bits[i] != bits[i+1]) # 0x15 = 00010101 → transitions at positions 2,3,4,5,6 = 5 ✓ answer = next(hex(b)[2:] for b in range(256) if count_transitions(b) == 5) # → '15' ``` ```text node.spi_write 15 # → fragment spi_parity=15 ``` ### Knight 4 — SRAM_WRITE Write a specific value to a specific address. ```text node.write 0x8b 0x89 # → fragment sram_write=8b_89 ``` ### Knight 5 — LOGIC_AND Find a pair `(a, b)` such that `a & b` equals the secret. Probe with `node.and_probe 0xff 0xff` → reveals the secret (e.g. `0xa8`). Then `a = secret`, `b = 0xff`: ```text node.and_probe 0xff 0xff # → "ff & ff = ff" (not our target — reveals target is 0xa8) node.submit_and 0xa8 0xff # → fragment logic_and=a8_ff ``` ### Knight 6 — FUSE_BITS Probe with full mask to reveal the secret fuse bits directly: ```text node.fuse_probe 0xff # → "Probe: (fuse & ff) = 30" node.submit_fuse 0x30 # → fragment fuse_bits=30 ``` ### Knight 7 — FAULT_INJECTION Try all offset/mask combinations until the knight is purged: ```python for offset in range(8): for mask in [1, 2, 4, 8, 16, 32, 64, 128]: print(f"node.inject {offset} 0x{mask:02x}") # Correct answer: offset=2, mask=0x08 ``` ```text node.inject 2 0x08 # → fragment fault_injection=2_08 ``` ## Step 4 — Check fragment collection ```text fragments # i2c_mirror = 0000_7600 # can_checksum = 0026 # spi_parity = 15 # sram_write = 8b_89 # logic_and = a8_ff # fuse_bits = 30 # fault_injection = 2_08 ``` ## Step 5 — Build the exploit Concatenate fragments in the order Lain specified, hash with SHA-256, take first 24 hex chars: ```python import hashlib fragments = { "i2c_mirror": "0000_7600", "can_checksum": "0026", "spi_parity": "15", "sram_write": "8b_89", "logic_and": "a8_ff", "fuse_bits": "30", "fault_injection":"2_08", } order = ["i2c_mirror", "can_checksum", "spi_parity", "sram_write", "logic_and", "fuse_bits", "fault_injection"] payload = "".join(fragments[k] for k in order) # → "0000_76000026158b_89a8_ff302_08" exploit = hashlib.sha256(payload.encode()).hexdigest()[:24] # → "69b4a17e33b0cdace34b7610" ``` ## Step 6 — Submit to the Founder and get the flag ```text bus.connect @node:0311 node.exploit 69b4a17e33b0cdace34b7610 # → "[ROOT] Exploit accepted! You are now root." node.flag # → ESPILON{0nlY_L41N_C4N_S0lv3} ``` ## Flag `ESPILON{0nlY_L41N_C4N_S0lv3}` ## Author Eun0us