IPv6 Focus Month: The warm and fuzzy side of IPv6
Protocols like IPv6 and IPv4 suffer from two very different types of security issues: Oversights in the specification of the protocol and implementation errors. The first one is probably the more difficult one to fix as it may require changing the protocol itself and it may lead to incompatible implementations. The second one isn't easy to avoid, but at least we do have some decent tools to verify the correct implementation of the protocol. In implementing protocols, developers usually try to stick to the specifications, and implement the "robustness principle" (RFC 1122) which is sometimes also referred to as Postel's law after Jon Postel. In short, the principle stipulates that a protocol implementation should stick close to the specification in sending data, but should be very forgiving in accepting data. This principle makes robust interoperability possible, but also leads to many security issues. For example, in many cases an IDS may not consider data because it is "out of spec" but the host will still accept it because it will try to make things work. Or on the other hand, an IDS may consider a host to be more forgiving then it actually is.
What we need is techniques and tools to check the implementation and push the boundaries of what the specification considers acceptable. This method of security testing is usually referred to as "Fuzzing", and one great tool to implement it for IPv6 is scapy. Scapy used to have an add on, scapy6, that implmeneted IPv6. However, recent versions of scapy include scapy6 as part of the tool.
So what can we do? Lets start with something straight forward: A simple TCP packet. In scapy, we first build an IPv6 header, then attach a TCP header. Here we keep it as simple as possible:
# scapy
Welcome to Scapy (2.2.0)
>>> ip=IPv6(dst="2001:db8::1");
>>> tcp=TCP(sport=32666,dport=80,flags=S);
>>> sr1(ip/tcp) Begin emission: Finished to send 1 packets. Received 293 packets, got 1 answers, remaining 0 packets <Pv6 version=6L tc=0L fl=0L plen=24 nh=TCP hlim=57 src=2001:db8::1 |<TCP sport=http dport=32666 seq=3689474164 ack=1 dataofs=6L reserved=0L flags=SA window=5680 chksum=0xaab6 urgptr=0 options=[('MSS', 1420)] |>>
>>> hbh=IPv6ExtHdrHopByHop(nh=59,len=0,options=Jumbo(jumboplen=0));
>>> sr1(ip/hbh/tcp);
Begin emission:
Finished to send 1 packet.
# tcpdump -i en0 -nn -tvv ip6 and host 2001:db8::1
IP6 (hlim 64, next-header Options (0) payload length: 28) 2001:db8::2 > 2001:db8::1: HBH (jumbo: 0) no next header
>>> ip=IPv6(dst="2001:db8::1",plen=0); >>> sr1(ip/hbh/tcp);
and again no response.
So this was prety simple. Next step: Lets do a 3 way handshake. Instead of pasting the script here, I uploaded a simple IPv6 3-way TCP handshake here. The script will setup a TCP connection to port 80, then transmit a simple HTTP request in two segments. Again: We start simple. This should work.
Next, lets be a bit evasive. We will retransmit the second segment, but the second segment contains a different content. The full script can be found here. The interesting part:
my_payload2="sec546.com
"
my_payload3="secxxx.com
"
TCP_PUSH=TCP(sport=sport,dport=dport, flags="PA", seq=isn+1,ack=my_ack)
send(ip/TCP_PUSH/my_payload1)
TCP_PUSH=TCP(sport=sport,dport=dport, flags="PA", seq=isn+1+len(my_payload1),ack=my_ack)
send(ip/TCP_PUSH/my_payload2)
send(ip/TCP_PUSH/my_payload3)
DH=IPv6ExtHdrDestOpt(options=HBHOptUnknown(otype=255,optdata='x'))
send(ip/DH/TCP_PUSH/my_payload2)
send(ip/TCP_PUSH/my_payload3)
------
Johannes B. Ullrich, Ph.D.
SANS Technology Institute
Twitter
Application Security: Securing Web Apps, APIs, and Microservices | Online | US Eastern | Jan 27th - Feb 1st 2025 |
Comments
Latest presentation: https://www.ernw.de/download/IPv6%20Extension%20Headers%20-%20New%20Features,%20and%20New%20Attack%20Vectors.pdf
Scapy scripts: https://www.ernw.de/download/IPv6%20Extension%20Headers%20-%20New%20Features,%20and%20New%20Attack%20Vectors.py
James Small
Mar 20th 2013
1 decade ago