Bypassing IDS/IPS Signatures

In this post we’ll see issues found regularly with Snort signatures. First, groups making signatures will be categorized, then problems amongst these writers will be explored, and finally more interesting signature bypass techniques will be discussed. All of the examples will be from real signatures still in use. The end goal is to get people thinking differently about how they write signatures and/or how to personally verify inappropriate detection.
*Please note that multiple bypasses will be seen per signature, but we will cover just one.

The Attack
The initial analysis will be done using signatures for a popular denial of service attack in Apache that dealt with overlapping byte ranges (CVE-2011-3192) [1]. When Apache saw the Request or Request-Range HTTP headers each range was treated as a new request. Memory was thusly allocated for each request and responded with the range of the requested file. If hundreds of packets were sent containing thousands of ranges then memory usage shot through the roof at an average consumption rate of 1gb/10 seconds. Example attack:
GET /stun.png HTTP/1.1
Range: bytes=0-,2-10,2-11,2-12,2-13,2-14,2-15,… (0- means 0 to EOF)

Amateur Signatures
The amateur signature group is comprised of people who may write signatures as a small part of their job or are new to signatures in general. This covers the busy SOC/NOC (security operations/network operations) who need to provide coverage for a vulnerability/exploit as quick as possible to appease clients. You hear a lot about SOC woes from companies and that’s because there’s so much different work to do it’s hard to focus on one thing. Because of this the vulnerability is generally not understood enough or at all and we find most of these signatures built around the public exploit more than the vulnerability. The example signatures were pulled from a public security mailing list.
Amateur Signature #1
Signature: Content:”Range:bytes=0-”; threshold:type threshold, track by_src, count 5, seconds 20;
Explanation: Within 20 seconds, if 5 packets are seen containing “Range:Bytes=0-” in the payload, then fire.
There are a couple of issues with this signature. Even our initial example attack will bypass this signature, however, let’s focus on the bytes=0-. All the public exploits started with this value, but that did not have to be the case. Instead it could start with 1- and continue on. This signature did not take into account any kind of exploit variation and was built solely around the public exploits. Bypass attack:
GET /stun.png HTTP/1.1
Range: bytes=1-,0-,2-10,2-11,2-12,2-13,2-14…
Amateur Signature #2
Signature: content:”Range”; nocase; http_header; pcre:”/(d,){6,}/xH”;
Explanation: If “Range” is seen anywhere in a http header, then check if a digit followed by a comma is repeated six or more times sequentially.
If you know the attack and PCRE then this one should be easy to spot. The issue lies in an invalid vulnerability check. This regex will never actually fire on the real attack beacuse it doesn’t check for a range like 2-15, only comma seperated numbers such as 2,3,4. This problem exists in a decent amount of public and commercial signatures. Some of these in-use signatures can’t even detect the attack. One can comprise several lists of such problematic signatures by looking for common PoC examples like “|41 41 41 41|” and “|61 61 61 61|”. For a more interesting search string to find these issues, please see the defender conclusion section.
Amateur Signature #3
Signature: pcre: “/^Range:bytess+d+-d?,d+-d+,d+-d+,d+-d+,d+-d+,d+-d+,/xH”;
Explanation: If the start of the normalized HTTP header (H) starts with and contains Range:bytes followed by one or more whitespace characters, one or more digits (+), a dash, zero or more digits (?), and a comma six time sequentually then fire. This rule also ignores extra whitespace characters (x).
This is a well thought out signature, unfortunately they missed a very important piece of the puzzle. First off, let’s describe normalize to clear any confusion:
“Match normalized HTTP request or HTTP response header (Similar to http header). This modifier is not allowed with the unnormalized HTTP request or HTTP response header modifier(D) for the same content. For SIP message, match SIP header for request or response (Similar to sip header).” [2]
The issue is subtle but deadly. While this signature is great, it forgets about case sensitivity. Normalize currently does not normalize to a case insensitive string and no /i was provided in the regex for such. To bypass this all that’s needed is to mix up some casing in a valid manner. New attack:
GET /stun.png HTTP/1.1
RANGE: bytes=0-,2-10,2-11,2-12,2-13…
*Humour: “Remember kids, capititalization is the difference between helping your uncle jack off a horse and your uncle Jack off a horse.”

