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

ip_fw3_register_module: strncpy bounded by strlen(src) not sizeof(dst): buffer overflow + missing NUL

Summary

ip_fw3_register_module(:180): strncpy(tmp->name, module_name, strlen(module_name)). fw3_modules[].name is char[20](ip_fw3.h:496). Uses SOURCE length not DEST size. module_name>=20 chars overflows name[20] into adjacent fw3_modules slot type/id or past array into fw3_sync_ctx. strncpy with strlen(src) never appends NUL. Stale bytes on slot reuse propagate to strcat in ip_fw3_ctl_get_modules(:982) reading past name[20] until zero -> module_str[1024] overflow or global data leak via IP_FW_MODULE. Fix: strlcpy(tmp->name, module_name, sizeof(tmp->name)).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0476 Β· 9 files
FileTypeDescriptionSize
df0476_read.c trigger-source userspace helper to read IP_FW_MODULE list 3.3 KB view raw
fix.diff suggested-fix strlcpy instead of strncpy+strlen 575 B view raw
build.sh build-log build script 116 B view raw
run.sh run-log run script 379 B view raw
env.txt environment guest environment 429 B view raw
VERDICT.md verdict full narrative with fix validation 3.4 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-0476 β€” 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-0476 β€” ipfw3 register_module strncpy bounded by strlen not sizeof

Verdict

REPRODUCED β€” confirmed live via KLD module load with a 40-char name. Fix validated.

Mechanism

ip_fw3_register_module (sys/net/ipfw3/ip_fw3.c:170-186) registers a module in the global fw3_modules[] array. The slot name copy is:

strncpy(tmp->name, module_name, strlen(module_name));

tmp->name is char name[20] (sys/net/ipfw3/ip_fw3.h:493-497). The strncpy length argument is strlen(module_name) β€” the source length, not sizeof(tmp->name). If module_name >= 20 chars: - The write overflows name[20] into the adjacent fw3_modules slot's type/id fields (or past the array into fw3_sync_ctx). - strncpy with strlen(src) never appends a NUL terminator, so name is left un-terminated.

On a subsequent IP_FW_MODULE getsockopt (ip_fw3_ctl_get_modules, ip_fw3.c:972-989), strcat(module_str, mod->name) reads mod->name past the 20-byte boundary until it hits a zero byte β€” leaking adjacent kernel memory into the returned module string, or overflowing the 1024-byte module_str buffer.

Live reproduction (unfixed #0 kernel)

  1. Built a tiny KLD module df0476_mod.ko that calls ip_fw3_register_module(7, "AAAAAAAAAAAAAAAAAAAA1234567890123456789012") (42 chars).
  2. sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload df0476_mod.
  3. The overflow propagated to userspace: the ipfw3 show CLI (which dlopens libipfw3<name>.so for each registered module) tried to open: /usr/lib/libipfw3AAAAAAAAAAAAAAAAAAAA1234567890123456789012.so β€” the full 42-char overflowed name, proving name[] was not NUL-terminated and strcat read past the 20-byte boundary. dmesg confirms "ipfw3 module AAAAAAAAAAAAAAAAAAAA1234567890123456789012 loaded".

Evidence

  • df0476_mod.c + Makefile β€” the KLD module that triggers the overflow.
  • df0476_read.c β€” userspace helper to read back the module list.
  • run.log β€” the dmesg + CLI output showing the overflowed name propagation.

Threat model

Root loads a KLD module whose init_module calls ip_fw3_register_module with a name β‰₯ 20 chars. The overflow corrupts adjacent global data. The un-terminated name[] then leaks adjacent kernel memory to any process that can issue IP_FW_MODULE getsockopt (root via raw socket). Impact: global data corruption + info leak. (A buggy or malicious KLD module is the trigger; this is a root-configurable path, not unprivileged.)

Fix

fix.diff β€” replace strncpy(tmp->name, module_name, strlen(module_name)) with strlcpy(tmp->name, module_name, sizeof(tmp->name)). strlcpy is bounded by the destination size and always NUL-terminates. Validated: built fixed ipfw3.ko, loaded df0476_mod β€” the CLI now tries libipfw3AAAAAAAAAAAAAAAAAAA.so (19 chars, truncated by strlcpy to sizeof(name)-1 = 19), proving the overflow is prevented.

Fix validation

  • Before: CLI tried libipfw3AAAAAAAAAAAAAAAAAAAA1234567890123456789012.so (42 chars β€” overflowed past name[20]).
  • After: CLI tried libipfw3AAAAAAAAAAAAAAAAAAA.so (19 chars β€” truncated by strlcpy).

Build / Run

cd df0476_mod && make                # build the KLD module
cc -o df0476_read df0476_read.c      # build the reader
# sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload ./df0476_mod/df0476_mod.ko
# dmesg | tail ; ipfw3 show 2>&1 | grep libipfw3

Fix verification

fixed

validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED (live). ip_fw3_register_module strncpy strlen not sizeof -> 22B overflow + unterm name leak. Module fix: strlcpy.