Suricata TCP Evasions
If you’re currently using the Open Source intrusion detection/prevention system Suricata, I urge you to upgrade to the recently released version 1.0.2 as soon as possible. In my recent research, I've found more than half a dozen TCP evasions in code from prior versions.
In my experience with Open Source intrusion detection systems like Snort, accurate and thorough TCP reassembly is extremely difficult. Snort's TCP stream reassembly code has transitioned from being pretty lame and rudimentary (see Stick and Snot attacks) into to a flawed stream4 implementation, finally evolving into a solid and comprehensive stream5 module--over a decade later.
I've been interested in Suricata's stream reassembly code since they offered their first beta release over a year ago. When they released their first non-beta stable 1.0 release this past July, I felt it was finally fair game to try some evasions. Unfortunately, it was fairly easy to find them. Some, like the failure to properly perform TCP checksum validation, were trivial; others like the one discussed below took more poking.
My intent was to find evasions that were more or less universal – not affiliated with a particular operating system (AKA target-based). I crafted the evasion techniques with Scapy and sent them to a destination operating system running Linux first and later Windows Vista. Obviously, TCP evasions are most dangerous when Windows is the destination host since Windows is still the most prevalent OS. In all fairness, I ran any TCP evasion that I discovered against Suricata later against a current version of Snort – 18.104.22.168. Snort alerted on all of the techniques that successfully evaded Suricata.
Let’s examine one of the several Suricata TCP evasions I was able to uncover in versions prior to 1.0.2. Suricata uses Snort rules. Assume that the following Snort rule exists and is configured for use:
alert tcp any any -> any 80 (msg:"EVILSTUFF alert"; flow:to_server, established; content: "EVILSTUFF"; http_uri; sid:1234; )
This rule looks for the content of "EVILSTUFF" in an HTTP URL.
First, here is the Scapy code that I used. I'm not going to go over the specifics of the code since I covered how to create the client side of the code in the blog post on an unusual response from a bad acknowledgement on the last segment of the three-way handshake. The code for this evasion shares many of the concepts and some of the code.
Now, let's take a look at the client side traffic that the the Scapy code generated.
Segment 1 represents a SYN with a crafted Initial Sequence Number (ISN) of 10. The next crafted segment 2 shows the acknowledgement of the server's SYN/ACK. Next, I reset the connection in segment 3. This should be the end of tracking this session.
Segment 4 is a SYN sent about 5 seconds after the reset and is identical to the first SYN, except it uses an ISN of 11. Segment 5 completes the acknowledgement of the three-way handshake from the new session and segments 6 and 7 contain the malicious split content of "EVILSTUFF". Suricata fails to alert here; we assume this is because it gets confused after improperly closing/cleaning up after the first session and mistakenly considers the new session as a continuation of old one. It doesn't track the new session SYN with the ISN of 11 and when the malicious content is sent later—with a sequence number one more than Suricata expects—it creates a gap in sequence numbers for Suricata. Suricata cannot properly reassemble the malicious content since there is a missing sequence number before it, causing the evasion.
The failure to properly close one session and distinguish it from a new one is neither a trivial nor a target-based issue. So please make sure that you upgrade right away if you are currently using Suricata.
Additionally, if you have or are considering dumping Snort in favor of Suricata, my advice is: Don't do it yet! Suricata is a work in progress. I don't mean to diminish the hard work of some very talented people, but the product needs to mature before I would consider using it as my exclusive means of IDS/IPS. And please note that I've scrutinized only a specific aspect of the product and have found what I consider to be substantial issues. The Suricata team has fixed the issues I reported, so the stream reassembly has improved.
In the interest of full disclosure, I worked at Sourcefire on the Vulnerability Research Team for about 5 years. Specifically, I did research on Snort's current stream reassembly preprocessor known as stream5. Steve Sturges translated the research and improved stream4 to produce stream5. It was at Sourcefire that I learned Scapy and began to use it to try to evade other companies' TCP stream reassembly as well as help build a regression suite to ensure that code changes to stream5 or other modules didn't accidentally introduce unintended consequences. So, yes – I am somewhat biased in my preferences for Snort; I very much appreciate the opportunities presented to me at Sourcefire and I have an abundance of loyalty and allegiance to this day. But, I've got no current financial or other investment in Sourcefire, so I've nothing to gain monetarily from exposing Suricata's TCP reassembly issues.
I'd like to thank Todd Wease, who used to work for Sourcefire on the Snort team, for validating the evasions I found and for examining the code to help me understand the logic issues. And, I'd like to thank Jen Harvey, Co-Founder at Voxilate, for reviewing this post and cleaning it up so it is coherent.
As a final note: You, too, can attempt to find evasions such as these in your IDS/IPSes packet-crafting with Scapy. There’s still time and room to sign up for my Power Packet Crafting Using Scapy course in Las Vegas at the end of September. Please come join me in Las Vegas in September!