DF-3260 / verdict.json
{ "finding_id": "DF-3260", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Unvalidated ipfw rule act_ofs (copied verbatim at ip_fw2.c:4515, never checked in ipfw_check_ioc_rule) escalates DF-0261s OOB read into: (a) a deterministic packet-triggered kernel panic (reproduced twice: panic: -- unknown opcode 127 from ipfw_chk via the O_PROBE_STATE ACTION_PTR jump, trace captured), (b) a netisr infinite-loop hang when the misaligned word has F_LEN==0, and (c) a 4-byte kernel heap write with attacker-chosen offset (act_ofs*4+8, up to +256KB) and attacker-influenced value, demonstrated deterministically via clear_counters()/IP_FW_ZERO (cmd[3] 0x0000012b -> 0x00000128 observed byte-exact through IP_FW_GET) and additionally reachable packet-triggered via ipfw_log()s log_left-- when fw_verbose=1. Root-gated setup (SYSCAP_NONET_RAW, securelevel>=3 blocks), unprivileged/remote trigger after install, so no uid0 chain exists; impact is kernel integrity + DoS. fix.diff (act_ofs < cmd_len and on an instruction boundary) validated on the guest: both PoCs rejected with EINVAL, valid rules and traffic unaffected.", "exploit_chain": "root: setsockopt(IP_FW_ADD) rule with crafted act_ofs -> (panic route) second packet on the keep-state flow makes O_PROBE_STATE jump to ACTION_PTR=operand word -> opcode dispatch on operand byte 0x7f -> panic(-- unknown opcode 127); (write route) setsockopt(IP_FW_ZERO, rulenum) -> clear_counters() -> 4-byte store at rule->cmd + act_ofs*4 + 8, value from act_ofs*4+4, offset/value attacker-chosen within +256KB of the M_IPFW allocation; (packet-triggered write route) rule containing O_LOG + fw_verbose=1 -> ipfw_log() decrements 4 bytes at the same OOB address on every matching packet from any user.", "evidence": [ "panic.txt: full serial-console panic unknown opcode 127 with ipfw_chk/ipfw_check_in/pfil_run_hooks/ip_input trace", "run.log: PoC2 byte-exact proof - baseline cmd[3]=0x0000012b, after IP_FW_ZERO cmd[3]=0x00000128 (attacker-chosen max_log), EXIT=0", "run.2.log: repeat run, mutation persists in kernel rule object", "VERDICT.md: sink-by-sink line-cited analysis + fix validation transcript", "fix.diff: act_ofs bound + instruction-boundary validation in ipfw_check_ioc_rule", "build_fix.log: patched module built clean with -Werror" ], "kernel_refs": [ "sys/net/ipfw/ip_fw2.c:4515", "sys/net/ipfw/ip_fw2.c:5310", "sys/net/ipfw/ip_fw2.c:5204", "sys/net/ipfw/ip_fw2.c:1408", "sys/net/ipfw/ip_fw2.c:4204", "sys/net/ipfw/ip_fw2.c:4368", "sys/net/ipfw/ip_fw2.c:3616", "sys/net/ipfw/ip_fw2.c:4425" ], "poc_changes": "seedless (new finding); three iterations: v1 spray-based cross-object write wedged the guest twice (heap metadata corruption, unlogged), v2 restricted candidates still wedged, v3 self-referential in-allocation proof (act_ofs=1 at max_log word) made the store deterministic and byte-exact observable via IP_FW_GET; separate panic PoC made deterministic by aliasing 127.0.255.255 on lo0 so the O_IP_SRC operand low byte equals opcode 127.", "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": 2400, "guest_dirty": 1, "build_cmd": "cc -O -o poc_misalign poc_misalign.c && cc -O -o poc_write poc_write.c", "run_cmd": "sysctl net.filters_default_to_accept=1; kldload ipfw; ./poc_misalign # panic on packet 2; then fresh boot: ./poc_write", "code_hash": "c3dc0a6eb9853573359ab7344b5346fed1f1819a520b963ad1b5c7dec9d7c33d (poc_misalign.c), 68ca5aa8abdb909a758b12cc2b42210288cb927ddc04bc7538f33f42ff1a8d1b (poc_write.c)", "notes": "Evidence pack uses reserved DF-3260. Cross-object OOB landing (act_ofs > cmd_len) proven by code (same single store, offset unbounded to 65535) plus two guest-wide hard wedges exactly at IP_FW_ZERO of sprayed weapon rules (attempts logged in VERDICT.md); no clean log survives a wedge, so the deterministic in-allocation write + reproduced panic carry the verdict. Related: DF-0261 (pass-1 OOB read, same root cause - this finding adds write + panic + loop sinks and the fix).", "recommended_fix": "In ipfw_check_ioc_rule(), reject rules whose act_ofs >= cmd_len or whose act_ofs does not land exactly on an instruction boundary of the validated command chain (see fix.diff).", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 (stock kernel; fixed code validated as rebuilt ipfw.ko module: cd /usr/src/sys/net/ipfw && make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC, loaded via kldload)", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "With fix.diff applied to the guest /usr/src and the rebuilt ipfw.ko loaded, poc_misalign and poc_write are both rejected at IP_FW_ADD with EINVAL (dmesg: ipfw: act_ofs N not on instruction boundary); the panic and the heap write are gone. poc_regression confirms valid rules (act_ofs on the action boundary and on an O_LOG boundary) still install, state-rule traffic flows, and IP_FW_GET works - no functional regression.", "fix_evidence": "run.log (fix validation section), build_fix.log" } |