⬢ DragonFlyBSD Kernel Audit
DF-3260 / verdict.json
← back to finding ↓ download raw
{
 "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"
}