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!


What I Learned At Camp

I just returned from teaching my Scapy course at a Cyber Camp in Richmond, VA. The Cyber Camps are part of the US Cyber Challenge to educate and cultivate future cyber security talent, taught by SANS instructors. It was an amazing experience and the students were so bright, I think I learned more than I taught.

For years, I’ve praised the ease of assembling protocol layers to form packets in Scapy. At the same time, I’ve cursed the difficulties Scapy presented changing one or more values in an existing packet. Scapy recomputes any checksums (IP, UDP, TCP, ICMP) when you assemble a packet layer by layer. However, it does not recompute the checksum when you simply alter a value in an already assembled packet, resulting in a bad checksum for the particular layer(s). I’d scoured the Internet for documentation for an elegant remedy and found none, so I basically reassembled a new packet, layer by layer, keeping the values I wanted to preserve and altering the values I wanted to change. It’s a clumsy ham-handed way of doing things, but it worked.

As I lamented about this particular idiosyncrasy of Scapy while teaching, an exceptional student named Chris Cockerham asked what would happen if I just deleted the checksum attribute in the layer of the changed value. Magically, it worked and provided an elegant solution. Let me take you through the process of evolved code. I’ll start with the inelegant solution I used and progress to Chris’ solution.

All the code is entered in the Scapy interactive interface. First, the pcap file “/tmp/onepacket.pcap” is read into a list named “r”. The only packet in the list is stored in variable named “rec”. We display “rec” and see it has a current TTL of 50. Ultimately, we want to change the TTL value to 255. We attempt to do that and redisplay “rec” to find a new value of 255.

We read the packet into Wireshark and discover that the problem is that the IP checksum is now broken since Scapy did not recompute it. This means we cannot send this packet nor do anything useful with it.

Let’s look at the old solution I used. We reread the packet to get a clean version of it. Next, we extract the IP layer. Unfortunately, it’s not just the IP layer; it’s the IP layer and everything that follows.

Therefore in order to disassemble all layers, we have to extract each layer as best we can and clone it – preserving the values we want and updating those we want to change. Similarly, we extract the TCP layer, but find it has the following payload layer. We extract the final layer – the payload.

Now we begin to clone the new layers. The new IP layer is called “newIP” and we preserve the source and destination IP’s and alter the TTL value to be 255. If we wanted to preserve any other field in the IP header instead of settling for Scapy’s default value, we’d have to add its field and value to the new IP layer. Next, we rebuild the TCP layer. We save all the current fields and values in the TCP header. You can see how cumbersome this is. Finally, we build the new packet to be the new IP header followed by the new TCP header followed by the payload. We see that the packet has the desired TTL value of 255.

But what does Wireshark think about the IP checksum?

Wireshark examines the IP checksum and discovers it is correct. We accomplished what we wanted to do – change the IP TTL to 255 and cause Scapy to recompute the IP checksum. But, it was a tedious process.

The elegant solution that Chris suggested involves deleting the IP checksum attribute from the IP header with the line “del rec[IP].chksum”. We set the TTL in the IP layer of “rec” to be 255.

Let’s read the new packet into Wireshark.

Wirehshark displays our new TTL of 255 and a correct IP checksum, reflecting that Scapy recomputed the checksum after the attribute was deleted. This is so much easier than essentially cloning each of the layers, altering the desired value(s), and reassembling the packet.

To expand on this concept, you need to delete the checksum for the layer that you change. For instance, if we change the TCP window size in an assembled packet, we have to delete the TCP checksum. Be careful, though if you are changing IP addresses. Obviously, you’ll need to change the IP checksum. If the protocol that follows is UDP or TCP, both the UDP and TCP checksums have something known as a pseudo header that includes the IP addresses in the checksum. This means that you’ll need to delete the protocol checksum (UDP/TCP) attribute in addition to the IP checksum attribute.

Thanks a lot Chris. I never thought I’d learn so much teaching!


Sorting Packet Captures with Scapy

Today I spent a little time looking into a packet capture supplied by Vivek Ramachandran at SecurityTube. This packet capture is part of a series of WiFi hacking challenges he is putting together, and immediately after opening it I got freaked out.

Normally, packet captures are sorted by their capture timestamp, though there is no requirement for that to be the case. In Vivek's challenge, the packet capture appears to be intentionally out-of-order to make analysis more difficult. You can open it up in Wireshark and sort by the timestamp column, but it makes it impossible to apply packet filters such as "frame.number < 10000" since the frame number isn't related to incrementing timestamps.

Scapy to the rescue! A little Scapy and some Python love lets us re-write the packet capture, sorting the capture by the timestamp field. After starting Scapy we read the contents of the packet capture into a list element "p" as shown:
>>> p=rdpcap("Challenge2-Extract1.pcap")
>>> p[0]
<Dot11  subtype=8L type=Data proto=0L FCfield=from-DS+wep ID=11264 addr1=60:fb:42:d5:e4:01 addr2=00:21:91:d2:8e:25 addr3=00:21:91:d2:8e:25 SC=35792 addr4=None |<Dot11QoS  TID=0L EOSP=0L Ack Policy=0L Reserved=0L TXOP=0 |<Dot11WEP  iv='h-`' keyid=0 wepdata='\xb2\xaa\xdf\x8dV\xbb\xa3\x15\xc9$\xcb\x1d\x00\xffZ\xdd\xa2\xbe\xb4\xc0\xae\xa3u@\xeb\x99u\xeaO\x0f+\xec\x00\x19\xa2\xaa' icv=2325285167L |>>>
Great, p[0] is the first packet in the capture file. I wasn't sure how Scapy represented the timestamp value for each packet, but a little Python introspection made it easy to figure out:

