Corresponding to my talk at DENOG 9, this article covers the topic of whether different operating systems have correctly implemented RFC 6980.
Following my work on RFC 6980 (Security Implications of IPv6 Fragmentation with IPv6 Neighbor Discovery), that has been previously published on insinuator.net, I was happy to present my latest findings at the DENOG 9 meeting.
To make it available to anyone who did not meet me there and go into some more detail that would have exceeded the boundaries of the talk, I will cover the topic here.
After the preceding work on Windows Server 2016 and FreeBSD, as a Linux user, lover and administrator, I of course wanted to take a look at how different Linux systems complied with the RFC 6980 standard.
Introduction to RFC 6980
As many people I spoke with before my talk weren't familiar with the topic I'm going to give a short introduction. So if you just grabbed a link to this from twitter and don't want to take a detailed look at the RFC right now, here's a short sketchy description of the topic. If you're already familiar with the topic, feel free to skip the next two paragraphs.
An important design component of IPv6 is the Neighbor Discovery (ND) protocol, which provides a framework for all local management tasks like Router Discovery, Prefix Delegation, Address Autoconfiguration, Duplicate Address Detection and so on. As this only takes place on the local link, all of these mechanisms are unencrypted by design - if not replaced by SEcure Neigbor Discovery (SEND). Thus, clients on the network have no means of verifying the sender or data of ND messages. While in IPv4, a router was mainly a forwarding device, in IPv6 - through the Router Advertisement (RA) - a router also provides the client with the necessary information to connect to the network, like IP and prefix information. The risk of an attack (which is the main reson for doing this research) lies in an unauthorised client on the network sending a rogue RA, feeding clients a different IP address and default route and thus interceping its traffic by replacing the original router. Another problematic part of IPv6 design are Extension Headers. For the purpose of simplifying the IPv6 header, the "Next Header" field was introduced, thus allowing any number of extension headers to be placed in nearly any order in the IPv6 packet.
RFC 6980 states that these ND packets, as they're only traveling on the local link, need not be fragmented. Thus, it advises the client (a "MUST" as defined in RFC 2119) to silently ignore such messages, if fragmented. In our preceding work, my colleagues and I could show that this was not properly done if the RA came in fragments with some arrangements of Extension Headers placed in the fragmentable part of the message. A possible protection mechanism is the Router Advertisement Guard (RFC 6105) which can be enabled on a switch interface and forbidding RAs to be sent on that interface. However, as this mechanism can be evaded by some creative aligning of Extension Headers, we could successfully combine both flaws in the past. This way we managed to provide Windows and FreeBSD targets with freely chosen prefixes, have them set corresponding IP addresses and set routes to our attacker.
Testing it on Linux
After the spooking results on FreeBSD and Windows, offering multiple possibilities to evade the Router Advertisement Guard on the switch and inject malicious route and IP information to the client, I wanted to know if it looked quite as shocking in the Linux world.
Was my Debian laptop, my daily companion that I treasure and update regularly, that susceptible to fraud? (Spoiler alert: It's not, but it's time for my Arch using colleagues to stop mocking me.)
Please note: As this kind of benchmarking and testing is forbidden in some end user licence agreements (EULAs), I only used free Linux distributions and did not test any enterprise versions.
I mainly reused my lab setup that consisted of
- A Cisco Catalyst 3560 switch
- The "attacker" system running Linux and Chiron
- The "target" system, which was continuously re-installed with different operating systems
- My laptop, running a script to control attacker and target via SSH and collect the results
To send a router advertisement with Chiron, the baseline command was as follows:
./chiron_local_link.py enp0s25 -ra -pr 2001:db8:10:50:: -pr-length 64 -mtu 1400 -s fe80::ee9a:74ff:fef5:a385
The detailed test cases that are described in the tables below, were achieved by appending the corresponding options to the command shown above. For more details please refer to the Insinuator posts or the Chiron manual.
Once with the attacker connected to a plain switch interface and once with it plugged into a port where RA guard was enabled, I had an automation script execute the following steps for every test case:
- Reset target network interface to ensure a clean slate
- Start tcpdump packet capture on attacker and target
- Call Chiron with test flags on attacker machine
- Stop tcpdump and collect the PCAPs
- Collect target IP and route information
The observations were not completely pleasing:
In summary, the results were:
- I managed to get through to the ArchLinux, CentOS, FreeBSD and Windows Server systems.
- For the Debian, Ubuntu and OpenSUSE systems, however, I did not manage to find a combination that would allow me to inject addresses and/or routes.
- I could verify all packets being sent and - without RA guard - successfully received in the PCAP
- With RA guard enabled, I could only see the fragments - and not the original RAs in the first fragment.
- This leads to the even more shocking conclusion, that - on some systems at least - fragments of packets without the original first packet are evaluated based only on the contained Extension Headers.
As this affects nearly everyone who has IPv6, a local link and a router, the feedback I got after my talk was overwhelming. Thank you to all of you who approached me with questions, additional input or praise. This was my first talk at any conference ever and it has been a pleasure to be able to give it, so thanks to the DENOG organisers as well! The reactions I got really broadened the scope of where I thought this might apply and gave a lot of input to what still is there to be tested.
Many people approached me with the question of whether this was already being discussed with developers or vendors. The question of where this problem or incorrect behaviour as per RFC 6980 originates, is still to be discussed. My next steps will be looking at the different kernel versions, parameters and whether or not systemd-networkd is involved in the processing.
However, if you're reading this and you are a kernel, systemd or other network stack developer, feel free to approach me on this topic.