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)
PoC verification
Evidence pack
findings/poc/DF-0475 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
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)
- Missing validation confirmed:
setsockopt(IP_FW_X, IP_FW_ADD)withact_ofs=99, cmd_len=1succeeds β the rule is installed. On a FIXED kernel this is rejected. - OOB panic via CHK_STATE: install a keep_state rule (creates states with
->stubpointing at the rule) with OOBact_ofs=99, flood UDP to create states on all CPUs, install a check_state rule, flood again. Whencheck_check_statefinds a state, it returnsIP_FW_CTL_CHK_STATE;ip_fw3_chkat :520-524 doescmd = ACTION_PTR(f)=cmd + 99= OOB heap, reads garbagemodule/opcode, callsfilter_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β theFatal trap 9 ... Stopped at ip_fw3_chk+0x1a4: retexcerpt.
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=1rule 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
fixedvalidated
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.
No comments yet.