Snort Fortification Against Evasions

I'm trying to post this before the arrival of hurricane Irene and the almost certain power outage since our neighborhood receives electricity from above-ground tree-lined power lines. I'd like to send nature's router a source quench from sending any more disasters our way – between the once-in-a-century East coast 5.9 earthquake and a predicted very destructive hurricane, I think we've had enough!

Most of the evasion techniques I've discussed in past blogs have involved a single technique. I thought I'd demonstrate one with multiple different evasions and how Snort was not fooled by any. I'll discuss the evasions in order of protocol – IP then TCP – while the Scapy program below needs to generate the TCP layer first and then fragment the entire IP packet. Specifically, I fragmented all PUSH segments into 8-byte payloads, fragmenting not only the payload but the 20-byte default-sized TCP header. Next, I wanted to test the TCP reassembly capabilities by causing the TCP sequence numbers to wrap back to 0 from the largest value of 4294967295 or 2**32 – 1 in the middle of the PUSH payload. Finally, I also chopped all the PUSH segments into one-byte payloads that were sent in reverse order from highest to lowest TCP sequence numbers. If Snort or any other IDS/IPS or session reconstructing tool is able to reassemble all these combined evasion attempts, it has robust TCP reassembly.

My Python is not the most efficient, but it gets the job done. The program imports all the necessary modules, creates a list to store the packets before sending them, assigns an Initial Sequence Number value that will cause the TCP sequence numbers to wrap somewhere in the payload of "EVILSTUFF", and then sends the SYN, listens for and increments the servers TCP sequence number for the acknowledgement, and sends the client acknowledgement. This is not very different, except for the care taken to assign the ISN value, than many of the other Scapy programs I've blogged about.

The second part of the program simply crafts a separate packet with a PUSH segment for each letter found in the URL request. The TCP sequence number is incremented by 1 except if the maximum TCP sequence value is found, where it wraps back to 0. Each of these new packets is added to a list and the list is reversed so that the last segment is first, etc., effectively reversing the order of the packets. Finally, each of these PUSH packets is fragmented into 8 bytes and sent on the network. If you decide to run this program, I'd suggest sending it to a netcat listener on port 80 so you can verify that the payload arrives as expected.

The traffic was captured using tcpdump into a file called foolids.pcap. Next, let's run it against Snort. Here is the Snort configuration file.

What is important for this particular evasion attempt is that the frag3 and stream5 preprocessors are defined. I failed to define the frag3 preprocessor and Snort didn't alert. But, my buddies on the Snort team – Russ Hook-and-Ladder Combs (volunteer fireman), Steve Spike Sturges (volleyball superstar), and Todd Awesome Wease (very smart and all around nice guy) – figured out what I was doing wrong. Even though these evasions do not use any specific policy evasions, both the global and specific frag3 and stream5 preprocessors must be defined. The frag3 preprocessors ensure that Snort handles fragmentation properly and should be defined if you have IP fragmentation in your network. The specific "windows" policy doesn't really matter for this evasion – you could have any policy and Snort would reassemble the fragments properly. The policy becomes more important for operating system specific behaviors like overlapping IP fragments or TCP segments that were not used in these attempted evasions. As well, you should always have a stream5 global and policy preprocessor, and you may or may not elect to have specific policies for different operating systems.

The http_inspect preprocessors are needed because the single Snort rule uses the "http_uri" keyword to find the content of "EVILSTUFF" in the URL. I ran Snort version using the following command:

snort –A console –q –K none –c russ.conf –r foolids.pcap

This command displays the alert to the console, quiets start-up messages, does not log, reads the configuration from "russ.conf" and reads the packets from the file "foolids.pcap". Snort found the content despite all evasion attempts:

[**] [1:12345:0] EVILSTUFF [**] [Priority: 0] {TCP} ->

As I was reminded, the Snort code is all there, but it is important to include all the necessary and default Snort configuration preprocessors to prevent evasions.

I'm hoping hurricane Irene evades us all!