# Snort rule structure and syntax


A rule is a specified set of keywords and arguments used as matching criteria to identify security policy violations.
The engine compares packets against the conditions specified in each rule.
If the packet data matches all the conditions specified in a rule, the rule is triggered.
The action field in the rule specifies what should be done next.
Rules contain two logical sections: the rule header and the rule body.

The rule header

Identifies how to match traffic based on the following criteria:

- Action
- Protocol
- Source IP
- Source port
- Operator
- Destination IP
- Destination port

Note the usage of variables. Makes rule management easier.


The following actions are available in IDS mode:

- alert: Log the event and send an alert message to the output component.
- pass: Ignore the packet.

When your installation is deployed inline:

- Generate Events: Issues console alerts.
- Drop & Generate Events: Block and alert.
- Disabled: Prevents the rule from being enforced thus saving processing cycles.


The following protocols are supported:

- IP

Source and Destination IPs

In all cases, the best practice is to use variables.
The system recognizes only IP addresses and will not accept hostnames.
You cand specify:

- Any IP address, using the keyword 'any'.
- A single IP address.
- A list of IP addresses [,].
- A range of IP addresses
- The negation of IP addresses !
- A variable previously defined in the snort.conf file.

Source and Destination Ports

You cand specify:

- Any port, using the keyword 'any'.
- A single port.
- A list of ports [21, 23, 80-90]
- A range of ports 80-443.
- The negation of ports !80
- A variable previously defined in the snort.conf file.

Operator: specifying direction

To indicate that a rule triggers against traffic:

- From the source IP to the destination IP, use the directional operator '->'.
- Between the source IP and the destination IP, use the bi-directional operator '<>'.

The rule body

This is where you can specifically drill into a packet and get the content that actually signals malicious or suspicious activity.
The entire body is enclosed in parentheses.
Rule options end in a semicolon (;).
An option is structured as a keyword followed by a colon (:), followed by one or multiple arguments. Arguments are separated by commas (,).
For example: tag:session, 30, seconds;
Options that act on content, apply on the previous argument string of the previous content keyword.
For example: content:"root"; nocase;
A rule can contain multiple content references. They are treated as an AND operation.
For example: content:"username:"; nocase; content:"password:"; nocase;

