This page is out of date! Please see for newer information!

Sagan Rule Set Tips

If you're familiar with how Snort rule sets function, then you already know how to work with Sagan rule sets. In some ways, Sagan is easier to deal with because you don't have to deal with aspects of the TCP/IP layer presents to IDS/IPS (Intrusion Detection/Prevention Systems). This short document will explain how rule sets are written, how to make them as effective as possible and slight differences between Sourcefires Snort and Sagan rule sets.

Note: Rules are examples only, and might not be part of the active Sagan rule set

Example # 1. Remote IP address, no port information.

To kick things off, let's take a look at a basic rule. The below rule looks for an "attempted recon" of an SMTP server (Sendmail, in this case). The attempted recon is someone or something doing a " expn root or "vrfy root" SMTP command.

alert tcp $EXTERNAL_NET any -> $HOME_NET 25 ( msg:"[SENDMAIL] VRFY or EXPN root attempt"; pcre: "/vrfy|expn/i"; nocase;  content: "root"; 
nocase; classtype: attempted-recon;program: sm-mta|sendmail; reference: url, 5000034; sid:5000034; rev:1;) 

Let's start breaking the rule apart.

  • alert - Sagan keeps this for compatibility reasons. It has no function.
  • tcp - Sagan is a bit different here. Normally, this would tell snort what protocol (tcp, udp, icmp, etc) to examine at the packet level. Since there is no way, from a log stand point, for Sagan to know what protocol the log message was generated by, Sagan uses this field to tell the correlation engine what the protocol was. For example, in this alert, we're looking for SMTP traffic. Since SMTP is TCP based, this tells Sagan to log to the TCP Snort database. If a Snort database is not being used, then we simply use the information for logging purposes. Basically, Sagan treats the field the exact opposite than what Snort uses it for.
  • $EXTERNAL_NET - Sagan does not currently use this field, but there are plans to implement it. (see the SaganTODO list).
  • any - This is the remote port being used, and it's not likely you'll know what it is. For example, if the connection is on port 25 (SMTP) the remote port might be 45010. It might be on port 61231. Unless the remote port is always static, then it can be placed here. That is often not the case, so we leave it any. Later in this document, the "parse_port" flag goes into details about retrieving the remote port.
  • $HOME_NET - Sagan does not currently use this field, but there are plans to implement it. (see the SaganTODO list).
  • 25 - This is the port the service is likely running on. Like the protocol above (tcp), this tells Sagan what port the service is running on. In most cases, SMTP runs on port 25. So, we can assume the inbound connection is TCP since we've set the protocol and that inbound connection is on port 25.
  • msg: - Brief description of what alert was triggered. In this case "[SENDMAIL] VRFY or EXPN root attempt"
  • pcre: - This tells Sagan to use the PCRE library to match strings in the logged event. PCRE is used when you want to use a regular expression for matching. In this simple example, we're telling PCRE to identify when an event has the term "vrfy" OR "expn". Using regular expressions is very valuable, but also adds a bit more CPU overhead to the rule. When possible use the "content" flag. I'll go into detail about this more in a bit.
  • nocase; - This tells PCRE that we want to find the content regardless of case. So if the SMTP commands are "EXPN", "Expn" or "expn" are given, Sagan will match it.
  • content: - this flag does simple content matching. That is, it does not use regular expressions, and simply matches if the content contains the pattern. This is less CPU overhead, as it uses the strstr function to identify patterns within a string. While less powerful than using regular expressions (PCRE), it is faster and good for simple identification of pattern matching.
  • nocase; - Similar to the PCRE example, this tells the "content" flag not to use casing. So regardless if the string is "root" or "ROOT" it'll still match. At this point, we can match any variety of "expn root" (for example, "ExPn RoOt").
  • classtype - This is the "type" of event we've seen. In this case, it's someone "probing" the server and attempting to get more information about were the e-mail to "root" goes to. This is an "attempted-recon". Snort also classifies this type of event as "attempted-recon". Since Sagan and Snort attempt to use similar classifications, the event can be correlated by its classification type.
  • program - This is the "program" that triggered the event. In this case it's Sendmail ('sm-mta' or 'sendmail'). This field is used to "tell" Sagan to only look for events from "Sendmail" (sm-mta) that "match" a "expn root" or "vrfy root". Since we can specify to look for events from particular applications , we can reduce the amount of false positives that occur. This can be multiple programs, like in this example.
  • reference - This points to the reference system used by Snort. The "msg" flag might not give enough information, so we can "point" to further references (links) that the person viewing the event might be interested in. It provides additional resources to the event that happened. In this case, the reference to the Softwink, Inc. Wiki page with information about the rule.
  • sid - This is a unique ID given to the rule set. Each and every rule must have its own unique ID.
  • rev - Revision of this rule. This way, if the rule is modified, you'll know "which" revision of the rule was triggered.

    Pretty simple, eh? Let's break this down for Sourcefire Snort users and see how this rule attempts to correlate the event.

    1. Sagan "looks" for patterns via PCRE and "content" flag for "expn" or "vrfy" and "root". The rule is case insensitive. So "expn root", or "EXPN rOOt" doesn't matter. It also doesn't matter if additional spacing is introduced into the SMTP command.

    2. Since this is an SMTP session, we can assume it's TCP based.

    3. Since the session is known to be STMP, we can assume it's on TCP port 25.

    4. When Sagan "sees" this event, it knows it's an "attempted recon". Snort identifies this traffic as an "attempted recon" as well. We classify it the same way.

    At this point, we can correlate the event by the protocol (TCP), the target port (25), and the classification (attempted recon). Sagan can go a bit further. By using the "parse_ip" flag within a rule, Sagan will attempt to identify the remote IP address of the cause of this event.

    Example # 2. Remote IP address with port information.

    This example uses the same flags above, but introduces another flag into the mix. This flag is not standard to Snort, so we thought it would be worth mentioning.

    Here's the rule:

    alert tcp $EXTERNAL_NET any -> $HOME_NET 25 ( msg:"[SENDMAIL] VRFY or EXPN root attempt"; pcre: "/vrfy|expn/i"; nocase;  content: "root";
     nocase; classtype: attempted-recon;program: sm-mta|sendmail; reference: 5000034; sid:5000034; rev:1;) 

    This rule works off the same principles as the first example rule. In this case, Sagan is looking for log information from "sshd" (via the "program" flag). Sagan is using a simple regular expression to look for "invalid user" or "illegal user" in the message. In this case, Sagan does care about the casing of the event. We're assuming that SSH traffic is TCP based and on port 22. When this rule is matched, we'll classify it as an "invalid-user".

    You might have noticed an additional flag, "parse_port". In some cases, Sagan can correlate the remote port connecting to the service. For example, if we have an event that shows:

    sshd[18890]: Failed keyboard-interactive/pam for invalid user sagantest from port 44014 ssh2 

  • parse_port - Sagan will attempt to identify the remote port being used to connect to the service. In this case, the remote port is 44014. This is a non-standard, Sagan only flag.

    Since this does not apply to all inbound events, we apply this function to specific Sagan rules. In this case, it's possible for Sagan to use "parse_port" to identify the remote port of the system connecting to the service. In this case, we're able to correlate the event by:

    1. It was triggered by OpenSSH

    2. The PCRE and content.

    3. Sagan can "assume" this is a TCP session on port 22.

    4. We can classify it as an "invalid-user" attempt.

    5. We can correlate via the remote IP address (

    6. Since the triggering event also specified the port, we know the remote port connection is on 44014, which Sagan can use for further correlation

    However, not all events specify the remote "port" the event happened on. This is the reason this is used on a pre-rule basis. Sagan can find these remote port formats:

  • Any reference to "IPADDRESS port #", for example, " port 44014" (OpenSSH, for example). Regardless of casing.
  • Any reference to "IPADDRESS PORT", for example, " 3128"
  • Any reference to "IPADDRESS:PORT", for example "" (stunnel and rsync, for example).
  • Any reference to "IPADRESS::PORT", for example "" (Windows RealVNC connections, for example)
  • Any reference to "IPADDRESS#PORT", for example "" (Bind/named DNS services, for example)
  • Any reference to "IPADRRESS##PORT", for example, "" (Bind/Named DNS services, for example)

    If you know of more, let me know.

    Example # 3 - No protocol, no IP address and no port information.

    Not all events that happen can be tied to a network protocol level. In many cases, these are events that have happened on a local machine that cannot be tied to network related traffic. Let's look at the following rule:

    alert syslog $EXTERNAL_NET any -> $HOME_NET any ( msg: "[WINDOWS] Application error"; content:  "Application Error"; classtype: program-error;
     program: Application; reference: Main/5000309; sid:5000309; rev: 1;) 

    At this point, the rule structure should make sense. However, since this is a machine with an "Application error", there won't be any TCP/IP information Sagan can pull from the event. In this event, Sagan considers the "source" of the event (IP address) as the true source of the event. That is, if has an "Application error", since Sagan cannot find network related information, it's considered to be from the source "". The destination becomes the target of the logging server, which could be local or a centralized server. Since it's a true "syslog" event, we'll classify it as UDP traffic to port 514 (via the "syslog" traffic type) with the true IP address of the event ( So, the event comes "from" with a destination of the target logging system.

    Example # 4 - Thresholding.

    In some cases, you might want to enable 'thresholding' to keep from being overwhelmed with alerts. In these cases, you'll want to add the 'threshold:' directive to your rule set. Below is an example of an OpenSSH 'Authentication failure' rule set. These can commonly be triggered by brute force attacks and can lead to thousands of events. In many cases, you want to know that the event happened, but do not want to become over loaded with repeated information. Threshold, like with Snort rules, will help:

    alert tcp $EXTERNAL_NET any -> $HOME_NET 22 ( msg:"[OPENSSH] Authentication failure"; content:  "authentication failure"; classtype: unsuccessful-user; 
    program: sshd; threshold: type limit, track by_src, count 10, seconds 120;  parse_ip;  parse_port; reference: url,; sid:5000016; rev:2;) 

    Considering this is an OpenSSH attack, we know the protocol will be TCP. OpenSSH, unless specified otherwise, runs on port 22. This rule watches for output from the program 'sshd', and looks for the content 'authentication failure'. The thresholding is tracked by the 'source' IP address. Depending on the type of message, this might be from the system where the syslog message was generated or the source IP address of the system/attacker that caused the 'authentication failure' message. Since both the parse_ip and _parse_portare specified, Sagan will attempt to determine the source and TCP port of the authentication failure.

    With this rule, if Sagan sees the attacker's IP address (source) more than 10 times within 120 seconds, it will squelch any additional alerts. Sagan will only 'reset' this rule for that source if it hasn't seen any new events from the source for over 120 seconds. Currently, Sagan only supports threshold 'limit', and not the 'threshold' option. If you want to threshold by destination IP, change 'by_src' to 'by_dst'.

    Additional flags not mentioned in the above examples.

    Here's a few more rule flags that have not been covered in the above examples. Depending on your situation, you might find these useful, so it seemed important to cover them:

  • facility: - Syslog facility that triggered the event. For example; authpriv, cron, local7, etc.
  • level: Syslog priority level. For example; debug, info, notice, warning, err, crit, alert, emerg, etc.
  • tag: Syslog tag ID
  • -- ChampClark - 2010-06-23

    Topic revision: r6 - 2014-03-28 - ChampClark
    This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2014 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
    Ideas, requests, problems regarding TWiki? Send feedback