DF-5375 / verdict.json
{ "finding_id": "DF-5375", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED: targwrite() has no func_code whitelist -- ANY 32-bit opcode from the daemons CCB is forwarded to xpt_action(). A write(2) of a CCB with func_code=XPT_SCSI_IO (0x901, XPT_FC_DEV_QUEUED) executes as an initiator I/O and its completion lands in targdone() whose default case panics: guest shows panic: targdone: impossible xpt opcode 0x901 with backtrace targdone <- camisr_runqueue <- swi_cambio. Deterministic (SWI fires within milliseconds), repeatable, guest dead. Same missing whitelist also forwards opcodes carrying raw kernel pointers (SCSI_IO CAM_CDB_POINTER cdb_io.cdb_ptr, data_ptr; targsendccb maps only CTIO/DEV_MATCH at scsi_target.c:783-799) into SIMs -- CWE-822; upstream FreeBSDs rewritten scsi_target.c restricts targwrite to target-mode opcodes for exactly this reason. Root-gated on the stock guest (0600 root:wheel + SYSCAP_RESTRICTEDROOT), so impact = privileged-daemon-triggered kernel panic/DoS + pointer-trust surface, not unpriv escalation. Fix validated: patched kernel returns EINVAL (errno 22) from write(), no panic.", "exploit_chain": "open /dev/targ -> TARGIOCENABLE(targsim bus,0,0) -> write(fd, &ccb_ptr, 8) where user ccb has func_code=0x901 -> queued path targstart -> targusermerge copyin of ccb_scsiio -> targsendccb -> xpt_action(XPT_SCSI_IO) -> SIM completes -> xpt_done -> SWI camisr_runqueue -> targdone default case -> panic(\"targdone: impossible xpt opcode 0x901\").", "evidence": "panic.txt (panic: targdone: impossible xpt opcode 0x901; trace targdone/camisr_runqueue/swi_cambio); run.log (EN_LUN OK, SCSI_IO via targ, panic); targsim.c (target-capable SIM rig: PIT_PROCESSOR); run.patched.log (errno=22 EINVAL, no panic, guest alive).", "kernel_refs": [ "sys/bus/cam/scsi/scsi_target.c:584", "sys/bus/cam/scsi/scsi_target.c:603", "sys/bus/cam/scsi/scsi_target.c:837", "sys/bus/cam/scsi/scsi_target.c:847", "sys/bus/cam/scsi/scsi_target.c:783", "sys/bus/cam/cam_ccb.h:117" ], "poc_changes": "No seed existed. Rig (targsim.c) extended from the DF-5240 evilsim pattern: target_sprt=PIT_PROCESSOR, XPT_EN_LUN accepted, forwarded CCBs detected via ccb_h.targ_descr (== periph_priv.entries[1].ptr, a scsi_target.c-local macro), SCSI_IO with targ_descr completed CAM_REQ_CMP. Three -Werror bring-up fixes (targ_descr macro, cam_periph.h include, drop unused statics).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Sat Oct 3 17:18:54 UTC 2026 x86_64 (baseline) / #3: Sat Oct 3 18:32:37 UTC 2026 (patched)", "runtime_sec": 900, "guest_dirty": 0, "build_cmd": "cd /root/targpwn && make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC && cc -O0 -g -o poc5375 poc5375.c", "run_cmd": "cd /root/targpwn && kldload ./targsim.ko && sleep 2 && ./poc5375 (as root; guest panics)", "code_hash": "a276f662fef1b0d45130bf66fa805de63772c792b2e54ed3b5291bdc841ec342", "notes": "Rig choreography verified live: TARGIOCENABLE succeeds even on a device whose da driver failed to attach (CAM EDT entry suffices for path creation).", "recommended_fix": "Whitelist func codes in targwrite() (XPT_ACCEPT_TARGET_IO/XPT_IMMED_NOTIFY/XPT_CONT_TARGET_IO/XPT_ABORT only) and reject everything else with EINVAL, matching upstream FreeBSDs rewrite.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Baseline: panic targdone: impossible xpt opcode 0x901 (trace targdone <- camisr_runqueue <- swi_cambio), guest dead. Patched (whitelist): write() returns -1 errno=22 (EINVAL), no CCB forwarded, no panic, guest alive after 3s. Bad behavior gone.", "fix_evidence": "panic.txt + run.log (baseline); run.patched.log (EINVAL, alive).", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #3: Sat Oct 3 18:32:37 UTC 2026 x86_64" } |