Snort Rule Writing for the IT Professional
Snort–the open source intrusion detection and prevention (IDS/IPS) system—for over a decade now has proven its value and efficacy and is ranked among the best IDS/IPS systems on the planet now. Snort installations can be found on every continent and in nearly every nation. It has been deployed by government/military agencies, non-profits, healthcare institutions, and private corporations. Although recent data indicates that Sourcefire, Snort’s commercial brother, is second only to Cisco in market share in the IDS/IPS market, this data fails to take into accounts the millions of IDS/IPS’s that use the open source Snort software as its detection engine. When these are included, Snort is likely the world’s most widely used IDS/IPS system.
One of the great strengths of the Snort IDS system and a major factor in its popularity is the fact that it is open source and therefore allows and enables the IT security professional to write or tailor its rules. This set of rules establishes the conditions whereby alerts or other actions are taken when packet (s) meet the defined conditions of suspected malicious activity. The rules are a bit of simple logic. When the logic evaluates to true, Snort alerts the end-users of the protected environment that there is a new security threat. Martin Roesch, the developer of Snort and CTO of Sourcefire, developed a simple and flexible language for writing your own rules for Snort. Although the syntax appears daunting at first blush, it is actually relatively simple and easily mastered. In this, the first of a continuing series on Snort rule-writing, we will explore how you can write your own rules for this extraordinary IDS/IPS.
We will begin examining the syntax of this language by utilizing a simple rule. In each installment, we will examine and take on ever more complex rules. It is my hope and intention that the reader should have a strong fundamental understanding of Snort rule writing at the end of the series, strong enough that they can take on the task of developing their own rules within their security environment.
Snort can be downloaded from www.snort.org and the rules can be downloaded from the same site. Sourcefire makes available a free rule set that is 30 days old. The Snort VRT rule set is available by subscription. I won’t spend a lot of time on the installation and configuration of Snort, because that information can be garnered from a variety of other sources, including the excellent book by Jack Koziol, Intrusion Detection with Snort. I will say, though, that the configuration file is usually found in /etc/snort/snort.conf and the rule files are usually located at /etc/snort/rules (these files may be in other locations on your system, depending upon the setup and configuration, but this is the most common configuration). We will need both of these files for this exercise.
Snort rules can be broken up into two key parts, the header and the options section. The header defines such things as the action, the protocol, the source IP and port, the traffic direction, and finally, the destination IP and port. Everything else will be further defined and refined in the options section. Packets must meet the threshold conditions of the header before any conditions in the options section are even evaluated. Let’s look at each of these elements separately in a simple rule.
Our Simple Rule
The syntax of snort rules is actually fairly simple and elegant. This simple rule below provides us with all the basic elements of any Snort rule. First, the initial keyword indicates the action the rule should take when triggered by the snort detection engine. In our case here, you can see that this action is defined as alert. As you would likely suspect, this rule will send an alert when the conditions of the rule are met. Other possible actions in IDS mode (we will address IPS mode in a later installment) include log and pass. The log action simply records that the conditions of the rule were met and logs it into a designated log file (usually /var/snort/log, but this is variable and configurable). The action defined by the keyword pass allows the packet meeting the condition defined by the rule to be dropped. It is also possible to define your own action, for example, to route a particular type of traffic to a defined destination, but that is beyond our scope here in this initial installment and we will save it for a later lesson.
Having defined the action that we want Snort to take when the logic of the rule evaluates to true, we next want to begin to define the conditions that make the packet suspicious. Immediately following the action keyword, we can then define the protocol of the packets we are looking for. These can be tcp, ip, icmp, or udp. In our example above, you can see that this rule is looking for packets that are using the tcp protocol. If we don’t know what protocol the packet will be using, the keyword all will cover all possibilities here.
Source IP Address
Next, we can define the source IP address. In the earlier versions of Snort, only IPv4 addresses could be used, but in recent versions Sourcefire has added the capability to use IPv6 addresses. For now, we will limit this lesson only to IPv4 addresses and c IPv6 addresses at a later installment in this series (was that too many addresses?). The source IP address, as you would expect, defines the source IP address that we suspect the malicious activity is coming from. We can specify a single IP address, a single subnet, multiple addresses, and subnets, all of which must be specified in CIDR notation. This can be used to send an alert about a known offending IP address, but usually the IP of the offender is unknown and the type of packet is what we want to be alerted about. This can be set to all, meaning we want to be alerted whenever the offending packet comes from any address. Furthermore, we can define a variable in snort.conf to include a list of IP addresses, a subnet, or anything that is not on the subnet being protected ($HOME_NET meaning everything that is outside of my defined subnet I am protecting). In the example above, we are looking for malicious packets coming from 18.104.22.168. Most often, we will want to define the source of the malicious packets as coming from outside of our network. We can do this by going to the snort.conf file and setting the variable $EXTERNAL_NET to a collection of IP addresses or simply not our protected HOME_NET by setting $EXTERNAL_NET = !$HOME_NET (the exclamation point or bang is the negation character). We can then re-write this simple rule using this variable rather than the single IP address as seen below.
Just like the source IP address, Snort allows us to define the source port of the offending packet. In most cases, this is defined as all unless we are certain that the offending packet will likely always be coming from the same port. In this era of sophisticated attacks and attackers, that seems increasingly unlikely. Using all here is a good default in writing your rules. Since ICMP doesn’t use ports, when you use ICMP in the protocol field, the rule defaults to a source port of all. If need be, ports can be defined by a single port, all ports, a series of ports, a variable, ports below a specified port, and ports above a specified port. The syntax below defines how we would do each of those, respectively.
As a mnemonic device to help remember the syntax for ports less than and greater than, I remember that when the colon comes on the left side of the port number where smaller numbers would appear on the number line this represents “less than”. When the colon appears on the right side where numbers greater than would appear on the number line, this represents “greater than.” Hope that helps.
Next we define the direction of the traffic. Traffic can be defined as moving toward a defined IP address or subnet, away from a defined IP address or subnet and moving in either direction (<>).
Destination IP Address and port
The last section of the header is the destination address. The same rules apply here as for the source IP address. We can define an IP address, the destination port, or use a pre-defined variable that we can define in the snort configuration file such $HOME_NET. In the figure below, we have modified our simple rule to use the variable $HOME_NET. We have also designated port 21 on our HOME_NET to watch for this malicious FTP traffic.
The next section of our rule is referred to as the OPTIONS section. As you might guess, this section is optional, but you probably wouldn’t want to do without it, because it provides much of the power of our Snort rules. It is here that we define the conditions a packet must meet to set off an alert after it has met the threshold conditions of the rule header. In addition, the option section may include administrative information to classify the alert, where the information about the malicious packet type came from, what message to send to the operator, what number to classify the rule, etc.
Among the most commonly used options in Snort rule writing is searching the packet payload for a particular word or other content that might signify that the packet may contain malicious content. We can set a condition for the rule by simply opening the options section with a parenthesis and follow it with the keyword CONTENT and then a colon “:”. This tells the Snort engine to look in the packet payload for whatever follows the ” :” and is enclosed in double quotation marks (“). In our simple rule above, we have followed the keyword content with “anonymous.” This tells the Snort detection engine to look in the packets payload for the word “anonymous.” If the detection engine finds that word in a packet that meets our header conditions, then an alert will be triggered. Let’s note now that we are looking for the ASCII representation of “anonymous,” but our servers (and hackers) are capable of understanding the hexadecimal and binary representations of the word “anonymous,” as well. This being the case, this rule will not detect a packet with these alternative representations of the word “anonymous.” We will address how to write a rule to pick up such IDS evasion techniques in a later installment of this series.
We close the CONTENT section of the OPTIONS portion of the snort rule with a semicolon (;). As a general rule (with a few exceptions) each of the options designated within the OPTIONS section of the snort rule pairs are separated by a colon. So, in this case, we have a couplet of the keyword content, the colon and then the argument of the content keyword “anonymous” and closed with a semicolon. Nearly all options in the options section follow this structure.
Finally, in our simple rule, we designate the message that will be sent to the operator. Note that everything we have done up to this point has established the conditions that must be met to trigger the rule and the alert. In this case, the message is simply what we want Snort to tell the operator regarding this alert on a suspicious packet. Here you can see that we used the keyword msg. Following the keyword /couplet structure, we place a colon after the keyword and the message we want sent to the operator enclosed within double quotation marks.
Now that we have mastered the basics of Snort syntax and simple rule writing, we will examine more complex rules to address more sophisticated malicious activity in my next installment.