Threat Level: green Handler on Duty: Johannes Ullrich

SANS ISC: Internet Storm Center - SANS Internet Storm Center Internet Storm Center


Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!

Latest Diaries

How to Have Fun With IPv6 Fragments and Scapy

Published: 2017-01-23
Last Updated: 2017-01-23 19:26:38 UTC
by Johannes Ullrich (Version: 1)
0 comment(s)

I may extend this with a second entry later this week. But as so often, I found myself on a long flight with some time on my hands, and since the IETF just released a new RFC regarding IPv6 atomic fragments, I figured I will play a bit with scapy to kill time. [1] And well, this also makes good material for my IPv6 class [2]. This is supposed to entice you to play and experiment. Let me know if you find anything neat.

Fragmentation is a necessary evil of packet networking. Packets will encounter networks with different MTUs as they traverse the network, and even if all of your networks have the same MTU, it may not be large enough to accommodate some packets (for example large DNS replies taking advantage of EDNS0).

IPv6 made some substantial changes to the way packets are fragmented. The goal was to simplify and even discourage fragmentation, and also to remove some of the work involved from routers. As a result, only the source of the packet is supposed to fragment, not the router. This will not only make live easier for routers, but it also allows senders to adjust the packet size appropriately and forego fragmentation. Double fragmentation, where two routers fragment already fragmented packets further, should no longer happen. This double fragmentation was one source of a lot of pain for IPv4. To further reduce the probability of having to fragment packets, IPv6 defines a minimum MTU of 1280. Networks with an MTU of less than 1280 bytes will no longer be able to route IPv6 traffic.

But what does this mean for network traffic, how is this implemented, and how do I test implementations? In short: scapy ;-)

1 - What happens to fragments that are smaller than 1280 bytes (and not the last fragment)? 

For all of our tests, we use simple echo requests. To build them in scapy:

I=​IPv6(dst="2001:db8::1")
ICMP=ICMPv6EchoRequest(data='A'*1000)
FH=IPv6ExtHdrFragment()
packets=fragment6(I/FH/ICMP,100)

This creates a set of packets that should never be seen via IPv6. But, after sending it with:

for p in packets:
   send(p)

We see in tcpdump that this works quite well:

IP6 2001:db8::2 > 2001:db8::1: frag (0|48) ICMP6, echo request, seq 0, length 48
IP6 2001:db8::2 > 2001:db8::1: frag (48|48)
IP6 2001:db8::2 > 2001:db8::1: frag (96|48)
....
IP6 2001:db8::2 > 2001:db8::1: frag (960|48)
IP6 2001:db8::1 > 2001:db8::2: ICMP6, echo reply, seq 0, length 1008

The recipient replies as expected with a non-fragmented packet.

2 - What happens if a "Packet Too Large" error comes back with an MTU of less than 1280 Bytes?

Now lets "up this" by a step. I will now send the same echo request packet without fragmenting it. Of course, we will get a reply, but my host will respond with a "Packet too large, Fragmentation Required" error and advertise the ridiculous small MTU of 100 bytes, which is small even for IPv4. The reason that I am doing it this way is so that I can send all the crafted packets from one host:

send(I/ICMP)
IP6 2001:db8::2 > 2001:db8::1: ICMP6, echo request, seq 0, length 1008
IP6 2001:db8::1 > 2001:db8::2: ICMP6, echo reply, seq 0, length 1008

send(I/ICMP)
send(I/ICMPv6PacketTooBig(mtu=100))/
send(I/ICMP)

IP6 2001:db8::2 > 2001:db8::1: ICMP6, echo request, seq 0, length 1008
IP6 2001:db8::1 > 2001:db8::2: ICMP6, echo reply, seq 0, length 1008
IP6 2001:db8::2 > 2001:db8::1: ICMP6, packet too big, mtu 100, length 8
IP6 2001:db8::2 > 2001:db8::1: ICMP6, echo request, seq 0, length 1008
IP6 2001:db8::1 > 2001:db8::2: ICMP6, echo reply, seq 0, length 1008

ICMP error messages need to include parts of the packet that caused them to be taken serious. So we need to capture the ICMP echo response, and append it to the error:

r=sr1(I/ICMP)
send(I/ICMPv6PacketTooBig(mtu=100)/r)
send(I/ICMP)