Professional Public Signatures
This category made up of groups that release signatures for free. The issue is the same as the last group, but different. It boils down to not having time to analyze a
vulnerability in-depth as it’s a quantity arms race. How many vulnerabilities/exploits are released daily that need coverage? There isn’t a large enough time window to get deep into analyzing or understanding certain vulnerabilities on a fundamental level. Again, these were pulled from currently used signatures released by such a popular free signature group.
Professional Public Signature #1
Signature: content:”Range|3a|bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14r43;; http_header;
Explanation: If the specified content (|3a| is hex for colon) is seen inside of an http_header, then fire.
I bring this to your attention as it is a perfect example that signature experience or a professional name does not automatically make the rules better than amateur signatures. The issue here is an even more specific exploit check than the amateur signatures. Again, simply modifying the byte ranges to use, for example, 2- instead of 5- will bypass this signature.
Professional Public Signature #2
Signature: content:”Range|3a|”; nocase; http_header; content:”bytes=”; http_header; nocase; distance:0; isdataat:10,relative; content:”,”; http_header; within:11; isdataat:10,relative; content:”,”; http_header; within:11; isdataat:10,relative; content:”,”; http_header; within:11; isdataat:70,relative; content:!”|0d 0a|”; within:12; pcre:”/Rangex3as?bytes=[-0-9,x20]{100,}/iH”;
Explanation: Certainly a mouthful of a signature, but I’ll try to make it as simple as possible to follow. If Range: is seen in an http header, bytes= somewhere after last match, a comma within 11 bytes of the last match for three consecutive times, and CRLF (end of HTTP header) is not seen within 12 bytes of last match, then check for 100 of the given characters (dash, 0-9, comma, or a space) with case insensitive and HTTP normalizer modifiers.
Now we’re starting to see some professional rule optimizations. This will cripple other professional signatures soon enough. However, the issue here is that the PCRE is not quiet complete. It’s interesting that this displays an inverse issue as seen in amateur signature #3. What the PCRE does not do is ignore whitespace (x). Because of this there exists a mistake with the PCRE whitespace check (Rangex3as?bytes=). We know that ? means 0 or 1 matches, but it does not match continuous whitespaces. So it’s possible to insert lots of extra whitespaces validly between Range: and bytes to bypass the signature. So close, yet so far! New attack:
GET /stun.png HTTP/1.1
Range: bytes=0-,2-10,2-11,2-12,2-13,2-14,2-15…
Professional Commercial Signatures
These are created by companies who sell rules as a commodity. They have more focus on optimization tactics and, as usual, do not have time to analyze vulnerabilities in-depth. There’s interesting issues at play inherently with commercial rules. The more rules that are added, the more a sensor has to work when analyzing a single packet. The more rules enabled, the more it has to analyse and the higher risk you have for packet drop on analysis. Due to this, many optimization measures, like byte offsets, are used so the check can fail as quick as possible and continue with other checks. Commercial signatures attempt to utilize this so companies can run as many rules as possible and not have missing packet checks due to drop rates. Our example company created three different rules to detect for this attack. One of them we’ve shown a technique earlier that can be used to bypass the check (I’ll offer that as a challenge/exercise for the reader if they’re interested). The other two are practically the same signature but a different http method and less of a content check. One of these two will be analysed below.
Professional Commercial Signature #1
Signature: content:”HEAD”; nocase; http_method; content:”Range|3A|bytes|3D|0-|2C|”; nocase; http_header;
Explanation: If the HTTP method used is HEAD and content in a HTTP header contains Range:bytes=0-, then fire.
Here’s where we start failing checks early on in the packet. This signature looks for the HEAD method. While several public exploits used HEAD so they did not get large amounts of data returned back to them, other methods can be used and just dropped with firewall rules. They did include another signature that detected for POST, but not one that checked for GET. We will get more into how the detection of GET often works to our favor later on. To bypass this one nothing needs be changed from our starting attack example.
Generic Bypass Techniques
As previously mentioned, rules need to be as optimized as possible when more get introduced to the ruleset and/or environment. In this way the possibility of having drop rates is reduced while being able to check for every attack we want. We have content modifiers like depth, within, offset, distance, http_method, and etc. that validate if content is located at certain bytes of the payload. Unfortunately for the defender this turns out to be a problem due to a lack of understanding with the protocol instead of the vulnerability itself. We will start using random examples found in current public and commercial professional signatures in use today instead of the Apache DoS.
“The depth keyword allows the rule writer to specify how far into a packet Snort should search for the specified pattern. depth modifies the previous ‘content’ keyword in the rule. A depth of 5 would tell Snort to only look for the specified pattern within the first 5 bytes of the payload.” [2]
Signature: content:”GET AAAAAAAAAAAAAAAAAAAAA”; depth:25;
Explanation: If GET AAAAAAAAAAAAAAAAAAAAA is seen within the first 25 bytes of the http payload then fire.
Aside from the obvious PoC specific check for this attack, there is something much more interesting we can use to bypass this signature. It comes from an understanding of the HTTP protocol. As this only checks 25 bytes in, if we put the content beyond this range, the check is bypassed. We could either insert an arbitrary number of spaces between the GET and A’s. Example:
GET A’s HTTP/1.1
Or even more fun we can insert an arbitrary amount of CRLF characters before the actual GET request. Example:
rnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnrnGET A’s HTTP/1.1
“The within keyword is a content modifier that makes sure that at most N bytes are between pattern matches using the content keyword (See Section 3.5.1). It’s designed to be used in conjunction with the distance (Section 3.5.6) rule option.” [2]
Signature: content:””; within:100;
Explanation: Within shows the same issue as depth does. If the “/ur.php>” is seen within 100 bytes of the original match then fire.
This is a simple bypass. If we were to insert more than 100 characters between the domain name and /ur.php, we are home free. Example:
“The http_method keyword is a content modifier that restricts the search to the extracted Method from a HTTP client request.” [2]
Signature: content:”GET”; http_method; content:”.php?”; http_uri; content:”x=”; http_uri; content:”&u=”; http_uri; content:”&s=”; http_uri; content:”&t=”; http_uri; content:”&java”; http_uri; content:”&pdf=”; http_uri; content:”&flash=”; content:”&qt=”; http_uri;
Explanation: If the GET http method is seen and the rest of the content are see in the HTTP URI, then fire.
This is one of my favorite types of bypasses because many rules are susceptible. According to the HTTP RFC, a HEAD request performs the action of the GET request, but only header responses are reported back. Or, as per RFC:
“The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response.”
So if we switch our request over to HEAD, we still report to the web server with the information (example rule checks for trojan checkin), receive no data in the body as a response, and effectively bypass the signature. Example:
HEAD /checkin.php?x=yes&u=Mozilla&s=Windows&t=12072012&java=1&pdf=0&flash=1&qt=1
*URI values made up and does not represent accurately the report variable content

