DF-5374 / verdict.json
{ "finding_id": "DF-5374", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "REPRODUCED: deterministic NULL-deref kernel panic from one 8-byte write(2) (or one read(2)) on /dev/targ before any TARGIOCENABLE. targwrite (sys/bus/cam/scsi/scsi_target.c:566-602) and targread (:874) call cam_periph_lock(softc->periph) with softc->periph == NULL (only targctor :522 sets it); cam_periph_lock is CAM_SIM_LOCK(periph->sim) (cam_periph.h:186-190). Guest proof: Fatal trap 12, fault virtual address 0x40 (= offsetof(struct cam_periph, sim)), Stopped at targwrite+0x130: movq 0x40(%rax),%rax, guest dead in DDB. Unprivileged users cannot reach it on the stock guest (clone node 0600 root:wheel + SYSCAP_RESTRICTEDROOT; verified live: EACCES/EPERM for non-root), so the attacker is the privileged/compromised target daemon or an operator granted the node via devfs rules. Fix validated on a patched kernel: both write() and read() now return ENXIO with no panic.", "exploit_chain": "open(\"/dev/targ\", O_RDWR) -> write(fd, &ptr, 8) with ptr to any user CCB -> targwrite -> cam_periph_lock(NULL) -> read at NULL+0x40 -> Fatal trap 12 -> kernel panic (local DoS; no escalation potential from this primitive itself).", "evidence": "panic.txt (Fatal trap 12, fault va 0x40, Stopped at targwrite+0x130 movq 0x40(%rax),%rax); run.log (write-mode + unpriv EACCES); env.txt (uname, /dev/targ 0600 root:wheel); run.patched.log (write/read return ENXIO=6, guest alive).", "kernel_refs": [ "sys/bus/cam/scsi/scsi_target.c:566", "sys/bus/cam/scsi/scsi_target.c:588", "sys/bus/cam/scsi/scsi_target.c:602", "sys/bus/cam/scsi/scsi_target.c:874", "sys/bus/cam/scsi/scsi_target.c:880", "sys/bus/cam/scsi/scsi_target.c:522", "sys/bus/cam/cam_periph.h:186" ], "poc_changes": "No seed existed (pass-2 finding). PoC is minimal: open + 8-byte write of a pointer to a zeroed union ccb with func_code=XPT_NOOP; read-mode variant included.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Sat Oct 3 17:18:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline) / #3: Sat Oct 3 18:32:37 UTC 2026 (patched)", "runtime_sec": 300, "guest_dirty": 0, "build_cmd": "cd /root/targpwn && cc -O0 -g -o poc5374 poc5374.c", "run_cmd": "cd /root/targpwn && ./poc5374 w (as root; guest panics)", "code_hash": "5abba336a272f5bbb46068f8e2215baeee272abe441def70e532ab00b00c4e75", "notes": "Kernel built in-guest with option targ (conf/files optional-gated; conf/options needs a targ line for config(8) -- documented in env). Guest reset (with-src) after the run.", "recommended_fix": "Reject I/O on unenabled softc: in targwrite()/targread() return ENXIO when softc->periph == NULL or !(softc->state & TARG_STATE_LUN_ENABLED).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Baseline kernel: write()/read() before enable -> Fatal trap 12 in targwrite+0x130 (fault va 0x40), guest dies. Patched kernel (fix.diff state checks): write returned -1 errno=6 (ENXIO), read returned errno=6, no panic, guest healthy. Bad behavior gone.", "fix_evidence": "run.patched.log; panic.txt (baseline).", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #3: Sat Oct 3 18:32:37 UTC 2026 x86_64" } |