One of the many things that occupy the time of the VRT is reviewing rule performance data, whether that data is internally generated from one of our test environments or received from customer reports. In the “Rule Performance” series of blog posts, we’ll look at the set of issues that encompass the problematic rule constructs that we’ve found most significantly impact the performance of Snort sensors. Hopefully you can use this information to add additional detection capability customized to your environment without adding undue processing load.

The first and perhaps most important common error is a lack of a long, unique content match. As discussed in the “Writing Effective Rules, Part 1” webcast ( Snort takes the first, longest content match in a rule, and places it in the appropriate fast-pattern matcher. Here is an example of how it works:

alert tcp $EXTERNAL_NET $HTTP_PORTS -> $HOME_NET any (msg:"WEB-CLIENT VML fill method overflow attempt";flow:from_server,established; content:"|3A|fill"; nocase;pcre:"/<\w+\x3afill\s[^>]*method\s*=\s*(\x27[^\x27]{32}|\x22[^\x22]{32}|[^\s>]{32})/smi";metadata:policy balanced-ips drop, policy connectivity-ips drop, policy security-ips drop;reference:bugtraq,20096; reference:cve,2006-4868;reference:url,;classtype:attempted-user; sid:8416; rev:5;)

This rule detects a 2006 vulnerability in Microsoft’s Vector Graphics Rendering engine. Notice that there is a content match for “|3A|fill”, but that string also appears in the PCRE that carries the actual detection methodology. The reason for this is to support the fast-pattern matcher and to reduce entry into the PCRE engine. When Snort loads, it will place the content “|3A|fill” into the fast pattern tree that runs on the TCP ports that in HTTP_PORTS. What this means is that only packets on one of the HTTP_PORTS that contain the pattern “|3A|fill” will enter into the rule chain to evaluate SID:8416. If the packet doesn’t contain this content, and thus can never alert, it will never evaluate against the rule.

Once the fast pattern matcher passes the packet to the detection engine, which executes the rule detection in the order the rule is written. So, after ensuring that the packet is part of an established stream, and that data is being passed to the client, the detection engine checks for “|3A|fill” (regardless of case), and then enters into the PCRE detection.

If we didn’t have the content match, what would happen? With no content match in the pattern engine, the rule would fall into an all-packet buffer where every packet that was on the HTTP_PORTS set would be evaluated against it. Without the content match to ensure that the packet had to have the “|3A|fill” pattern, every packet on the HTTP_PORTS set that was part of an established session would enter the PCRE detection engine. The PCRE engine is very useful, but we don’t want to pay the penalty of its use without ensuring we have a chance to detect.

One recent addition to Snort was the “fast_pattern” keyword. By default, the fast pattern matcher would use the first, longest content match in a rule. But sometimes that isn’t the most useful match. Consider the following fictional rule:

alert tcp $HOME_NET 300 -> $EXTERNAL_NET (msg:”SPECIFIC-THREAT Oh noes”;flow: to_client, established;content:”|00 00 00 00 00 00 00 00|”; content:”BISCUIT”; sid: 5;)

Because 8 nulls is longer than the phrase “BISCUIT!”, the Snort engine would take the NULLs and place them into the fast pattern matcher. But anyone who has done a stint as an analyst knows that a string of NULLs is not an uncommon occurrence in network traffic, and that “BISCUIT” is much more unique. We will enter this rules detection less often by forcing Snort to use the more unique of the content matches as the fast pattern entry:

alert tcp $HOME_NET 300 -> $EXTERNAL_NET (msg:”SPECIFIC-THREAT Oh noes”;flow: to_client, established;content:”|00 00 00 00 00 00 00 00|”; content:”BISCUIT”; fast_pattern; sid: 5;)

So, the lowly, basic content match has an enormous impact on the performance of a rule. When building custom detection for your environment, remember:

  1. Use a long, unique content match to ensure that your rule is only triggered when there is a chance it will fire.
  2. If you find a smaller content match that you feel is more unique and will cause the rule detection to be called less frequently, use the fast_pattern keyword to force that content match to be used in the fast pattern matcher.
  3. Ensure that you order your rule options so that faster operations are at the beginning. We’ll cover this more next time, but in general, run your content matches prior to your PCREs.

    Hope that helps! If you have questions, leave a comment, and we’ll get back to you!