The content keyword is one of the more important features of Snort. It allows the user to set rules that search for specific content in the packet payload and trigger response based on that data. Whenever a content option pattern match is performed, the Boyer-Moore pattern match function is called and the (rather computationally expensive) test is performed against the packet contents. If data exactly matching the argument data string is contained anywhere within the packet's payload, the test is successful and the remainder of the rule option tests are performed. Be aware that this test is case sensitive.
The option data for the content keyword is somewhat complex; it can
contain mixed text and binary data. The binary data is generally enclosed
within the pipe () character and represented
as bytecode. Bytecode represents binary data as hexadecimal numbers
and is a good shorthand method for describing complex binary data.
Figure
contains an example of mixed text
and binary data in a Snort rule.
Note that multiple content rules can be specified in one rule. This allows rules to be tailored for less false positives.
If the rule is preceded by a !, the alert will be triggered on packets that do not contain this content. This is useful when writing rules that want to alert on packets that do not match a certain pattern
NOTE
content: [!] "<content string>";
The content keyword has a number of modifier keywords. The modifier keywords change how the previously specified content works. These modifier keywords are:
The nocase keyword allows the rule writer to specify that the Snort should look for the specific pattern, ignoring case. nocase modifies the previous 'content' keyword in the rule.
nocase;
The rawbytes keyword allows rules to look at the raw packet data, ignoring any
decoding that was done by preprocessors. This acts as a modifier to the
previous content option.
rawbytes;
This example tells the content pattern matcher to look at the raw traffic, instead of the decoded traffic provided by the telnet decoder.
alert tcp any any -> any 21 (msg: "Telnet NOP"; content: "|FF F1|"; rawbytes;)
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 look for the specified pattern within the first 5 bytes of the payload.
As the depth keyword is a modifier to the previous `content' keyword, there must be a content in the rule before `depth' is specified.
See Figure for an example of a
combined content, offset, and depth search rule.
depth: <number>;
The offset keyword allows the rule writer to specify where to start searching for a pattern within a packet. offset modifies the previous 'content' keyword in the rule.
An offset of 5 would tell Snort to start looking for the specified pattern after the first 5 bytes of the payload.
As this keyword is a modifier to the previous 'content' keyword, there must be a content in the rule before 'offset' is specified.
See Figure for an example of a
combined content, offset, and depth search rule.
offset: <number>;
![]() |
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.
This can be thought of as exactly the same thing as depth (See Section
), except it is relative to the end of the last pattern match
instead of the beginning of the packet.
distance: <byte count>;
The rule listed in Figure maps to a regular
expression of /ABCDE.{1}EFGH/.
The within keyword is a content modifier that makes sure that
at most N bytes are between pattern matches using the Content ( See
Section ). It's designed to be used in conjunction
with the distance (Section
) rule option.
The rule listed in Figure constrains the search to not
go past 10 bytes past the ABCDE match.
within: <byte count>;
The uricontent parameter in the Snort rule language searches the NORMALIZED request URI field. This means that if you are writing rules that include things that are normalized, such as %2f or directory traversals, these rules will not alert. The reason is that the things you are looking for are normalized out of the URI buffer.
For example, the URI:
/scripts/..%c0%af../winnt/system32/cmd.exe?/c+ver\end{verbatim} will get normalized into: \begin{verbatim}/winnt/system32/cmd.exe?/c+ver Another example, the URI: \begin{verbatim} /cgi-bin/aaaaaaaaaaaaaaaaaaaaaaaaaa/..%252fp%68f? \end{verbatim} will get normalized into: \begin{verbatim}/cgi-bin/phf?
When writing a uricontent rule, write the content that you want to find in the context that the URI will be normalized. For example, if Snort normalizes directory traversals, do not include directory traversals.
You can write rules that look for the non-normalized content by using the content option. (See Section )
For a description of the parameters to this function, see the content rule
options in Section .
This option works in conjunction with the HTTP Inspect preprocessor specified
in Section .
uricontent:[!]<content string>;
Verify that the payload has data at a specified location, optionally looking for data relative to the end of the previous content match.
isdataat:<int>[,relative];
alert tcp any any -> any 111 (content:"PASS"; isdataat:50,relative; \ content:!"|0a|"; distance:0;)This rule looks for the string PASS exists in the packet, then verifies there is at least 50 bytes after the end of the string PASS, then verifies that there is not a newline character within 50 bytes of the end of the PASS string.
pcre:[!]"(/<regex>/|m<delim><regex><delim>)[ismxAEGRUB]";The post-re modifiers set compile time flags for the regular expression.
i | case insensitive |
s | include newlines in the dot metacharacter |
m | By default, the string is treated as one big line of characters. ^ and $ match at the beginning and ending of the string. When m is set, ^ and $ match immediately following or immediately before any newline in the buffer, as well as the very start and very end of the buffer. |
x | whitespace data characters in the pattern are ignored except when escaped or inside a character class |
A | the pattern must match only at the start of the buffer (same as ^ ) |
E | Set $ to match only at the end of the subject string. Without E, $ also matches immediately before the final character if it is a newline (but not before any other newlines). |
G | Inverts the "greediness" of the quantifiers so that they are not greedy by default, but become greedy if followed by "?". |
R | Match relative to the end of the last pattern match. (Similar to distance:0;) |
U | Match the decoded URI buffers (Similar to uricontent) |
B | Do not use the decoded buffers (Similar to rawbytes) |
The modifiers R and B should not be used together.
This example performs a case-insensitive search for the string BLAH in the payload.
alert ip any any -> any any (pcre:"/BLAH/i";)
Test a byte field against a specific value (with operator). Capable of testing binary values or converting representative byte strings to their binary equivalent and testing them.
For a more detailed explanation, please read Section .
byte_test: <bytes to convert>, [!]<operator>, <value>, <offset> \ [,relative] [,<endian>] [,<number type>, string];
Option | Description |
bytes_to_convert | Number of bytes to pick up from the packet |
operator | Operation to perform to test the value:
|
value | Value to test the converted value against |
offset | Number of bytes into the payload to start processing |
relative | Use an offset relative to last pattern match |
endian | Endian type of the number being read:
|
string | Data is stored in string format in packet |
number type | Type of number being read:
|
Any of the operators can also include ! to check if the operator is not true. If ! is specified without an operator, then the operator is set to =.
NOTE
The byte_jump option allows rules to be written for length encoded protocols trivially. By having an option that reads the length of a portion of data, then skips that far forward in the packet, rules can be written that skip over specific portions of length-encoded protocols and perform detection in very specific locations.
The byte_jump option does this by reading some number of bytes, convert them to their numeric representation, move that many bytes forward and set a pointer for later detection. This pointer is known as the detect offset end pointer, or doe_ptr.
For a more detailed explanation, please read Section .
byte_jump: <bytes_to_convert>, <offset> \ [,relative] [,multiplier <multiplier value>] [,big] [,little][,string]\ [,hex] [,dec] [,oct] [,align] [,from_beginning];
Option | Description |
bytes_to_convert | Number of bytes to pick up from the packet |
offset | Number of bytes into the payload to start processing |
relative | Use an offset relative to last pattern match |
multiplier ![]() ![]() |
Multiply the number of calculated bytes by
![]() ![]() |
big | Process data as big endian (default) |
little | Process data as little endian |
string | Data is stored in string format in packet |
hex | Converted string data is represented in hexadecimal |
dec | Converted string data is represented in decimal |
oct | Converted string data is represented in octal |
align | Round the number of converted bytes up to the next 32-bit boundary |
from_beginning | Skip forward from the beginning of the packet payload instead of from the current position in the packet. |
The regex keyword has been superceded by PCRE. See Section .
The content-list keyword is broken and should not be used.