IDS/IPS Evasion - Step 1. Awareness

Recently, I've been on a campaign to make people aware of TCP evasions of IDS/IPS solutions. Suppose that you could find a way to make an IDS/IPS believe that a TCP session has closed when it really hasn't. After you dupe the IDS/IPS into no longer tracking the session, you send your exploit and it goes undetected.

Sound difficult? Well, it really is not. There are a lot of nuances when dealing with a protocol as complex as TCP. And, many of these nuances are different interpretations made by different OS software. For instance, if you have wholly overlapping TCP seqments where both segments begin on the same sequence number and end on identical sequence numbers, but the payload is different which segment does the destination OS favor and just as important - does the IDS/IPS make the same choice?

I've got an interesting issue I found when doing research at Sourcefire years ago. This is a "nuance" that may not occur with all operating systems, but occurs when Linux runs on the destination host. And, it involves a RESET that appears to close the session, but really doesn't.

First let's take a look at some tcpdump output where I'm crafting the client side of the connection using a wonderful packet crafting tool called Scapy that uses the Python interpreter. The packets I crafted are in yellow. I'll cover how I crafted the session using Scapy in an upcoming blog. The three-way handshake starts on packet 1 where I craft the SYN. The server responds with the conventional SYN/ACK in packet 2. And, then in packet 3, I accidentally ACK the server with a value one more than the expected value. It isn't obvious, but the relative value
ack 2 in tcpdump parlance should have been ack 1 if I got it right.

Now, here's where the bizarre stuff happens. In packet 4, the server resets the connection because of the invalid ack number. Fair enough. But, in packet 5, I send 10 bytes of data "GET /EVIL" and I assign a correct acknowledgement number as tcpdump indicates with
ack 1. Now look at the server's response in packet 6. It acknowledges the 10 bytes of data! How is that possible - didn't it reset the connection? And, in packet 7, I send more data and the server acknowledges this in packet 8.

1. > Flags [S], seq 10, win 8192
2. > Flags [S.], seq 3506022755, ack 11, win 5840, options [mss 1460]
3. > Flags [.], ack 2, win 8192
4. > Flags [R], seq 3506022757, win 0
5. > Flags [P.], seq 1:10, ack 1, win 8192
6. > Flags [.], ack 10, win 5840
7. > Flags [P.], seq 10:51, ack 1, win 8192
STUFF HTTP/1.1\r\nHost:www.whatever.com\r\n\r\n
8. > Flags [.], ack 51, win 5840
9. > Flags [R.], seq 51, ack 1, win 8192

You're probably thinking what's going on. Before I explain what I believe occurred, let's look at the implications of what just happened. You'd think that when an IDS/IPS saw the reset, it would stop tracking the session. And, let's say that the string "EVILSTUFF" represents, well - evil stuff. It's in two separate segments requiring reassembly so it's fairly safe to say most IDS/IPS solutions would miss this.

Okay, as for the explanation. My thought is that the server considered the third packet with the bad acknowledgement number a "foreign" or "rogue" segment. It did not consider it a valid segment of the forming session establishment. If that doesn't make sense, what happens if you try to send a PUSH packet with data to a session that hasn't been established? The server resets it. And, I believe that since this was not yet an established session, the server considered the segment with an invalid ACK number as foreign to any session it knew.

This highlights a couple of things. First, it's not hard to cause TCP evasions for an IDS/IPS. And second, there are issues that affect specific operating systems only. Servers on other operating systems than Linux that receive the bad client acknowledgement number may continue to send the SYN/ACK until a valid one arrives. It is extremely difficult for an IDS/IPS solution to have knowledge of all these OS-specific TCP issues and deal with them properly. And, that is why TCP evasions are possible.