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)
PoC verification
Evidence pack
findings/poc/DF-0476 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
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)
- Built a tiny KLD module
df0476_mod.kothat callsip_fw3_register_module(7, "AAAAAAAAAAAAAAAAAAAA1234567890123456789012")(42 chars). sysctl net.filters_default_to_accept=1; kldload ipfw3; kldload df0476_mod.- The overflow propagated to userspace: the
ipfw3 showCLI (whichdlopenslibipfw3<name>.sofor each registered module) tried to open:/usr/lib/libipfw3AAAAAAAAAAAAAAAAAAAA1234567890123456789012.soβ the full 42-char overflowed name, provingname[]was not NUL-terminated andstrcatread 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 pastname[20]). - After: CLI tried
libipfw3AAAAAAAAAAAAAAAAAAA.so(19 chars β truncated bystrlcpy).
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
fixedvalidated
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.
No comments yet.