Network & capture · Router VM
Router VM (Kali)
A dedicated Kali Linux VM acts as the gateway for the analysis lab. Static 10.0.100.1/24 on the LAN side, iptables NAT outbound, DHCP for the bus, and a pre-installed tap pipeline that mirrors every frame to the host capture.
On this page
Why a router VM (and not the host)
Most VM apps NAT through the host's network stack. SecVF can do that too (it's the default "NAT" mode). But for analysis, routing through a dedicated guest is strictly better:
- Tooling parity. Run
tcpdump,tshark, ornmapon the router and you see exactly what malware sees on the wire. Host-side tools can't. - iptables / netfilter access. Full Linux firewall stack — DNAT, mangle, conntrack — without touching the host's networking.
- Containment. If malware exploits a network stack bug, the explosion is in the router VM, not on macOS. Restore the snapshot, move on.
- Reproducibility. The router's state — config, certs, captures — is in
~/.avf/Linux/Kali-Router.bundle/. Snapshot it, share it, version it. - L7 instrumentation. Easy to wedge a transparent proxy (mitmproxy, sslsplit) inline. Can't do that with host NAT.
Topology
┌─────────────────────────────────────────────────────────────┐
│ Host (macOS) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SecVF process │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ VirtualNetworkSwitch (in-process L2/L3) │ │ │
│ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌─────────┐ │ │ │
│ │ │ │ VM A │ │ VM B │ │ VM C │ │ Kali │ │ │ │
│ │ │ │ │ │ │ │ │ │ router │ │ │ │
│ │ │ └──┬───┘ └──┬───┘ └──┬───┘ └────┬────┘ │ │ │
│ │ └─────┼─────────┼─────────┼───────────┼───────┘ │ │
│ │ └─────────┴─────────┘ │ │ │
│ │ LAN: 10.0.100.0/24 │ WAN │ │
│ │ (no host involvement) │ via │ │
│ │ │ framework│ │
│ │ │ NAT │ │
│ └─────────────────────────────────────────┼───────────┘ │
│ ▼ │
└─────────────────────────────────────────────────────────────┘
Internet (or FakeNet)
The router has two virtual NICs:
- eth0 (LAN) — attached to the virtual switch with static
10.0.100.1/24. Other VMs use this as their gateway. - eth1 (WAN) — attached to the framework's NAT, gets DHCP from macOS. This is the only path out.
Provisioning the router
Start with a clean Kali install (see Your first VM), then attach the Scripts USB:
- In the VM Library, right-click the Kali VM → Mount Scripts USB… SecVF creates an HFS+ disk image with
kali-router-setup.shon it and attaches it as a USB device. - Inside the guest, mount and run:
sudo mount /dev/sdb1 /mnt cd /mnt sudo bash kali-router-setup.sh - Reboot when the script finishes (
sudo reboot). The router is now configured.
The script (~250 lines, source at scripts/kali-router-setup.sh) does:
- Sets
eth0static10.0.100.1/24via/etc/network/interfaces.d/eth0 - Enables IP forwarding (
net.ipv4.ip_forward = 1in/etc/sysctl.d/99-secvf.conf) - Installs and configures
isc-dhcp-serveron eth0 - Installs and configures
dnsmasqon eth0 for DNS - Installs analysis tooling:
tcpdump,tshark,nmap,tcpflow,ngrep,bettercap - Adds the helper commands (
secvf-status,secvf-monitor,secvf-capture) to/usr/local/bin/ - Writes iptables rules to
/etc/iptables/rules.v4and enablesnetfilter-persistent - Disables Kali's default services that aren't needed (cups, avahi, etc.)
Network configuration
Subnet layout
| Range | Use |
|---|---|
10.0.100.1 | Router (eth0). Static. |
10.0.100.10 – 10.0.100.99 | DHCP pool. Most lab VMs land here. |
10.0.100.100 – 10.0.100.199 | Reserved for static assignments (mitmproxy hosts, FakeNet endpoints). |
10.0.100.200 – 10.0.100.254 | Free-form; use for ad-hoc IPs. |
DHCP server (isc-dhcp-server)
Config at /etc/dhcp/dhcpd.conf:
option domain-name "secvf.lab";
option domain-name-servers 10.0.100.1;
default-lease-time 600;
max-lease-time 7200;
authoritative;
subnet 10.0.100.0 netmask 255.255.255.0 {
range 10.0.100.10 10.0.100.99;
option routers 10.0.100.1;
option broadcast-address 10.0.100.255;
}
DNS resolver (dnsmasq)
Default config forwards upstream to 1.1.1.1 and 9.9.9.9. Override with FakeNet by replacing /etc/dnsmasq.conf with the FakeNet variant — see FakeNet.
Firewall & NAT rules
The router's /etc/iptables/rules.v4:
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i eth0 -p icmp -j ACCEPT
-A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT # SSH from LAN
-A INPUT -i eth0 -p udp --dport 53 -j ACCEPT # DNS
-A INPUT -i eth0 -p udp --dport 67 -j ACCEPT # DHCP
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o eth1 -j MASQUERADE # NAT out via WAN
COMMIT
Reload with sudo iptables-restore < /etc/iptables/rules.v4 or just sudo systemctl restart netfilter-persistent.
eth0 → eth1 (so guests can reach the internet) but drop eth1 → eth0 (so the outside can never initiate to lab guests).
Helper commands
Three commands ship with the router setup. All live in /usr/local/bin/ and run as root via sudoers entry.
secvf-status
One-shot summary of the router state. Useful first-thing diagnostic.
$ secvf-status
== SecVF Router ==
IP forwarding : enabled
LAN (eth0) : 10.0.100.1/24 UP
WAN (eth1) : 192.168.64.7/24 UP (gw 192.168.64.1)
DHCP : running (4 active leases)
DNS : running (forwarders 1.1.1.1, 9.9.9.9)
iptables FORWARD: ACCEPT (default)
NAT : POSTROUTING MASQUERADE on eth1
Conntrack : 213 entries, max 65536
secvf-monitor
Live tail of new connections, DNS queries, and ARP activity. Curses-based, refreshes 4 Hz.
$ secvf-monitor
─ Connections (last 30s) ─────────────────────────
10.0.100.15 → 1.1.1.1:53 (UDP)
10.0.100.15 → 142.250.80.46:443 (TCP, ESTABLISHED)
10.0.100.42 → 185.199.108.153:443 (TCP, SYN_SENT)
─ DNS (last 30s) ────────────────────────────────
10.0.100.15 ? google.com → 142.250.80.46
10.0.100.42 ? github.io → 185.199.108.153
10.0.100.42 ? evil.example.invalid → (no answer)
─ ARP ─────────────────────────────────────────
10.0.100.15 = 52:54:00:a3:b1:90
10.0.100.42 = 52:54:00:c8:d2:e4
secvf-capture
Wrapper around tcpdump/tshark with sensible defaults — rotating files at 100 MB each, in /var/captures/, with hostname + timestamp filenames.
$ secvf-capture start # capture all eth0 traffic
[+] writing /var/captures/kali-router-2026-05-10T14-32-00.pcap (rotate 100 MB)
$ secvf-capture status
running: pid 1234, 2 files, 187 MB total
$ secvf-capture stop
[+] stopped. files in /var/captures/
$ secvf-capture --host 10.0.100.15 start # filter to one client
$ secvf-capture --port 443 --no-rotate start
In-guest capture vs host capture
You have two places you can capture traffic when running in Router-VM mode. Use both for full coverage:
| Location | What you see | What you miss |
|---|---|---|
| Host (SecVF packet panel) | Every frame on the virtual switch — before the router touches it. | Internet-side traffic past the router's MASQUERADE. |
Router (secvf-capture) | eth1 (post-NAT) traffic — what the internet actually sees. | Inter-VM lab traffic that the router never sees. |
For a typical malware analysis: start the host capture before detonation (catches first-stage staging traffic, fast), and start secvf-capture on the router (gives you post-NAT correlation with anything external).
Hardening the router
Defaults are convenient, not secure. Once your lab is stable:
- Tighten FORWARD. Replace the default
ACCEPTwith a curated set of rules: alloweth0 → eth1only, dropeth1 → eth0, optionally rate-limit. - Lock down OUTPUT. The router itself shouldn't be initiating connections except to its known forwarders (DNS) and its package mirror.
- Disable SSH password auth.
PasswordAuthentication noin/etc/ssh/sshd_config.d/secvf.conf. Only key-based access from the host. - Snapshot it. Once configured, duplicate the bundle (see Snapshots). If a future analysis bricks the router, restore in seconds.
- Disable systemd services you don't need.
cups,avahi-daemon,bluetooth— none of them belong on a router VM. The provision script does most of this; review the final list withsystemctl list-unit-files --state=enabled.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Lab VMs get DHCP but no internet | IP forwarding disabled, or MASQUERADE missing | sysctl net.ipv4.ip_forward should be 1; iptables -t nat -L POSTROUTING -v should show MASQUERADE rule |
| No DHCP lease at all | isc-dhcp-server not running, or listening on the wrong interface | systemctl status isc-dhcp-server; check /etc/default/isc-dhcp-server has INTERFACESv4="eth0" |
| DNS resolves on host but not lab guest | dnsmasq not running, or guest pointing at host's resolver | systemctl status dnsmasq; in lab guest, resolvectl status should show 10.0.100.1 |
| External hosts can reach lab VMs (bad) | FORWARD is ACCEPT by default — see hardening above | Tighten FORWARD chain |
| Router itself can't reach the internet | eth1 not getting a DHCP lease, or upstream firewalled | dhclient -v eth1 from inside the router; check Apple framework's NAT range |
secvf-status reports "command not found" | Scripts USB not mounted, or setup script never ran | Re-mount Scripts USB and re-run kali-router-setup.sh |