IP6 2001:db8::2 > 2001:db8::1: ICMP6, echo request, seq 0, length 1008
IP6 2001:db8::1 > 2001:db8::2: ICMP6, echo reply, seq 0, length 1008
IP6 2001:db8::2 > 2001:db8::1: ICMP6, packet too big, mtu 100, length 1056
IP6 2001:db8::2 > 2001:db8::1: ICMP6, echo request, seq 0, length 1008
IP6 2001:db8::1 > 2001:db8::2: frag (0|1008) ICMP6, echo reply, seq 0, length 1008

Now we get a rather strange ICMP echo reply in the end. It is actually standard compliant, and referred to as an "atomic fragment". The packet includes a fragmentation header, but isn't really fragmented. The offset is 0, the more fragment flag is cleared, and well, the packet still carries the full 1008 bytes IP payload (it is actually 8 bytes longer now with the fragment header). The idea here is that the small "packet too big" message came likely from a tunnel, and that the packet will be fragmented over IPv4. Adding the IPv6 fragment header provides the tunnel endpoint with a fragment ID to derive an IP ID from. Odd.. but well, the RFC tells us to do this. 

These atomic fragments have been noted to lead to possible DoS conditions if we receive two of them (one of them spoofed). This would represent an overlapping fragment, and then both will get dropped.

3 - Are overlapping fragments still an issue?

For IPv6, overlapping fragments need to be dropped. But are they? Creating them is a bit tricky. We need to get the protocol checksum right for the "after re-assembly" packet, which of course is ambiguous. In IPv4, we were able to "cheat" with UDP packets. So far, I haven't been able to find a set of packets / operating system combination that violates the RFC. Here is a sample scapy script to create these packets. It avoids the checksum issue by just using a static payload throughout the packet. And since we do not get an answer for the ICMP echo request, the question of reassemble preference doesn't come up.

#!/usr/bin/python
from scapy.all import *

dst="2001:db8::1"
fid=random.randint(0,100000)
I=IPv6(dst=dst,nh=44)
ICMP=ICMPv6EchoRequest(data='x'*104,cksum=0x2de5)
FH=IPv6ExtHdrFragment(nh=0x3a,offset=0,m=1,id=fid)
send(I/FH/ICMP)
FH=IPv6ExtHdrFragment(nh=0x3a,offset=13,m=0,id=fid)
send(I/FH/'xxxxxxx')

 

[1] https://tools.ietf.org/html/rfc8021
[2] https://www.sans.org/course/ipv6-essentials

---
Johannes B. Ullrich, Ph.D.
STI|Twitter|LinkedIn

Keywords:
0 comment(s)

If you have more information or corrections regarding our diary, please share.

Recent Diaries

Sage 2.0 Ransomware
Jan 21st 2017
2 days ago by Brad (5 comments)

PowerShell 5.1 for Windows 7 and later
Jan 20th 2017
3 days ago by Basil (0 comments)

Making Windows 10 a bit less "Creepy" - Common Privacy Settings
Jan 18th 2017
5 days ago by Rob VandenBrink (3 comments)

domain_stats.py a web api for SEIM phishing hunts
Jan 17th 2017
6 days ago by Mark (0 comments)

View All Diaries →

Latest Discussions

Importance of File Integrity Monitoring software
created Jan 18th 2017
5 days ago by Promisec (0 replies)

New Incident Response/Forensics tool : srum-dump.exe
created Jan 12th 2017
1 week ago by Mark (1 reply)

How to make the social media accounts safe from hacking?
created Jan 6th 2017
2 weeks ago by Brad4333 (3 replies)

Time Warner Cable IMAP SSL certificate expired
created Dec 31st 2016
3 weeks ago by Paul (2 replies)

SonicWALL Setup
created Dec 29th 2016
3 weeks ago by HateTheSnow (3 replies)

View All Forums →

Latest News

View All News →

Top Diaries

Dyn.com DDoS Attack
Oct 21st 2016
3 months ago by Johannes (9 comments)

Port 7547 SOAP Remote Code Execution Attack Against DSL Modems
Nov 29th 2016
1 month ago by Johannes (21 comments)

Increase in Protocol 47 denys
Dec 29th 2016
3 weeks ago by Rick (11 comments)

TR-069 NewNTPServer Exploits: What we know so far
Nov 29th 2016
1 month ago by Johannes (12 comments)

Making Windows 10 a bit less "Creepy" - Common Privacy Settings
Jan 18th 2017
5 days ago by Rob VandenBrink (3 comments)