Lab Notes
Lab 01

Lab 01 — ARP Poisoning & Dynamic ARP Inspection

2026-09-21Cisco Catalyst 2960-X · OPNsense · Proxmox
lablayer2arpdaidhcp-snoopingproxmoxciscosecurity-plus

Result

Ran an ARP cache poisoning attack between two hosts on an isolated VLAN, confirmed a man-in-the-middle position by capturing the victim's traffic, then defended against it with Cisco Dynamic ARP Inspection on a Catalyst 2960-X.

The defense failed the first time — and the reason was more instructive than a clean pass.

DAI was correctly configured and actively logging 80 dropped forged ARPs, yet the victim's ARP table still flipped to the attacker's MAC. Both machines were VMs on the same hypervisor. Their traffic was switched in software by the virtual bridge and never reached the physical switch, so the control had nothing to inspect.

Rebuilding the topology so the attacker had its own physical switch port — via an Intel i350 passed through to the VM — produced the expected result: 584 forged ARPs dropped, and the victim's ARP table never changed at any point during the attack.

Attacker and victim on one host Attacker on its own port
Attack traffic crosses the switch No — software bridge Yes
Forged ARPs dropped by DAI 80 (gateway-directed only) 584
Victim's ARP table Poisoned Never altered
Outcome Attack succeeded Attack defeated

The takeaway: a layer-2 control governs only the traffic that crosses the device enforcing it. Two endpoints on the same virtual bridge form a segment the physical switch cannot see or protect — a blind spot that exists in production virtualization, not just in labs.


Environment

Role Host Detail
Switch Cisco WS-C2960X-48FPD-L (lab-sw01) IOS 15.2(7)E
Gateway OPNsense (lab-fw01) 10.0.30.1, DHCP for VLAN 30
Hypervisor Proxmox VE (lab-pve01) Uplink on Gi1/0/46
Attacker Kali (lab-kali01) VLAN 30
Victim Metasploitable 2 (lab-victim01) VLAN 30

VLAN 30 is an isolated lab segment, 10.0.30.0/24, firewalled from the other VLANs at the gateway.


Method

Part 1 — Establishing the MITM

The victim's ARP table only populates after it resolves something, so it has to be seeded before there is anything to poison:

ping -c 3 10.0.30.1
arp -n

Record the gateway's real MAC. On the attacker, enable forwarding first — without it the victim loses connectivity and the attack is a denial of service rather than an interception:

sudo sysctl -w net.ipv4.ip_forward=1

Then poison both directions. The victim is told the attacker is the gateway; the gateway is told the attacker is the victim:

sudo arpspoof -i eth0 -t <victim-ip> 10.0.30.1
sudo arpspoof -i eth0 -t 10.0.30.1 <victim-ip>

The victim's arp -n now shows the attacker's MAC for the gateway. Interception confirmed by capture:

sudo tcpdump -i eth0 -n host <victim-ip> and not arp

The victim's ICMP to its gateway appears on the attacker — traffic that should never reach a third host on a switched network.

Part 2 — DAI, and why it didn't work

DAI validates ARP against the DHCP snooping binding table, so snooping is a prerequisite:

conf t
ip dhcp snooping
ip dhcp snooping vlan 30
no ip dhcp snooping information option
ip arp inspection vlan 30
end

no ip dhcp snooping information option is required here. Without it the switch inserts option-82 data that the OPNsense DHCP server discards, which breaks leasing on the VLAN entirely.

The uplink toward the real gateway must be trusted, or DAI inspects legitimate gateway ARP and takes down the segment:

conf t
interface GigabitEthernet1/0/48
 ip arp inspection trust
 ip dhcp snooping trust
end

The victim leased its address before snooping was enabled, so it had no binding. Forcing a re-lease creates one:

sudo dhclient -r eth0 && sudo dhclient eth0

Re-running the attack produced contradictory evidence: the switch logged %SW_DAI-4-DHCP_SNOOPING_DENY and counted 80 drops, while the victim's ARP table was poisoned anyway.

show mac address-table vlan 30 explained it — only the attacker's MAC appeared. The switch had never seen a frame from the victim. Both VMs shared one hypervisor, one virtual bridge, and one physical uplink. Proxmox switched their traffic in software:

The control was working correctly on every packet it was shown. It was shown the wrong half of the attack.

Part 3 — Rebuilding for physical separation

An Intel i350-T4 passed through to the attacker VM gives it a dedicated switch port, so attacker-to-victim traffic has to traverse the 2960-X.

IOMMU groups were clean — each NIC port isolated in its own group (10–13), while the onboard NIC shared a group with chipset functions and could not be passed:

lspci -nn | grep -i ethernet
dmesg | grep -i iommu

The VM's existing virtual NIC was disconnected so it had only one path to the VLAN — otherwise traffic can still take the software bridge and defeat the test.

Hardware note. The host would not POST with the NIC installed, throwing amber LED code 2,7 — documented as a memory failure. The real cause is a known OptiPlex 7060 quirk: it will not POST with a multi-port NIC installed while DIMM slot 4 is populated. Reported by others with Intel X710 and Broadcom quad-port cards, and on equivalent HP models. Moving the DIMM from slot 4 to slot 3 resolved it with no memory lost. A previous NIC had been returned as faulty on the assumption the card was bad; it almost certainly wasn't.

With the attacker on Gi1/0/19 and the victim still behind the hypervisor uplink, the same attack ran again. The switch logged drops on the attacker's port, the counter reached 584, and the victim — forced to re-resolve the gateway during the attack — received the correct MAC.

Precision: in Part 2 the victim's table was poisoned and then recovered when the attack stopped, because arpspoof re-broadcasts correct mappings on clean exit. In Part 3 there was nothing to recover. The entry never changed.


Side finding — detection and containment are separate

The DAI rate limit on the attacker's port (15 pps) never tripped; the port stayed up throughout. arpspoof's default rate sits below the threshold.

That distinction matters. The rate limit is a containment mechanism — it err-disables an offending port. It is not the detection mechanism. DAI drops every forged ARP regardless of rate. An attacker throttling below the threshold evades port shutdown while still having 100% of forgeries dropped.

Which also means the reverse: a switch running DAI without a rate limit detects and blocks everything, and isolates nothing.


What this demonstrates