Writing Snort Signatures
Last updated
Last updated
It is possible to use a signature language called Snort to create or download custom signatures to be enforced by CloudGuard WAF. For exact details how to configure:
There are many guides and video tutorials online on how to write Snort rules, and you can read the full documentation in https://www.snort.org/.
This page will provide a short guide on top of those links.
Each Snort rule is written in a single line and is made up of two parts: the header and the keywords.
The header section has a fixed format made up of seven distinct elements, and answers the questions: What action to take (detect or drop), and on which connections (remember that Snort was originally conceived as a layer 3 IDS) is should apply to.
The keywords section is made of parenthesis that holds a variable set of distinct instructions called keywords, and each keyword is terminated by a semi-colon.
The Snort header is made of seven parts:
Action.
Protocol.
IP address or addresses.
Port number or set of numbers.
Direction operator.
IP address or addresses.
Port number or set of numbers.
The header is intended to answer the questions: What action to take (detect or drop), and on which connections (remember that Snort was originally conceived as a layer 3 IDS) it should apply to. However, in our setting these definitions will be overridden by what the user definitions in the system.
Since that is the case, the simplest thing to do is just copy a standard header for any rule that you write, like this one:
The Snort keywords section contains a variable number of keywords - each one of them represent one "thing" that the rule should do.
We can divide the keywords into three categories:
Metadata keywords - these keywords provides general information on the rule, the log that will be produced, etc..
Context keywords - these keywords express on which part of the traffic (in our case, which part of the HTTP protocol) will the inspection keywords will apply.
Inspection keywords - these keywords test the traffic for certain conditions. They are the ones that determine if the rule is matched and an action should be taken.
Metadata-type keywords can be divide into three categories:
Must-have keywords need to be present in the rule, or the rule won't be enforced.
Recommended keywords can be absent from the rule, but we suggest they will be added for ease of operational use of the feature: managing signatures, tracking them, etc..
Optional keywords won't effect any operational aspect of the system, but may contribute to the user-experience.
Must-have metadata keywords
"msg" keyword: This keyword determine the text that will appear in the log as the protection name. The name itself should be inside a quotation marks, for example:
msg: "Testing CloudGuard WAF Snort";
"flow" keyword: This keyword determine which side of the communication should the rule inspect. In CloudGuard WAF we protect servers so the keyword should read:
flow: to_server,established;
"service" keyword: This keyword marks which parser should apply for the rule. In CloudGuard WAF we protect HTTP traffic so the keyword should read:
service: http;
Recommended metadata keywords
"sid" keyword: The "signature id" keywords provides a unique numerical value that identify the signature. These allows you to have the same log message for different rules and still distinguish between them. It should have a "random" numerical value, for example:
sid: 12345;
"rev" keyword: The "revision" keywords is meant to distinguish between different iterations of the same signature, so you can tell for example if a false-positive is from an old version that is already fixed or not. It should start with the number 1 and increase with each version, for example:
rev: 1;
Optional metadata keywords
"reference" keyword: This keywords allows you to add links that will appear in the log and will provide more information. There can be multiple instances of this keyword in a single rule. The easiest way to use it is to write the word "url", followed by comma, followed by the URL itself. For example:
reference: url,www.acunetix.com;
Context-type keywords define which section of the HTTP protocol the following inspection keywords will refer to. So when wanting to check the URI, you need to first mention the appropriate keyword and then all the following inspection keyword will apply to the URI - until another context keyword will appear.
By default Snort scan the raw packet, not parsed HTTP. Since we are working in Layer 7 only, this is not possible for us to do. So you must use a context keyword before using any inspection keywords.
The main context keywords you want to know are:
"http_raw_uri" - This holds the URI as it appears "on the wire" without decoding. So if we send '/W%41F' the context will be '/W%41F'.
"http_uri" - This holds the URI after decoding any encoding in the URI (only, we don't resolve directory traversal for example. So if we send '/W%41F' the context will be '/WAF'.
"http_header" - This holds the headers section of the HTTP (request in our case).
"http_client_body" - This holds the body of an HTTP request.
Inspection-type keywords determine if the request's data (in the section specified by the previous context keyword) meets a certain condition. If it does, than the keywords are said to be matched. If all the inspection keywords match then the rule is said to be matched and the appropriate action (drop and\or send log) will take place.
There are several inspection keywords, and each of them usually have several options. Here we are going to present only the basic syntax of two inspection keywords - this is sufficient for most proposes.
"content" keyword - This keyword checks whether a specific content (i.e. a simple string) appears in the context. The string itself appears within quotation marks. For example, if we want to search for the string "attack data" we can write:
content: "attack data";
"pcre" keyword - This keywords checks whether a specific Perl Compatible Regular Expression is found in the context (For a full description of the syntax see here). The expression is placed with forward-slashes within quotation marks. For example, if we want to search for "hello" followed any number of spaces followed by "world", we can write:
pcre: "/hello\s*world/";