Offensive Conclusion
It is not necessary to know the signature to know how to mutate the exploit code for a vulnerability. These examples show that you need to modify the exploit code away from any public code as much as possible. The most important part is understanding the protocol the exploit is going to be delivered on. You can start padding possible places to store extra bytes to see what available byte or bytes can be utilized without disturbing the exploit itself. This is what I did before Zalewski’s book “The Tangled Web” [4] came out which created a cheatsheet for acceptable HTTP padding on different web servers.
Defensive Conclusion
Similiar to the offensive conclusion, a signature writer needs to understand protocols as much as vulnerabilities. Without that knowledge, an actual attacker can use simple mutations for exploits to fly under the IDS/IPS radar. The writer also needs to be wary about byte offset detection unless they know for sure data can not be inserted between the offset and content match, for example a known file format struct. If you are just auditing your own rules, please look for as many proof of concept signatures as you can to see if you really have detection for certain vulnerabilities. grep -iE “(61 61 61 61)|(41 41 41 41)|(aaaa)” -r /location/to/rules/ will give you a decently sized list to start finding issues with. Not all of them will be falses, though, a better query can be made by knowing your rulesets and what may generically be accurate detection (malware-cnc).
Other Bypasses
If you want to check out a list of current generic network-level bypass tricks, please take a look at the AET project at [3]
[1] – Original Apache Overlapping Range Attack Original Public Exploit Code
[2] – Online Snort Manual
[3] – Network-level bypass list
[4] – Michal Zalewski – The Tangled Web

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s