β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0475

act_ofs never validated: OOB pointer deref via ACTION_PTR during packet matching

Summary

act_ofs copied verbatim from user input(add_rule_dispatch:651) no check act_ofs<=cmd_len. ACTION_PTR(ip_fw3.h:134)=(uint32_t*)cmd+act_ofs points past cmd array when act_ofs>=cmd_len. OOB deref at: ip_fw3_chk CHK_STATE(:522-524) reads cmd->module/opcode from OOB bypassing l>0 guard -> filter_funcs OOB call; lookup_next_rule(:294-296) reads OOB; ip_fw3_dummynet_io(:608-611) reads OOB opcode. Root crafts rule, remote traffic triggers OOB heap read + func call. Fix: validate act_ofs<cmd_len in add_rule_dispatch.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0475 Β· 10 files
FileTypeDescriptionSize
df0475_oob.c trigger-source installs OOB act_ofs=99 rule (proves missing validation) 5.1 KB view raw
df0475_trigger.c exploit-chain full state-path trigger: keep_state+check_state+flood -> OOB panic 4.4 KB view raw
fix.diff suggested-fix validate act_ofs<=cmd_len in add_rule_dispatch 966 B view raw
build.sh build-log build script 120 B view raw
run.sh run-log run script 342 B view raw
env.txt environment guest environment 429 B view raw
VERDICT.md verdict full narrative with fix validation 3.5 KB ↓ raw
README.md readme human reproduce doc 467 B ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human reproduce doc
↓ download raw

DF-0475 β€” PoC evidence pack

See VERDICT.md for the full analysis.

Files

  • trigger source β€” the PoC program(s)
  • build.sh β€” exact build command
  • run.sh β€” exact run command
  • fix.diff β€” git-apply-able fix for the verified bug
  • VERDICT.md β€” full narrative: mechanism, evidence, fix validation
  • manifest.json β€” machine-readable catalog
  • env.txt β€” guest environment

Quick reproduce

./build.sh && ./run.sh
VERDICT.md verdict full narrative with fix validation
↓ download raw

DF-0475 β€” ipfw3 act_ofs never validated β†’ OOB pointer deref

Verdict

REPRODUCED β€” confirmed live via OOB rule acceptance (missing validation) AND OOB kernel panic via the CHK_STATE path. Fix validated.

Mechanism

add_rule_dispatch (sys/net/ipfw3/ip_fw3.c:651) copies rule->act_ofs = ioc_rule->act_ofs; verbatim from user input with no check that act_ofs <= cmd_len. ACTION_PTR(rule) (ip_fw3.h:134-135) computes (ipfw_insn *)((uint32_t *)rule->cmd + rule->act_ofs), so when act_ofs >= cmd_len it points past the cmd[] array into adjacent kernel heap.

The OOB pointer is dereferenced in three places: 1. ip_fw3_chk CHK_STATE case (ip_fw3.c:520-524): cmd = ACTION_PTR(f); l = f->cmd_len - f->act_ofs; goto check_body; β€” reads OOB cmd->module/cmd->opcode and calls filter_funcs[OOB_module][OOB_opcode] (line 506) β†’ arbitrary/NULL function-pointer call. 2. lookup_next_rule (ip_fw3.c:294-296) β€” on dummynet reinject. 3. ip_fw3_dummynet_io (ip_fw3.c:608-611) β€” on dummynet pipe delivery.

Live reproduction (unfixed #0 kernel)

  1. Missing validation confirmed: setsockopt(IP_FW_X, IP_FW_ADD) with act_ofs=99, cmd_len=1 succeeds β€” the rule is installed. On a FIXED kernel this is rejected.
  2. OOB panic via CHK_STATE: install a keep_state rule (creates states with ->stub pointing at the rule) with OOB act_ofs=99, flood UDP to create states on all CPUs, install a check_state rule, flood again. When check_check_state finds a state, it returns IP_FW_CTL_CHK_STATE; ip_fw3_chk at :520-524 does cmd = ACTION_PTR(f) = cmd + 99 = OOB heap, reads garbage module/opcode, calls filter_funcs[garbage] β†’ kernel panic: Fatal trap 9: general protection fault while in kernel mode kernel: type 9 trap, code=0 Stopped at ip_fw3_chk+0x1a4: ret (dfbsd-qemu/boot.log)

Evidence

  • df0475_oob.c β€” installs the OOB rule (act_ofs=99, cmd_len=1); prints "[+] installed buggy rule 200 (act_ofs=99, cmd_len=1) -- OOB accepted".
  • df0475_trigger.c β€” the full state-path trigger (keep_state + check_state + flood) that causes the panic.
  • panic.txt β€” the Fatal trap 9 ... Stopped at ip_fw3_chk+0x1a4: ret excerpt.

Threat model

Root installs a crafted rule. Any subsequent traffic that triggers CHK_STATE (or dummynet reinject) dereferences the OOB pointer. On default GENERIC (INVARIANTS ON) this panics; on a kernel without INVARIANTS, the OOB function-pointer call could potentially be shaped via heap grooming into an arbitrary call (escalation primitive β€” not pursued here as it requires bypassing INVARIANTS).

Fix

fix.diff β€” add if (ioc_rule->act_ofs > ioc_rule->cmd_len) { kprintf(...); netisr_forwardmsg_all(...); return; } in add_rule_dispatch. Validated: built fixed ipfw3.ko, loaded it, ran df0475_oob β€” dmesg shows "ipfw3: refusing rule 200: act_ofs 99 > cmd_len 1" (printed once per CPU); the rule is refused.

Fix validation

  • Before: act_ofs=99, cmd_len=1 rule accepted; OOB panic via state path.
  • After: "ipfw3: refusing rule 200: act_ofs 99 > cmd_len 1" (Γ—6 CPUs); rule NOT installed; no OOB possible.

Build / Run

cc -o df0475_oob df0475_oob.c         # proves missing validation (rule accepted)
cc -o df0475_trigger df0475_trigger.c # full OOB panic trigger via state path
# sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload ipfw3_basic
# ./df0475_oob        # UNFIXED: "OOB accepted"; FIXED: rule refused
# ./df0475_trigger     # UNFIXED: kernel panic (trap 9 at ip_fw3_chk+0x1a4)

Fix verification

fixed

validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED (live panic). ip_fw3 act_ofs>cmd_len OOB ACTION_PTR -> filter_funcs OOB call -> trap 9. Module fix validated.