DF-0632 / df0632_test.sh
#!/bin/sh # DF-0632 — ipfw3 per-CPU state counters are monotonic (incremented on # state creation but never decremented on expiry/flush). # # Demonstrates that after `state_max_icmp_out` lifetime creations, the # firewall permanently stops creating new states for that proto/direction. # # REQUIRES: # - ipfw3.ko + ipfw3_basic.ko kernel modules (loadable; root) # - The DF0632-probe.ko instrumentation module built from # ipfw3_counter_probe.c (to read kernel counters via kldload) # - Run as root (firewall administration is privileged). # # RUN: sh df0632_test.sh # EXPECTED on vulnerable kernel: # - icmp_out counter goes 0 -> 6 after round 1 (5 is max+1) # - icmp_out counter STAYS at 6 after timeout/expiry # - Subsequent pings CANNOT create state; counter pinned forever set +e OUT=/tmp/df0632_result.txt PROBE_KO=${PROBE_KO:-/usr/src/sys/modules/df0632_probe/df0632_probe.ko} # kldload ipfw3 disables network by default-deny; we run as nohup so SSH # drops but the script keeps running and writes results to $OUT. { echo "=== DF-0632 ipfw3 state counter monotonic-increment test ===" date # Modules must already be loaded kldstat | head sysctl -w net.inet.ip.fw3.enable=1 /sbin/ipfw3 -f flush /sbin/ipfw3 add 100 allow tcp from any to any # keep SSH alive /sbin/ipfw3 add 200 allow icmp from any to any keep-state /sbin/ipfw3 list # Configure small max for fast exhaustion sysctl -w net.inet.ip.fw3_basic.state_max_icmp_out=5 sysctl -w net.inet.ip.fw3_basic.icmp_timeout=3 sysctl -w net.inet.ip.fw3_basic.cleanup_interval=1 echo "--- Initial counter (should be 0) ---" kldload "$PROBE_KO"; dmesg | tail -8 | grep "DF0632 cpu0"; kldunload df0632_probe echo "--- Round 1: send 6 ICMP packets with unique dst IPs ---" for i in 1 2 3 4 5 6; do ping -c 1 -t 1 -W 5 10.0.2.$((i+100)) >/dev/null 2>&1 done sleep 1 echo "--- Counter after round 1 (expect icmp_out=6: 5 max + 1 over) ---" kldload "$PROBE_KO"; dmesg | tail -8 | grep "DF0632 cpu0"; kldunload df0632_probe echo "--- Wait for state expiry (timeout=3, cleanup=1) ---" sleep 6 echo "--- Counter AFTER expiry (BUG: counter still 6, NOT decremented) ---" kldload "$PROBE_KO"; dmesg | tail -8 | grep "DF0632 cpu0"; kldunload df0632_probe echo "--- Round 2: send 6 MORE pings ---" for i in 11 12 13 14 15 16; do ping -c 1 -t 1 -W 5 10.0.2.$((i+100)) >/dev/null 2>&1 done sleep 1 echo "--- Counter after round 2 (BUG: should still be 6; counter > max so no new states) ---" kldload "$PROBE_KO"; dmesg | tail -8 | grep "DF0632 cpu0"; kldunload df0632_probe echo "--- Wait for second expiry ---" sleep 6 echo "--- Counter after second expiry (BUG: cumulative, never decremented) ---" kldload "$PROBE_KO"; dmesg | tail -8 | grep "DF0632 cpu0"; kldunload df0632_probe echo "--- Round 3: send 5 more pings (counter is pinned) ---" for i in 21 22 23 24 25; do ping -c 1 -t 1 -W 5 10.0.2.$((i+100)) >/dev/null 2>&1 done sleep 1 echo "--- Final counter (BUG: counter still pinned; no new states can ever be created) ---" kldload "$PROBE_KO"; dmesg | tail -8 | grep "DF0632 cpu0"; kldunload df0632_probe /sbin/ipfw3 show sysctl -w net.inet.ip.fw3.enable=0 date echo "=== Done ===" } > "$OUT" 2>&1 echo "Result written to $OUT" |