DF-3112 / verdict.json
{ "finding_id": "DF-3112", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "Reproduced on the audit guest (DFly 6.5-DEVELOPMENT, X86_64_GENERIC): an unprivileged uid-1001 process executed HAMMERIOC_GETHISTORY/GET_VERSION/GET_INFO/SYNCTID/SCAN_PSEUDOFS (rc=0) on an O_RDONLY fd on a HAMMER1 mount, while gated contrast commands HAMMERIOC_PRUNE/ADD_SNAPSHOT returned EPERM from the same dispatcher (hammer_ioctl.c:72 gate result overwritten by 9 case labels). No direct memory-safety impact; ceiling is unprivileged fs-topology/state disclosure (incl. unclamped disk-length copyout in scan_pseudofs, hammer_pfs.c:363), TID-history enumeration, and global flusher forcing via SYNCTID. Patched kernel (all 9 handlers wrapped in the standard `if (error == 0)` guard) returns EPERM for all five as unprivileged and still succeeds as root - fix validated end-to-end.", "exploit_chain": "unprivileged user opens any file on a HAMMER1 mount -> ioctl(2) with the ungated commands -> dispatcher runs handler despite failed caps_priv_check (info disclosure / policy bypass); no escalation chain exists for this class.", "evidence": [ "findings/poc/DF-3112/run.log: lines [A]-[E] *** PRIV GATE BYPASSED *** as uid=1001 with [F]/[G] EPERM contrast", "findings/poc/DF-3112/run.root.log: same binary as root reaches handler validation (ADD_SNAPSHOT EINVAL at count check), proving the EPERM is the caps gate", "findings/poc/DF-3112/run.patched.log: [A]-[E] -> EPERM on the fix-built kernel", "findings/poc/DF-3112/fix.diff: 9 `if (error == 0) guards", "sys/vfs/hammer/hammer_ioctl.c:72,86,117,121,177,181,213,233,237,263" ], "kernel_refs": [ "sys/vfs/hammer/hammer_ioctl.c:72", "sys/vfs/hammer/hammer_ioctl.c:86", "sys/vfs/hammer/hammer_ioctl.c:117", "sys/vfs/hammer/hammer_ioctl.c:121", "sys/vfs/hammer/hammer_ioctl.c:177", "sys/vfs/hammer/hammer_ioctl.c:181", "sys/vfs/hammer/hammer_ioctl.c:213", "sys/vfs/hammer/hammer_ioctl.c:233", "sys/vfs/hammer/hammer_ioctl.c:237", "sys/vfs/hammer/hammer_ioctl.c:263", "sys/vfs/hammer/hammer_pfs.c:363" ], "poc_changes": "Seed sketch had none; wrote from scratch. In-program privilege drop had to be conditional (setuid(65534) fails when the PoC is already run as unprivileged uid 1001); added keep argv mode so the same binary demonstrates the root-path contrast and the DF-3114 fix validation on the patched kernel. Included kernel headers via -I/usr/src/sys using the sbin/hammer pattern.", "attempts": 4, "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", "runtime_sec": 1380, "guest_dirty": 1, "build_cmd": "cc -I/usr/src/sys -o /tmp/poc /tmp/hammer_gate_poc.c", "run_cmd": "/tmp/poc /mnt/h1/histfile (as unprivileged user; root contrast: /tmp/poc /mnt/h1/histfile keep)", "code_hash": "464fd8750494ffbfc0d5c269cc6d830527553311e503554d07ef7a7ae56f459e", "notes": "Guest /tmp is a 975M tmpfs - kernel builds need TMPDIR on / (first make nativekernel died ENOSPC in /tmp). Hammer1 test image: newfs_hammer -f on vn0 (needs -f below 10GB), mounted at /mnt/h1. Three-run stability confirmed (5 bypass markers each run). Same defect class as DF-0815 (BULKFREE gate bypass, hammer2) on the HAMMER1 dispatcher.", "recommended_fix": "Wrap all nine ungated case labels in hammer_ioctl() with the same `if (error == 0) guard used by the rest of the dispatcher.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Sun Sep 6 13:09:37 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched kernel (fix.diff applied to /usr/src, make nativekernel KERNCONF=X86_64_GENERIC, reboot) returns EPERM for all five ungated-class commands as uid 1001 while root still succeeds - the exact bypass behavior is gone.", "fix_evidence": [ "findings/poc/DF-3112/run.patched.log", "findings/poc/DF-3112/run.patched.root.log", "findings/poc/DF-3112/fix_combined_for_validation.diff" ] } |