DF-4948 / verdict.json
{ "finding_id": "DF-4948", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED. smic_polled_request() stores the BMC-controlled reply byte count unclamped into req->ir_replylen (ipmi_smic.c:341) while the write into ir_reply[] is clamped (:325) - the exact DF-4777 shape in the SMIC backend, still unfixed in the tree (DF-4777s fix was validation-only). The producer half was proven live by compiling the VERBATIM ipmi_smic.c into the smicshock.ko harness against a scripted hostile-BMC SMIC register emulator: with a 16000-byte streamed reply it stores ir_replylen=16000 for ir_replybuflen=1024, returns success, and even logs Read short: 1024 buffer, 16000 actual. The shared consumer (ipmi.c:448-468, unmodified stock kernel) then copies out min(user_len, 16001) bytes from the fixed 1024-byte IPMI_MAX_RX chunk: 3/3 end-to-end runs disclose 6624-7167 nonzero kernel-heap bytes past offset 1024 (first foreign byte 0x042e in every run), including slab-linkage pointers (0xfffff8008dcfxxxx) and 392-424 fragments of live neighboring allocations, to an unprivileged operator-group process. Info leak only - no write primitive; uid=0 not reachable from this bug alone. fix.diff (SMIC clamp + ipmi.c sink clamp) validated on a rebuilt+reinstalled kernel: ir_replylen clamped to 1024, copyout capped at 1025 bytes, zero foreign bytes, IPMI device still functional.", "exploit_chain": "hostile/compromised BMC (or BMC-transport confusion) streams >1024 SMIC reply data bytes before RD_END -> smic_polled_request records ir_replylen=N>1024 (ipmi_smic.c:341) -> request completes normally -> any operator-group process issues IPMICTL_RECEIVE_MSG_TRUNC with msg.data_len=65535 -> ipmi.c:448-468 copyout reads N bytes from the 1024-byte M_IPMI chunk -> up to 64KB adjacent kernel heap (pointers + neighbor allocation contents) disclosed per request, repeatable.", "evidence": "run.log (producer BUG line + full sink run 1 with hexdump of kernel pointers at 0x042e+), run.2.log/run.3.log (runs 2-3: 7167/7160 foreign bytes), leak_sample.txt (pointer values + geometry), fix_run.log (patched kernel: clamped 1024 / 1025 / zero), fix_build.log, VERDICT.md narrative", "kernel_refs": [ "sys/dev/misc/ipmi/ipmi_smic.c:320-336", "sys/dev/misc/ipmi/ipmi_smic.c:341", "sys/dev/misc/ipmi/ipmi_smic.c:346-350", "sys/dev/misc/ipmi/ipmi.c:448-468", "sys/dev/misc/ipmi/ipmi.c:401-403", "sys/dev/misc/ipmi/ipmi.c:534-546", "sys/dev/misc/ipmi/ipmi.c:831-832", "sys/sys/ipmi.h:35", "sys/dev/misc/ipmi/ipmi_kcs.c:411-432" ], "poc_changes": "Authored new producer harness (smicshock.c + mock headers): no seed existed for the SMIC file. Emulator iteration: first build dropped the SMIC_CC_SMS_RD_END case (smic_read_end correctly rejected the bogus status); after adding it the transaction completes. bsd.kmod.mk resets CFLAGS (line 98) so -DLOCAL_MODULE/-Imock must be appended after the include; SRCS needs device_if.h/bus_if.h. Sink half reuses DF-4777s bumpko.c/leak.c verbatim.", "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) / #1: Fri Sep 25 08:44:00 UTC 2026 (fix-validation build)", "runtime_sec": 2400, "guest_dirty": 0, "build_cmd": "sh /root/smicshock/build.sh (cp /usr/src/sys/dev/misc/ipmi/ipmi_smic.c . ; make ; bumpko ; cc -O2 -o /tmp/leak leak.c)", "run_cmd": "kldload /root/smicshock/smicshock.ko ; [QEMU ipmi sim] kldload ipmi ; kldload bumpko.ko ; maxx: /tmp/leak & ; root: sysctl hw.bumpreplylen=16000 ; cat /tmp/leak.out", "code_hash": "d2315c87253332e75c9ce52f512f729cd9e2012d3293a5bbbfd1ef64c447d5f4", "notes": "Producer proof works on any boot (no IPMI hardware needed - mock transport). Sink proof requires QEMU -device ipmi-bmc-sim + isa-ipmi-kcs (SMIC is not emulated by QEMU; KCS device provides the real /dev/ipmi0 whose ioctl path is shared by all transports). Guest reset (with-src) performed after validation. DF-4777 fix.diff included an identical SMIC hunk but was never applied to sys/; both backends plus the ipmi.c sink remain unclamped upstream as of this audit.", "recommended_fix": "Clamp i to req->ir_replybuflen before req->ir_replylen = i at ipmi_smic.c:341 (and clamp len to ir_replybuflen+1 in ipmi.c RECEIVE as defense-in-depth; cf. DF-4777 for the KCS twin).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fixed - with fix.diff applied (ipmi_smic.c clamp + ipmi.c sink clamp), nativekernel rebuilt and rebooted, the producer stores ir_replylen=1024 (clamped, transaction succeeds) and the armed sink returns msg.data_len=1025 with zero bytes beyond the 1024-byte buffer; IPMI traffic over the KCS sim still round-trips on the patched kernel.", "fix_evidence": "fix_run.log (producer: clamped (ir_replylen <= ir_replybuflen) + req->ir_replylen = 1024; sink: RECEIVE ok: msg.data_len = 1025, nonzero in [1024,1024): 0, marker fragments: 0), fix_build.log (>>> Kernel install for X86_64_GENERIC completed on Fri Sep 25 08:48:48 UTC 2026), baseline in run.log/run.2.log/run.3.log", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Fri Sep 25 08:44:00 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC" } |