IPv6 ICMP ND-NA Spoofing
Introduction
IPv6 solves the address resolution problem by introducing ICMP6 Solicitation message (a type of Neighbor Discovery message) and a type of Neighbor Advertisement message. This tutorial serves two purposes.
- We are to understand IPv6 Neighbor Disovery and Advertisement for address resolution via hands-on packet crafting experiments
- We need this to realize part of TCP protocol via packet crafting
There are two methods to add (or to spoof) a host.
- Add a static entry to a host’s ARP (or neighbor) table
- Reply to ICMPv6 Neighbor Discovery message
Adding Static ARP Entry
We can add a static IPv6 neighbor via the ip neighbor
command . Below is an
example of using the command.
sudo ip -6 neighbor add fc00::1:1:3 lladdr 08:00:27:08:0d:a1 dev enp0s9
Replying to IPv6 Neighbor Discovery Message
The following program does just that,
#
# ipv6phantom.py
#
# don't run this on the host the IPv6 neighbor to be updated with
# run it on any other host on the same network
#
import signal
import sys
import time
from scapy.data import IP_PROTOS
from scapy.sendrecv import sendp
from scapy.sendrecv import sniff
from scapy.layers.l2 import Ether
from scapy.layers.inet6 import IPv6
from scapy.layers.inet6 import ICMPv6ND_NS
from scapy.layers.inet6 import ICMPv6ND_NA
from scapy.layers.inet6 import ICMPv6NDOptSrcLLAddr
from scapy.layers.inet6 import ICMPv6NDOptDstLLAddr
def gracefully_exit(sig, frame):
print("Ctrl-C pressed. Exiting ...")
sys.exit(0)
def usage(prog):
# python ipv6phantom.py fc00::1:1:3 08:00:27:08:0d:a1 enp0s9
print("Usage: prog PHANTOM_HOST_IPv6 PHANTOM_HW_ADDR NIC")
def main(argv):
signal.signal(signal.SIGINT, gracefully_exit)
if len(argv) < 4:
usage(argv[0])
sys.exit(1)
# host = 'fc00::1:1:3'
# nic = 'enp0s9'
# hwaddr = '08:00:27:08:0d:a1'
host = argv[1]
hwaddr = argv[2]
nic = argv[3]
filter = "icmp6 and ( ip6[40] == 135 or ip6[40] == 136 )"
while True:
packets = sniff(filter=filter, count=1, iface=nic)
if not packets[0].tgt == host:
continue
if not packets is None \
and not packets[0][ICMPv6ND_NS][ICMPv6NDOptSrcLLAddr] is None \
and packets[0][ICMPv6ND_NS][ICMPv6NDOptSrcLLAddr].type == 1:
# https://tools.ietf.org/html/rfc2461#section-4.6.1
ndopthdr = ICMPv6NDOptDstLLAddr()
ndopthdr.lladdr = hwaddr
# https://tools.ietf.org/html/rfc2461#section-4.4
ndnahdr = ICMPv6ND_NA(R=0,S=1,O=1)
ndnahdr.tgt = host
# https://tools.ietf.org/html/rfc8200#section-3
ipv6hdr = IPv6(nh=IP_PROTOS.ipv6_icmp, hlim=255)
ipv6hdr.src = host
ipv6hdr.dst = packets[0][IPv6].src
# https://tools.ietf.org/html/rfc2464
ethhdr = Ether()
ethhdr.dst = packets[0][ICMPv6ND_NS][ICMPv6NDOptSrcLLAddr].lladdr
ethhdr.src = hwaddr
# now the packet
ndpkt = ethhdr / ipv6hdr / ndnahdr / ndopthdr
# now transmit ...
sendp(ndpkt, iface=nic)
ndpkt.display()
#print(ndpkt.summary())
time.sleep(5)
if __name__ == "__main__":
main(sys.argv)