The five modifiers that I am talking about are
content". The content keyword is one of the easiest pieces of the Snort rules language as all it does is look for a particular string. So for instance if I wanted to look for the word "joel" within a packet. A simple:
Would allow me to do that. The interesting part comes into play when you want to specify where inside of a particular packet you want the string "joel" to be looked for. If you are running just a plain content match with a simple string, and not specifying where in the packet to look for that string, your Snort instance will receive a ton of alerts, and then you, the analyst, are stuck looking through all of those alerts to try and pick out the alert that is needed. While a content match for "joel" might be pretty unique (that might not occur a lot on your network), but it will occur a bunch on mine.
The offset keyword allows the rule writer to specify where to start searching for a pattern within a packet.So, given a certain packet, Offset tells the content match it's modifying where to start looking, given an offset from the beginning of the data payload of the packet.
In the above example, if I wanted to find the word "GET" (highlighted). I would write:
Meaning, start at the beginning of the data payload of the packet (offset:0;) and find the word GET. Now, in this example, the word "GET" is at the very beginning of the packet making the search very easy. However, if I wanted to match on the word "downloads" that is found a bit later in the above screenshot, I could still start my content match at the beginning of the payload (offset:0;) but the content match would be more accurate and less computationally expensive if I were to make the offset more accurate.
Would tell Snort to start looking for the word "downloads" at the 13th byte in the data portion of the packet. So, what if I chained these two together?
In other words, start looking for "GET" at the beginning of the data payload of the packet, and start looking for the word "downloads" at the 13th byte of the packet. Now, why would I do this? This example tells Snort, after the first content match, go back to the beginning of the packet, move over 13 bytes and then start looking again for a second content match. There are several things wrong with this example, that I did on purpose. First off, if you are at the first content match in a Snort rule, or a content match you want to start at the beginning of the packet, you don't have to write "offset:0;". Any content match that doesn't have a modifier after it automatically starts at the beginning of the data payload portion of the packet by default. Offset:0; is implied for this type of match. Second, and a:
content:"GET"; offset:0; content:"downloads"; offset:13;
Some tend to think that if they stack two contents next to each other, that Snort will look for those contents in the order they are provided. For example, if I were to write:
Some people generally think that in the above example, that the word "downloads" will have to occur after the word "GET" in the packet. This is Wrong. If no modifiers to contents are specified than the order of the matches within a given packet (or stream for that matter) doesn't matter. "downloads" could be first, then "GET", and the rule will still fire. So given the above exampled screenshot, if I wanted to force the word "downloads" to occur after the word "GET". I could use a distance modifier. Which I will touch on a bit later.
Depth in the Snort manual is defined as:
The depth keyword allows the rule writer to specify how far into a packet Snort should search for the specified pattern from a given offset.So, given the above example again:
I want to match on "GET" but ONLY if it occurs as the beginning of the packet. Notice when I was describing offset above I said that offset tells Snort where to start looking. Not where to stop. If I don't tell Snort where to stop using a content match, Snort will search the entire packet. If I want to tell Snort where to stop looking for a content match, I have to use something like depth. So for the above example, if I want to match on "GET" but only at the beginning of the data portion of the payload:
Notice some things.
- I didn't start with Offset:0;. Remember, if I am beginning a content search at the beginning of the data payload of the packet, offset:0; is implied.
- Depth counts in positive integers. While offset starts counting at "0" bytes, depth counts in positive integers, "GET" is three bytes long, so my depth is "3".
- Depth starts counting from the offset point. Not from the beginning of the packet. While, in the above "GET" example, the offset point IS the beginning of the packet, don't get confused by this.
- By telling Snort to only look in the first three bytes, if Snort is analyzing millions of 1500 byte packets, only matching on the first three bytes is a significant CPU saver.
- BTW -- Don't do the above example, as you will essentially match on every single GET request on your network, turning your IDS into a brick. This is just an example. Besides this is what
http_methodis for, which i'll cover in a later blog post.
Distance is defined in the Snort manual as:
The distance keyword allows the rule writer to specify how far into a packet Snort should ignore before starting to search for the specified pattern relative to the end of the previous pattern match.(Emphasis added by me) Distance says to us, "okay, relative to the end of the previous content match, when should I start searching for the second content match?". So bringing back my previous example:
If I were to do this:
content:"GET"; depth:3; content:"downloads";
That by itself would force the content match "downloads" to occur after the "GET" content match. Doesn't matter where (distance:0;), just as long as the pattern match is AFTER the first one. However, if I wanted to be more specific and more specifically match on the screenshot that I provided above:
content:"GET"; depth:3; content:"downloads"; distance:0;
This says to the Snort engine, "match on GET, in the first 3 bytes of the data payload of the packet, then move 10 bytes relative to the end of GET and start looking for "downloads"". Notice I said start looking. Not limited to. Kinda like putting an offset without a depth there... so we have within.
content:"GET"; depth:3; content:"downloads"; distance:10;
Within in described in the Snort Manual as:
The within keyword is a content modifier that makes sure that at most N bytes are between pattern matches using the content keyword.Within allows you to specify a range between content matches, it also allows you to tell a second (relative) content match where to stop.
So, using the content matches we've built already:
The only problem here is "downloads" is being searched for in the entire packet, except for the first 13 bytes, essentially. How can we make downloads only be searched for at that specific spot? Within.
content:"GET"; depth:3; content:"downloads"; distance:10;
"Match on GET, in the first 3 bytes of the data payload of the packet, then move 10 bytes relative to the end of GET and start looking for "downloads", however, "downloads" must occur wholly within the next 9 bytes." Could I say "within:10;"? Yes, I could, and then downloads could be in it's present position, or if there was another byte in front of the actual content match. Also notice that within, like depth, also works in positive integers (distance starts counting at "1")
content:"GET"; depth:3; content:"downloads"; distance:10; within:9;
Finally, let me discuss "nocase";. nocase, or "No case" simply means, for the content match specified, do not pay attention to case sensitivity. "nocase" doesn't make the Snort engine work any harder in the grand scheme of things, and it's very handy for being able to make sure your rules do not get bypassed. Example? Let's say I wanted to match the above screenshot, no matter what. Well, if I was an attacker, and I came to your webserver trying to access your "downloads" directory, as the rule is written, I could pass my "GET" string as lowercase "get" or mixed case "GeT", and depending upon your webserver, it might accept it, and I have effectively bypassed your rule. The easiest thing to do with this type of evasion is to use a nocase; statement.
So, I want you to notice a few things:
content:"GET"; depth:3; nocase; content:"downloads"; distance:10; within:9; nocase;
- We went from very generic to very specific, your use case will vary.
- Modifiers to contents come AFTER the content match. Not before, they won't work, don't try it.
- Offset goes with Depth, distance goes with within. Don't mix them.
SNORT and Sourcefire are registered trademarks of Sourcefire, Inc.