⬢ DragonFlyBSD Kernel Audit
DF-3157 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3157",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "Stock INVARIANTS kernel: an unprivileged forged NFS file handle (unvalidated defid_dirclust/defid_dirofs through msdosfs_fhtovp -> deget -> readep/bptoep -> DE_INTERNALIZE/DE_EXTERNALIZE) yields (a) a deterministic fatal kernel page fault in deget from a single GETATTR (3/3, nfsd thread), (b) a silent 31-byte heap OOB read exfiltrated via stat() fields, (c) an arbitrary in-volume 32-byte read proven with a planted marker returned as st_size, (d) a persisted 32-byte structured write at a forged device offset with >=9 attacker-chosen bytes (utimes+chmod -> deupdat), and (e) an nfsd wedge from a forged SETATTR. Patched kernel (fix.diff: fh bounds validation) rejects every OOB/out-of-range probe with EINVAL, the panic and wedge are gone, and legitimate in-range handles still work; the residual in-range cluster-content disclosure via stat is inherent to untyped FAT clusters and matches upstream validation level. uid=0 escalation not pursued (buffer-cache heap grooming research project); panic + arbitrary-offset R/W + leak fully establish the memory-safety violation.",
 "exploit_chain": "NFS-exported msdosfs -> client learns fsid+fh format via mountd GETFH (standard client privilege) -> forged 28-byte fh with chosen (dirclust, dirofs) -> NFSPROC3_GETATTR -> deget/readep OOB or arbitrary-offset read -> stat()/fattr3 exfil (leak); NFSPROC3_SETATTR (utimes/chmod/truncate) -> msdosfs_setattr -> deupdat -> *dirp = dir 32-byte write at the same unbounded location (heap OOB write / arbitrary device-offset corruption); dirofs=511-family -> fatal page fault in DE_INTERNALIZE (panic), dirofs beyond buffer -> nfsd wedge. Local root-gated equivalents via fhstat(2)/fhopen(2).",
 "evidence": [
  "findings/poc/DF-3157/run.log -- marker read st_size=1145258561 (0x44434241 ABCD), OOB read garbage (mtime=0x12CE0FC0), persisted 32-byte write into VICTIM.DAT cluster (raw-block hexdump, bytes 11/22-25 changed to exact FAT-encoded futimes value)",
  "findings/poc/DF-3157/panic.txt + panic_capture.txt -- Fatal trap 12 at deget+0x2b5 (inlined DE_INTERNALIZE, movq (%rax),%rcx), current process = nfsd server thread, 3/3 deterministic",
  "findings/poc/DF-3157/run.2.log -- patched-kernel matrix: (3,481)/(3,511)/(0,481)/(0,511)/(0,8192)/(4294967295,0) all EINVAL; NFS (0,32) clean ESTALE instead of panic; nfsd healthy after (0,511) SETATTR"
 ],
 "kernel_refs": [
  "sys/vfs/msdosfs/msdosfs_vfsops.c:856",
  "sys/vfs/msdosfs/msdosfs_denode.c:363-377",
  "sys/vfs/msdosfs/msdosfs_denode.c:453-463",
  "sys/vfs/msdosfs/msdosfs_lookup.c:888-899",
  "sys/vfs/nfs/nfs_subs.c:1243"
 ],
 "poc_changes": "Seed sketch did not exist; written from scratch. Fixes vs initial draft: RPC reply parser (reply_stat/verf offsets), GETPORT null-cred verifier words, fh must be exactly NFSX_V3FH=28 with defid at offset 8 (nfsm_srvmtofh), mountd rejects unprivileged ports so fh is forged from the mount fsid (env-seeded locally; mountd-supplied remotely), status-first GETATTR reply parsing, bounds-checked XDR reads after a client segfault.",
 "attempts": 3,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline) / #3: Mon Sep 7 09:32:17 UTC 2026 (patched)",
 "runtime_sec": 6900,
 "guest_dirty": 0,
 "build_cmd": "cc -O2 -o /tmp/df3157 df3157.c; cc -O2 -o /tmp/fhtest fhtest.c; (fix) cd /usr/src && make -j6 nativekernel && make installkernel",
 "run_cmd": "sh setup.sh; DF3157_SRCPORT=800 DF3157_FSID0=0x1e110807 DF3157_FSID1=3 /tmp/df3157 getattr 0 32 (panic); /tmp/fhtest 3 0; /tmp/fhtest 3 0 write",
 "code_hash": "b3cd52e94ba7154cbad2b001b5dab136cd7c411a94a8dbb76cd886ddcf99a238",
 "notes": "fsid seeding root-assisted locally because DF masks f_fsid for non-root statfs; network clients obtain it from mountd GETFH (the real remote path). nfsd-context ESTALE for cluster-mode keys observed pre- and post-fix, not root-caused (documented in VERDICT.md); the local syscall path proves all primitives and the nfsd path demonstrably reaches deget (panicked there). DF-3091 covers a different arbitrary-offset path (pcbmap with crafted on-disk chains); this finding needs no crafted image.",
 "recommended_fix": "Validate fh in msdosfs_fhtovp (fid_len, cluster range CLUST_FIRST..pm_maxcluster, FAT12/16 root-area bounds, and uniform (dirofs & crbomask) <= bpcluster-32) -- validated in fix.diff"
}