Defining the event message
alert tcp any any -> $HOME_NET 23 \
(msg:"Suspicious Telnet!";
content:"root"; nocase; \
Content Matches

You can mix hexadecimal content and ASCII content.
content:"|90 90 90 E8 C0 FF FF FF|/bin/sh";
You can also negate or exclude content.

Constraining content matches

- nocase: To ignore the case in ASCII strings.
- offset: Allows you to specify where the detection engine should start searching for content within a packet (bytes).
- depth: The maximum search depth (bytes) from the offset value.
- distance: The detection engine starts searching for content matches after the specified number of bytes from the previous content match.
- within: The next content match must occur within the specified number of bytes after the end of the last content match including the distance value.

Server flow

- to_client or from_server: Triggers on server responses.
- to_server or from_client: Triggers on client responses.
- established: Triggers on established TCP connections.
- stateless: Triggers regardless of the state of the stream processor.
- no_stream: Does not trigger on rebuilt stream packets.
- only_stream: Triggers only on rebuilt stream packets.

Snort ID option

Snort rules all have unique ID numbers.
Custom rules should you a number greater than 1000000.

Rule revision number

Lets you assign a revision number to a rule that you have edited.


This is a required option for rules in the Sourcefire system.
You can embed additional information about a rule which other parts of the Snort engine can use.
For example:
metadata:service http;
metadata:impact_flag red;


To check if TCP flags are present:

1: Reserved bit 1
2: Reserved bit 2
0: No flags set

Use the following modifies to change the match criteria:

+: Match on the specified bits plus any others.
*: Match if any of the specified bits are set.
!: Match if the specified bits are not set.


The optional parameter indicates the flag bits you wish to ignore.


Sets the cursor used for detection to one of the following buffers:

- When traffic is HTTP:

1. HTTP response body, without chunking/compression/normalization.
2. HTTP de-chunked response body.
3. HTTP normalized response body, when normalized javascript is turned on.
4. HTTP UTF normalized response body, when normalized utf is turned on.
5. All of the above.

- When traffic is SMTP/POP/IMAP:

1. SMTP/POP/IMAP data body, including email headers and MIME when decoding is turned off.
2. Base64 decoded MIME attachment, when b64_decode_depth is greater than -1.
3. Non-Encoded MIME attachment, when bitenc_decode_depth is greater than -1.
4. Quoted-Printable decoded MIME attachment, when qp_decode_depth is greater than -1.
5. Unix-to-unix decoded attachment, when uu_decode_depth is greater than -1.


Replaces the threshold keyword in a rule.
It defines a rate which must be exceeded by source/destination before a rule can generate an event.
Is evaluated as part of the detection phase, just after pattern matching.


- track by_src or track by_dst.
- count: the number of events.
- seconds: the time over which the count is measured.

For example:

detection_filter: track by_src, count 10, seconds 30;

The alert will trigger after 10 events are seen from each unique source IP within 30 seconds.


By default, the longest content statement for each rule is placed into the fast pattern matching engine.
Only if this string is found in a packet, the remaining options in the rule are evaluated.
However, you have the ability to control which piece of content gets placed into the fast pattern matching engine with the fast_pattern rule option.

content:"jumanji"; fast_pattern;

fast_pattern:only: the content should only be used for fast pattern matcher and not evaluated as a content rule option.
fast_pattern:<offset>,<length>: specify where the fast pattern matcher should look for the content.


This rule option is used to set tags which can be referenced in subsequent rules.


- set: sets the specified state for the current flow.
- setx: sets the specified state exclusively.
- unset: unsets the specified states.
- toggle: inverts the specified state.
- isset: checks if the specified state is set.
- isnotset
- noalert: intructs to not generate an alert, regardless of the detection options.
- reset: resets the states on a given flow.

flowbits: [set|setx|unset|toggle|isset,isnotset,noalert,reset][,state_name][,group_name]

- state_name: the name that will be referred to in other rules when testing the flowbit.
- group_name: is used to associate multiple flowbits together.


Gives the ability to seek information within a packet payload where the location of content is not necessarily known but can be obtained.

- byte_jump: moves the inspection pointer a number of bytes forward.
- byte_test: reads bytes and determine if they are less than, greater than, equal or not equal to a specified value.
- byte_extract: reads bytes and create a variable that represents this value.

Case #1: Malformed GIF overflow

(msg:"FILE-IDENTIFY GIF file magic detected"; \
flow:to_client, established; \
file_data; \
content:"GIF8"; depth:4; fast_pattern; \
content:"a"; within:1; distance:1; \
flowbits:set,file.gif; flowbits:noalert; \
metadata:service http, service imap, service pop3; \
classtype:misc-activity; \
sid:20459; rev:8;)

file_data;, the search for this content starts at the beginning of the file buffer not the payload.
flowbits:set,file.gif;, the file.gif flowbit is set.
metadata:service http, service imap, service pop3;, using the Adaptive Profiles feature the rule is still being enforced even though the $FILE_DATA_PORTS variable does not include non-standard ports.

(msg:"FILE-IMAGE Oracle Java Virtual Machine malformed GIF buffer overflow attempt"; \
flow:to_client,established; \
flowbits:isset,file.gif; \
file_data; \
content:"GIF"; \
byte_test:1,!&,128,7,relative; \
content:","; within:1; distance:10; \
content:"|00 00|"; within:2; distance:4; \
metadata:policy security-ips drop, service http, service imap, service pop3; \
reference:bugtraq,22085; reference:cve,2007-0243; \
classtype:attempted-user; \
sid:10062; rev:9;)

flowbits:isset,file.gif;, checks if the file.gif flowbit is set.
byte_test:1,!&,128,7,relative;, reads 1 byte at a location 7 bytes away from the DOE's pointer current location. DOE means Detect Offset End. The byte_test returns True if the logical AND between the byte and the mask (10000000b ~ 128d) is equal to 00000000b ~ False => !(False) = True. After this operation, the DOE pointer remains in the same position.
content:","; within:1; distance:10;, from the "GIF" match, after 10 bytes and within 1 byte, the value should be a comma.
content:"|00 00|"; within:2; distance:4;, from the "," match, after 4 bytes and within 2 bytes, the value should be 0x00 0x00.

Case #2: Windows Help Center escape sequence vulnerability

alert  tcp $EXTERNAL_NET $HTTP_PORTS -> $HOME_NET any \
(msg:"OS_WINDOWS Microsoft Windows Help Center escape sequence XSS attempt"; \
flow:to_client,established; \
file_data; \
content:"hcp|3A 2F 2F|"; nocase; \
content:"script"; distance:0; nocase; \
content:"defer"; distance:0; nocase; \
pcre:"/hcp\x3a\x2f\x2f[^\n]*(\x3c|\x253c)script(\s|\x2520|\x2f)+defer/iO"; \
metadata:policy balanced-ips drop, policy security-ips drop, service http; \
reference:bugtraq,40725; \
reference:cve,2010-1885; \
reference:url,osvdb.org/show/osvdb/65264; \
reference:url,www.microsoft.com/technet/security/bulletin/MS10-042.mspx; \
classtype:attempted-user; \
sid:16665; rev:5;)

The distance:0 without a within, is simply telling the content match has to occur after the previous match, regardless where in the packet.
The use of three content matches before the pcre evaluation speeds up the rule.
hcp\x3a\x2f\x2f[^\n]*, search for hcp:// before no "new lines" characters until the next regex match.
(\x3c|\x253c)script, search for a "<" or a "%3c" before the "script" word.
(\s|\x2520|\x2f)+defer, search for at least one "whitespace" (space, tab, newline, formfeed, linebreak, ...) or a "%20" or a "/", before the "refer" word.
iO, two modifiers; i means case-insensitive and O forces to evaluate the regex, even if it is taking too long.

Case #3: The Kaminsky DNS vulnerability

alert udp $EXTERNAL_NET 53 -> $HOME_NET any \
(msg:"PROTOCOL-DNS large number of NXDOMAIN replies - possible DNS cache poisoning"; \
byte_test:1,&,2,3; \
byte_test:1,&,1,3; \ 
byte_test:1,&,128,2; \
detection_filter:track by_src, count 1200, seconds 30; \
metadata:service dns; \
reference:cve,2008-1447; \
reference:cve,2009-0233; \
reference:url,technet.microsoft.com/en-us/security/bulletin/MS09-008; \
reference:url,www.kb.cert.org/vuls/id/800113; \
classtype:misc-attack; \
sid:13948; rev:10;)

byte_test:1,&,2,3;, reads 1 byte at a location 3 bytes away and returns True if the logical AND between the byte and the mask (00000010b ~ 2d) is not equal to 0.
byte_test:1,&,1,3;, reads the previous byte again and returns True if the logical AND between the byte and the mask (00000001b ~ 1d) is not equal to 0.
byte_test:1,&,128,2;, reads 1 byte at a location 2 bytes away and returns True if the logical AND between the byte and the mask (10000000b ~ 128d) is not equal to 0.
detection_filter:track by_src, count 1200, seconds 30;, sets the threshold that must be met in order for the rule to trigger.

Case #4: The content option for overflow detection

alert tcp $EXTERNAL_NET any -> $HOME_NET 4000 \
(msg:"SERVER-OTHER Alt-N SecurityGateway username buffer overflow attempt"; \
flow:to_server, established; \
content:"username=",nocase; \
isdataat:450,relative; \
content:!"|3B|"; within:450; \
content:!"|3A|"; within:450; \
content:!"&"; within:450; \
content:!"|0A|"; within:450; \
metadata:service http;\
policy connectivity-ips drop, policy security-ips drop; \
reference:cve,2008-4193; reference:url,secunia.com/advisories/30497/; \
classtype:attempted-admin; \
sid:13916; rev:9;)

isdataat:450,relative;, verifies if there is 450 bytes after the end of the "username=" string.
Next, the engine verifies there is not a colon, semicolon, ampersand and linefeed within 450 bytes of the end of the "username=" string.



No comments: