DF-4978 / verdict.json
{ "finding_id": "DF-4978", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED and FIX VALIDATED. The verbatim SSIF transport (ipmi_ssif.c compiled unmodified into the harness) stores the BMC-accumulated multi-part reply total unclamped into req->ir_replylen (ipmi_ssif.c:304, accumulation at :294, single-part :240) -- a protocol-legal hostile BMC sending >33 full 32-byte continuation blocks yields ir_replylen=16023 over the fixed 1024-byte ioctl reply buffer. The shared stock-kernel sink (ipmi.c:448-468) then copyouts 16023 bytes from that buffer: 3/3 end-to-end runs on a real /dev/ipmi0 disclosed 7159/7183/7190 nonzero kernel-heap bytes past offset 1024 (kernel slab pointers at ir_reply[1070]+, 426 recognizable neighbor-chunk markers) to an unprivileged operator-group user; control run with a legal length leaked nothing. Two-line clamp fix (transport + sink) validated on a rebuilt kernel: producer clamps to 1024, sink returns 1025 with zero bytes beyond, IPMI device fully functional. Third transport carrying the DF-4777 (kcs) / DF-4948 (smic) shape.", "exploit_chain": "hostile/compromised BMC (or SMBus device at the BMC slave address) on an SSIF interface -> multi-part read with 515 legal 32-byte 0xff continuation blocks + numbered terminator -> ir_replylen=16023 on a completed request -> unprivileged operator-group user issues IPMICTL_RECEIVE_MSG(_TRUNC) with a large data_len -> ipmi.c copyout reads 14999 bytes past the 1024-byte ir_reply heap chunk -> kernel heap disclosure (slab linkage pointers -> kernel-address info; neighboring allocation contents). Info-leak primitive only: no write, no uid=0 from this bug alone.", "evidence": "run.log (producer: BUG DF-4978 REPRODUCED, ir_replylen=16023/1024); run.2.log + run.3.log + run.log (sink 3/3: data_len=16024, 7159/7183/7190 nonzero past 1024, first 0x042e, 426 markers); run.control.log (legal arm: 0 bytes beyond); leak_sample.txt (0xfffff8008dcfxxxx kernel pointers at ir_reply[1070]+); fix_run.log (patched kernel: ir_replylen=1024 clamped, data_len=1025, 0 beyond, ioctls functional); fix_build.log (37637-line validated kernel build); VERDICT.md for the full narrative incl. the inverted 0xff break condition at ipmi_ssif.c:297.", "kernel_refs": [ "sys/dev/misc/ipmi/ipmi_ssif.c:304", "sys/dev/misc/ipmi/ipmi_ssif.c:294", "sys/dev/misc/ipmi/ipmi_ssif.c:240", "sys/dev/misc/ipmi/ipmi_ssif.c:291-293", "sys/dev/misc/ipmi/ipmi.c:448-468", "sys/dev/misc/ipmi/ipmi.c:401-403" ], "poc_changes": "Seed sketch (none existed -- new pass-2 finding) was authored from scratch modeled on the validated DF-4777/DF-4948 methodology: (1) mock headers live in the module dir because bsd.kmod.mk puts -I. ahead of -Idragonfly, shadowing the real bus/smbus/smbconf.h; (2) first producer run returned ir_replylen=58 because DragonFlys multi-part break condition is INVERTED (breaks on non-0xff at ipmi_ssif.c:297, unlike FreeBSD) -- the hostile script was corrected to send 0xff-marked continuation blocks terminated by one numbered block; (3) bumpko.c/leak.c reused verbatim from DF-4777; (4) control run initially raced a stale sysctl arm value -- redone with explicit disarm first.", "attempts": 3, "guest_uname": "DragonFly dfbsd 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 runs); fix validation on #1: Fri Sep 25 14:44:16 UTC 2026 same tree + fix.diff", "runtime_sec": 1620, "guest_dirty": 0, "build_cmd": "vm.sh run_root sh /root/ssifshock/build.sh (compiles verbatim /usr/src ipmi_ssif.c + bumpko.ko + /tmp/leak)", "run_cmd": "vm.sh run_root kldload /root/ssifshock/ssifshock.ko (producer); QEMU_EXTRA=-device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-kcs,bmc=bmc0 boot + kldload ipmi + kldload bumpko.ko + unpriv /tmp/leak + sysctl hw.bumpreplylen=16023 (sink)", "code_hash": "def4e8bfd3fa98a5ab8ea3d5233d9143ae3b0cf7b514f3fca2eb8ac78cbe2ec0", "notes": "Per-block counts all exactly 32 = fully SMBus-legal, distinguishing this from DF-2025 (count>32 stack bcopy over-read) and DF-2026 (unterminated loop) -- pass-2 scope was to answer does SSIF also store an unclamped replylen?: YES. Bonus discovery documented in VERDICT.md: the :297 break condition is inverted vs the SSIF spec/FreeBSD (numbered block terminates, 0xff continues), which also silently truncates compliant multi-part replies; functional bug, not filed separately. size_t->int truncation of len at :304 (negative ir_replylen after ~2^32 accumulation, chained on DF-2026s uncapped loop) is neutralized by the clamp fix.", "recommended_fix": "Clamp the stored total to the reply buffer at ipmi_ssif.c:304 and clamp again in the shared ipmi.c sink (len = min(len, ir_replybuflen + 1)) -- exactly fix.diff, live-validated on a rebuilt kernel.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (transport clamp at ipmi_ssif.c:304 + sink clamp at ipmi.c:448) applied to the guest /usr/src, kernel rebuilt (make -j6 nativekernel KERNCONF=X86_64_GENERIC, 37637-line fix_build.log) and booted: producer now reports ir_replylen=1024 (clamped) for the identical 515-block hostile script, and the armed 16023 end-state on the real /dev/ipmi0 yields msg.data_len=1025 with ZERO nonzero bytes beyond 1024 and zero marker fragments; all IPMI ioctls still succeed (device functional). Baseline vs patched delta is conclusive.", "fix_evidence": "fix_run.log (patched-kernel producer + sink output), fix_build.log (full kernel rebuild), VERDICT.md Fix validation section with baseline-vs-patched table" } |