IP fragmentation is the source of many security issues, even from the IPv4 era; the case could not be different in IPv6 (see for example here and here). Of course, IPv6 Extension Headers can make the situation much worse. Hence, it seems that we have quite a few good reasons to block fragmentation at our firewalls, either at our network perimeter or even at host firewalls.
While writing an IPv6 Hardening Guide for Linux Servers, I found that the usual ip6tables rules to block fragmentation did not work in latest Red-Hat Enterprise OS and clones (e.g. Centos). Specifically, I found out that at Red Hat Enterprise version 6.6 (also affects version 7), while applying ip6tables to block incoming IPv6 fragments, IPv6 fragments are not blocked, as it should be the case, either for atomic or non-atomic ones. You can easily confirm it by:
a. Issuing the command: ip6tables -I INPUT 1 -i eth1 -m ipv6header --header frag --soft -j DROP
b. Sending ICMPv6 Echo Requests with size bigger than the MTU size, e.g.: ping6 -s 4000 <IPv6 address of the target machine>.
Note: This issue affects only the blocking of IPv6 Fragment header, not the rest of the IPv6 Extension headers.
Similar results (no blocking of IPv6 fragments) are obtained for the following commands:
ip6tables -I INPUT 1 -m frag --fragfirst -j DROP #(it should drop the first fragment)
ip6tables -I INPUT 1 -m frag --fraglast -j DROP #(it should drop the last fragment)
ip6tables -I INPUT 1 -m frag --fragmore -j DROP #(it should drop all but the last fragment)
I immediately reported it as a bug at Red Hat Bugzilla (Bug 1170144), assuming that this would be a kind of a bug that it would be fixed easily and quickly. However, the developers that responded mentioned that actually this was a consequence of a deliberate change of fragmentation handling in these systems. Specifically, they mentioned that this was probably because after bug id 1011214 (kernel-2.6.32-437.el6) netfilter started processing the reassembled packet instead of the fragments, like IPv4 does. For Red Hat 6.5 this was fixed on kernel-2.6.32-431.5.1.el6 and thus, any kernel before that would work differently. Now netfilter works like it works for IPv4. That is, the only way to see such fragments again through netfilter chains is by unloading nf_defrag_ipv6 module. They also mentioned that the change of behavior is because now only the final, reassembled, packet is pushed through netfilter chains - and not the fragments.
There is also a positive effect though. Due to this change, atomic fragments are not forward anymore, meaning that they will get reassembled and forwarded as a “clean” (without IPv6 Fragment header) packet, which is obviously good, at least from a security perspective.
However, this is not true for genuine fragments, since they will get re-fragmented on output in case you're forwarding them.
By unloading that module, you can still receive such packets locally but you're not able to use conntrack on that system.
After confirming that this was the case, it was obvious that a workaround should be found. A first few comments though.
First, the fact that "...genuine fragments, they will get re-fragmented on output, in case you're forwarding" seems to somewhat break one of the basic IPv6 principles (RFC 2460, section 4.5) which defines that: "unlike IPv4, fragmentation in IPv6 is performed only by source nodes, not by routers along a packet's delivery path". But this is not the most important consequence, although it introduces a delay (due to reassembly-refragmentation process).
As a security engineer I would like to have the ability to block fragments if I wanted to. Fragmentation in IPv6 when combined with Extension headers can have some really bad security consequences, as discussed briefly at the beginning of this article; so, I would like to be able to block them, if, for instance, this is the policy of my organisation, company, etc.
Now, if we use the aforementioned workaround (unloading the suggested kernel module), we will lose the capability of connection tracking, which is not good either (if not worse).
After a (short) discussion and a kinf of brainstorming, Florian Westphal from Red Hat suggested that IPv6 fragmented traffic could be filtered using nftables, because with them you can define arbitrary tables and priorities; so, its easy to hook packets before they hit defrag. However, at the time of this writing, March of 2015, nftables were not supported even on RHEL 7.1. Anyway, Marcelo Ricardo Leitner suggested a rule to block IPv6 fragments using nftables which would look like as follows:
table ip6 filter {
chain preroute500 {
type filter hook prerouting priority -500; policy accept;
ip6 nexthdr ipv6-frag counter packets 2 bytes 2104
}
}
As Marcelo explained, the line "ip6 nexthdr ipv6-frag counter" will match whenever an IPv6 packet is seen with a nexthdr field being set to ipv6-frag, which is only used for fragments (even atomic ones); other traffic will pass this check. Of course, for dropping, you have to add a 'drop' in this rule.
Is the aforementioned approach effective on blocking IPv6 fragments? Frankly, I have not tested it yet, but certainly, taking into account the latest kernel changes it seems to be the only way to do go. Hopefully, I will have the chance to test it soon, so, stay tuned :)
Write a comment
Olaf Martens (Saturday, 13 April 2019 00:15)
I'm currently checking another approach than the one mentioned here. In the mangle table I'm adding this to the PREROUTING chain:
iptables -t mangle -A PREROUTING -m frag --fragmore -j MARK --set-mark 1
Then, in the INPUT chain of the filter table, I just do this:
iptables -A INPUT -m mark --mark 1 -j DROP
In case this works the way it is supposed to, if a fragment is detected, the packet is marked, and whenever this mark is detected in the INPUT chain, the packet is discarded. This, of course, requires some testing, though.