>>> dir(p[0])
['__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__div__', '__doc__', '__eq__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__len__', '__lt__', '__metaclass__', '__module__', '__mul__', '__ne__', '__new__', '__nonzero__', '__rdiv__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__setitem__', '__str__', '__weakref__', '_do_summary', 'add_payload', 'add_underlayer', 'aliastypes', 'answers', 'build', 'build_done', 'build_payload', 'build_ps', 'canvas_dump', 'clone_with', 'command', 'copy', 'decode_payload_as', 'default_fields', 'default_payload_class', 'delfieldval', 'display', 'dissect', 'dissection_done', 'do_build', 'do_build_ps', 'do_dissect', 'do_dissect_payload', 'do_init_fields', 'explicit', 'extract_padding', 'fields', 'fields_desc', 'fieldtype', 'firstlayer', 'fragment', 'from_hexcap', 'get_field', 'getfield_and_val', 'getfieldval', 'getlayer', 'guess_payload_class', 'hashret', 'haslayer', 'hide_defaults', 'init_fields', 'initialized', 'lastlayer', 'libnet', 'lower_bonds', 'mysummary', 'name', 'overload_fields', 'overloaded_fields', 'packetfields', 'payload', 'payload_guess', 'pdfdump', 'post_build', 'post_dissect', 'post_dissection', 'post_transforms', 'pre_dissect', 'psdump', 'remove_payload', 'remove_underlayer', 'route', 'sent_time', 'setfieldval', 'show', 'show2', 'show_indent', 'sprintf', 'summary', 'time', 'underlayer', 'unwep', 'upper_bonds']
>>> p[0].time
>>> p[1].time

Great, each element in the list has an epoch timestamp associated with the packet as ".time". Next, we can use the Python sorted() method to sort the contents of the list using the packet timestamp as our sort criteria:

>>> o=sorted(p, key=lambda ts: ts.time)

The output "o" is our timestamp sorted packet capture. Now it's just a matter of saving the packet capture list to a new capture file:

>>> wrpcap("Challenge2-sorted-time.cap",o)

I've never had a reason to need to sort a packet capture by timestamp before, but it's nice to know that it only took a few minutes with Scapy and Python to make it happen.



Crafting Overlapping Fragments ….. Finally!

In the two posts leading up to this one, I discussed the theory of fragmentation and checksums . Now, we're finally ready to see the Scapy code that will craft the fragments show in the following diagram.

Because most of the complexity of crafting overlapping fragments is associated with fragmentation and checksum theory, the code is very simple.

We set some variables – our destination IP, and the payloads that will be found in the three different datagrams that will be sent. We craft the first IP header to contain the destination IP, ICMP as the protocol, an IP ID value of 12345, and the MF flag set. We craft an ICMP header for an ICMP echo request, and assign it the appropriate checksum value that we've already computed using techniques discussed in the 2nd post. We craft the packet consisting of an IP header, followed by the ICMP header, followed by the first payload of "AABBAABBAABB" and send it.

The second datagram has an IP header that is similar to the first; however, it contains a fragment offset of 1, or 8 bytes after the IP header. We attach what becomes the overlapping payload value of "BBAABBAABBAA" and send it. Finally, the third datagram IP header has an MF flag of 0 to indicate that this is the final fragment and give it a fragment offset of 2, or 16 bytes after the IP header. It has a payload value of "CCCCCCCC".

Now, let's execute the program and capture the traffic using tcpdump. Remember that if everything goes well, we should elicit an ICMP echo reply with a payload that reveals the favored fragment. Let's see the outcome. As you can see the receiver responds with a payload of "AABBAABBCCCCCCCC" in the ICMP echo reply, meaning that it favored the first fragment.

Remember that if you try this code, you may get a different response because different operating systems may favor the overlapping fragment. The test we just performed was with wholly overlapping fragments where the original and overlapping fragments began at the same fragment offset and were the same length. There are many more tests that are discussed in more detail in the "Target-based Fragmentation Reassembly"

Let's take one more example of overlapping fragments.

This time, the first datagram contains an IP header with the MF set and an ICMP header. The first fragment falls 16 bytes after the IP header, leaving an 8-byte gap between the end of the ICMP header and the fragment payload of "ABABABAB". We follow this with an overlap of the first fragment. But, this fragment payload begins before and ends after the first fragment payload. It fills in the missing 8-byte gap between end of the ICMP header and the beginning of the first fragment with a payload of "AAAAAAAA", overlaps the first fragment with a payload of "BABABABA", and has 8 additional bytes of "CCCCCCCC".

First, let's use interactive Scapy to craft the packet we're going to send and display the ICMP header to expose the ICMP checksum value we need to supply.

We see that we need an ICMP checksum value of 0xdce8. Here is the simple program to craft the overlaps.

Our first fragment is comprised of an IP header with the MF set, followed by the ICMP header with the correct ICMP checksum. The second fragment lies at an offset of 2, or 16 bytes after the IP header, with the MF set, and a payload of "ABABABAB". The third and final fragment begins at an offset of 1, or 8 bytes after the IP header (directly after the ICMP header), the MF is not set, with a payload of "AAAAAAAABABABABACCCCCCCC".

We see the receiver's ICMP echo response payload of "AAAAAAAABABABABACCCCCCCC", indicating it favored the overlapping fragment.

That wraps up this series on crafting overlapping fragments using Scapy. As you see, the code is fairly simple if you understand the concepts of fragmentation and checksums. Give it a shot and try these examples or other overlap combinations to see what responses you receive.