Rating:

---

## Solver

```python
#!/usr/bin/env python3
import socket, ssl, hashlib, ast, re, sys, time

HOST = "nitwit.challs.pwnoh.io"
PORT = 1337
CONNECT_TIMEOUT = 15
READ_TIMEOUT = 2.5
POST_SEND_READ_LOOPS = 40

def h(x: bytes) -> bytes:
return hashlib.sha256(x).digest()

def chain(x: bytes, t: int) -> bytes:
for _ in range(t):
x = h(x)
return x

def int_to_vec_buggy(m: int, vec_len: int, base: int = 16):
digits = [0] * vec_len
i = vec_len - 1
while m > 0:
digits[i] = m % base
m //= base
i -= 1
return digits

def df_vec(msg_bytes: bytes):
d = 15
n0 = 64
n1 = 2
m_int = int.from_bytes(msg_bytes, "big")
m_vec = int_to_vec_buggy(m_int, n0, 16)
c = d * n0 - sum(m_vec)
c_vec = int_to_vec_buggy(c, n1, 16)
return m_vec + c_vec

TOKEN_RE = re.compile(rb"""b(['"])(?:\\.|(?!\1).)*\1""", re.S)

def extract_sig_list(buf: bytes, expect: int = 66):
sig = []
for m in TOKEN_RE.finditer(buf):
lit = m.group(0).decode("ascii", "strict")
try:
sig.append(ast.literal_eval(lit))
except Exception:
continue
if len(sig) == expect:
return sig
return None

FLAG_RE = re.compile(rb'(?i)(flag\{[^}]+\}|pwnoh\{[^}]+\}|pwn\{[^}]+\})')

def main():
m0 = b"\x00" * 32
m_new = b"admin" + b"\xff" * 23 + b"\x40" + b"\x00" * 3
assert len(m_new) == 32

s_old = df_vec(m0)
s_new = df_vec(m_new)
for i, (a, b) in enumerate(zip(s_old, s_new)):
if b < a:
raise RuntimeError(f"delta negative at idx {i}: {b} < {a}")

ctx = ssl.create_default_context()
with socket.create_connection((HOST, PORT), timeout=CONNECT_TIMEOUT) as raw:
with ctx.wrap_socket(raw, server_hostname=HOST) as s:
s.settimeout(CONNECT_TIMEOUT)

buf = b""
while b">>> " not in buf:
chunk = s.recv(8192)
if not chunk:
break
buf += chunk
sys.stdout.write(chunk.decode(errors="ignore"))
sys.stdout.flush()

s.sendall((m0.hex() + "\n").encode())

data = b""
sig = None
while True:
chunk = s.recv(8192)
if not chunk:
break
data += chunk
sys.stdout.write(chunk.decode(errors="ignore"))
sys.stdout.flush()
if sig is None:
sig = extract_sig_list(data, expect=66)
if sig is not None and (b">>> " in data):
break

if sig is None or len(sig) != 66:
raise RuntimeError(f"[x] Failed to parse signature list (got {0 if sig is None else len(sig)})")

s.sendall((m_new.hex() + "\n").encode())

data2 = b""
while b">>> " not in data2:
chunk = s.recv(8192)
if not chunk:
break
data2 += chunk
sys.stdout.write(chunk.decode(errors="ignore"))
sys.stdout.flush()

forged = [chain(sig[i], s_new[i] - s_old[i]) for i in range(len(s_new))]
payload = repr(forged) + "\n"
s.sendall(payload.encode())

s.settimeout(READ_TIMEOUT)
full = b""
got_flag = None
for _ in range(POST_SEND_READ_LOOPS):
try:
chunk = s.recv(8192)
if not chunk:
break
full += chunk
sys.stdout.write(chunk.decode(errors="ignore"))
sys.stdout.flush()
m = FLAG_RE.search(full)
if m:
got_flag = m.group(1).decode(errors="ignore")
break
except socket.timeout:
continue

if got_flag:
print("\n[+] FLAG:", got_flag)
else:
time.sleep(0.2)
try:
while True:
chunk = s.recv(8192)
if not chunk:
break
full += chunk
sys.stdout.write(chunk.decode(errors="ignore"))
sys.stdout.flush()
m = FLAG_RE.search(full)
if m:
print("\n[+] FLAG:", m.group(1).decode(errors="ignore"))
break
except Exception:
pass

if __name__ == "__main__":
main()

```

## Output:
```text
FLAG: bctf{i_f0rg0t_h0w_t0_r3ad_m4th_n0t4t10n}
```

Original writeup (https://rxx2me.github.io/posts/nitwit/).