12. Rule Keywords

12.1. after

after: track {by_src|by_dst}, count {number of event}, seconds {number of seconds};

after: track {by_src|by_dst}, count {number of event}, seconds {number of seconds} “after” is used to trigger an alert “after” a numbers of events have happened within a specific amount of time. “after” tracks by the source or destination IP address of the event. The example would track events by the source IP address. If the event is triggered more than 10 times within 300 seconds (5 minutes), an alert is triggered.

after: track by_src, count 10, seconds 300;

12.2. alert_time

alert_time: days {days}, hours {hours};

“alert_time” allows a rule to only trigger on certain days and/or certain hours. For example, let’s assume that Windows RDP (Remote Desktop Protocol) is normal between the hours of 0800 (8 AM) to 1800 (6 PM). However, RDP sessions outside of that timeframe would be consider suspicious. This allows you to build a rule that will trigger outside of the “normal RDP” times.

Days are represented via digits 0 - 6. 0 = Sunday, 1 Monday, 2 Tuesday, 3 Wednesday, 4 Thursday, 5 Friday, 6 = Saturday.

Hours are represented by the 24 hours clock.

alert_time: days 0123456, hours 0800-1800;

The example above would cause a rule to trigger every day of the week between the hours of 0800 (8:00 AM) to 1800 (6:00 PM). One caveat is with “between” days. For example, if you wanted to create an alert_time rule that stretches from Monday 2300 (11 PM) to Tuesday 0700 (7 AM). The format would be:

alert_time: days 1, hours 2300-0800;

You do not need to include Tuesday (2) in the “days” option. Since the times stretch between two days, Sagan will automatically take this into consideration and makes the adjustments. If you were to include “days 12”, this would cause Sagan to alert on Monday-Tueday between 2300 - 0800 and Tuesday-Wednesday 2300-0800.

alert_time can also be used with sagan.yaml variables. For example, if you have “SAGAN_DAYS: 12345” and “SAGAN_HOURS: 0800-1300” in your sagan.yaml (see “aetas-groups” in your sagan.yaml), you could then create a rule like this:

alert_time: days $SAGAN_DAYS, hours $SAGAN_HOURS;

12.3. blacklist

blacklist {by_src|by_dst|both|all};

This looks up the TCP/IP address that was parsed via normalize, parse_src_ip or parse_dst_ip from a “blacklist” file. The “blacklist” file is a file that contains IPv4 and IPv6 addresses in CIDR notation from that file. In order to use this option the sagan.yaml processors blacklist must be enabled.

blacklist: by_src; parse_src_ip: 1;

12.4. bluedot

bluedot: type {ip_reputation},track {src|dst|both|all},{none|mdate_effective_period|cdate_effective_period},{category};
bluedot: type {file_hash|url|filename},{category};

Bluedot is Quadrant Information Security’s Threat Intellegence database that Sagan can query. In order to use this functionality you will need a Quadrant Information Security API key and have the bluedot processors enabled.

As Sagan extracts data like IP addresses, file hashes, URLs and filenames, Sagan can query the Bluedot database to determine if they are hostile or not. These types of lookups can be incorporated into signatures. For example:

bluedot: type ip_reputation, track by_src, none, Malicious,Tor,Honeypot,Proxy;

This will lookup the source IP out of the Bluedot database for Malicious, Tor, Honeypot or Proxy activity. If the source IP address is found in any of these categories, the option will fire.

In some cases, you might not want to trigger on older IoCs. To filter out older data from Bluedot you can use the mdate_effective_period (last modification of the IoC) or cdate_effective_period (creation date of the IoC). For example:

bluedot: type ip_reputation, track all, mdate_effective_period 1 months, Malicious,Tor,Proxy;

The will query all TCP/IP addresses found in a log line and query for Malicious, Tor and Proxy address that are no older than one month old. If the time is set to none, then any IoCs found for a TCP/IP address are returned reguarless of mdate_effective_period or cdate_effective_perid.

Below is an example of querying a file hash in Bluedot

bluedot: type file_hash,Malicious; parse_hash: sha1;

Note: Quadrnat Information Secrity Bluedot is not yet avaliable to the public.

12.5. bro-intel

bro-intel: {src_ipaddr},{dst_ipaddr},{both_ipaddr},{all_ipaddr},{file_hash},{url},{software},{email},{user_name},{file_name},{cert_hash};

This keyword allows Sagan to look malicious IP addresses, file hashes, URLs, software, email, user names, file hashes and certificate hash from Bro Intellegence feeds.

In order for the processors to be used, it must be enabled in your sagan.yaml file.

The following is a simple example within a Sagan rule:

bro-intel: src_ipaddr;

This informs Sagan to look up the parsed source address from the Bro Intel::ADDR data. The parsed source address is extracted via liblognorm or parse_src_ip.

Multiple keywords can be used. For example:

bro-intel: both_ipaddr, domain, url;

This instructs Sagan to look up the parsed source and destination from the Bro Intel::ADDR data. It also looks up the Intel::DOMAIN and Intel::URL. If any of the “bro-intel” lookups return with a positive hit, the bro-intel option is triggered. Consider the following example:

content: “thisisatest”; bro-intel: src_ipaddr;

If a log messages contains the term “thisisatest” but the parsed source IP address is not found in the Bro Intellegence feeds, the rule will not trigger. If the log message “thisisatest” is found and the src_ipaddr is found, the rule will trigger.

Sagan “bro-intel” types:

src_ipaddr   Intel::ADDR             Look up the parsed source address
dst_ipaddr   Intel::ADDR             Look up the parsed destination address
all_ipaddr   Intel::ADDR             Search all IP address in a log message and look them up
both_ipaddr  Intel::ADDR             Look up the parsed source & destination address
file_hash    Intel::FILE_HASH        Search message content for malcious file hash
url          Intel::URL              Search message content for malicious URL
software     Intel::SOFTWARE         Search message content for malicious software
email        Intel::EMAIL            Search message content for malicious email
user_name    Intel::USER_NAME        Search message content for malicious user names
file_nasm    Intel::FILE_NAME        Search message content for malicious file names
cert_has     Intel::CERT_HASH        Search message content for malicious certificate hashes

12.6. classtype

classtype: {classification}

This links the rule to a classification. Classification can be used to determined priority level. For example:

classtype: exploit-attempt;

A “exploit-attempt” classification is a priority 1 (highest) level event. For a complete list of classification types, see http://github.com/beave/sagan-rules/blob/master/classification.config

12.7. content

content is a simple means of determining if the {search} string is in a event/syslog message. For example:

content: “authentication failure”;

Will search a log message for the term “authentication failure”. content can also be used as part of a NOT statement. For example:

content:!”frank”;

This means the the message does NOT contain the term “frank”. Tired together, we can make statements like:

content: “authentication failure”; content:!”frank”;

If the term “authentication failure” is found and does NOT contain the term “frank”, then rule will trigger. Otherwise, the event is ignored.

content: “User Agent|3a| Testing”;

This tells content to search for “User Agent: Testing”. The |3a| is a hex encoded option for a “:”. You can use multiple hex encoded options. For example, “|3a 3b 3c|”. Hex values can also be broken up. For example, “This |3a| is a testing with |3b| in it”.

12.8. country_code

country_code: track {by_src|by_dst}, {is|isnot} {ISO3166 Country Codes}

Used to track events from specific countrys.

country_code: track by_src, isnot US;

The example above means, “track by the source address of the event. If the GeoIP 2 location is not from the Unitied States, trigger the rule”.

country_code: track by_dst, is [CN,RU,HK];

The example above means, “track by the destination address of the event. If the GeoIP 2 locaiton is going to China, Russia or Hong Honk, trigger the rule”.

Country codes are based on ISO3166. See http://dev.maxmind.com/geoip/legacy/codes/iso3166/ for the full listing.

Typically, country codes are tried to the sagan.yaml variable $HOME_COUNTRY (See “geoip-groups” in the sagan.yaml). For example:

country_code: track by_src, isnot $HOME_COUNTRY;

Note: This requires GeoIP2 support to be compiled into Sagan

12.9. default_proto

default_proto: {tcp/udp/icmp}

The default_proto sets the default protocol in the event normalization fails. For example, OpenSSH uses the TCP protocol. However, OpenSSH log messages do not specify the protocol in use. By using the rule option default_proto, Sagan will assign the protocol specified by the rule writer when triggered. This option can be overridden by parse_proto or liblognorm (if used).

Valid values are icmp, tcp and udp or defined variables (ie - “$PROTOCOL”). Defaults to the Sagan YAML “default-proto”.

12.10. default_dst_port

default_dst_port: {port number}

The default_dst_port sets the default port number in the event normalization fails. For example, OpenSSH typically uses port 22. However, OpenSSH log message do not spcify the port being used. By using the rule option default_dst_port, Sagan will assign the port specified by the rule writer when triggered. This option can be overriden by liblognorm.

Valid values are integers (1-63556) or defined variables (ie - “$SSH_PORT”). Defaults to the Sagan YAML “default-port”.

12.11. default_src_port

default_src_port: {port number}

The default_src_port works sets the default port number in the event normalization fails. For example, if a log message does not contain the source port, this value is used instead. This can be overridden by liblognorm.

Valid values are integers (1-63556) or defined variables (ie - “$SOURCE_PORT). Defaults to the Sagan YAML “default-port”.

Note: This requires GeoIP support to be compiled into Sagan

12.12. depth

depth: {depth value}

The depth keyword allows the rule writer to specify how far into a log line Sagan should search for the specified pattern from a given offset.

For example:

content: “bob”; depth: 10;

This would start searching at the begining of the log line (default offset: 0) and search only 10 bytes deep for the term “bob”.

Example with offset and depth used together:

content: “bob”; offset: 5; depth: 10;

Sagan will start searching for the term “bob” when it gets to 5 bytes into the log line (see offset). It will only search for “bob” after the offset for 10 bytes.

This function is identical to Snort’s “depth” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.13. distance

distance: {distance value}

The distance keyword allows the rule writer to specify how far into a log line Sagan should ignore before starting to search for the specified pattern relative to the end of the previous pattern match.

For example:

content:”GET”; depth:3; content:”downloads”; distance:10;

This will cause Sagan to look for the word “GET” within the first 3 bytes ( depth) of the log line. The next content will start looking for the term “downloads” 10 bytes away from the previous depth. The above would match on the term “GET /content/downloads” but not “GET /download”. The ” /content/” (10 bytes) is skipped over in the distance.

This function is identical to Snort’s “distance” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.14. dynamic_load

{dynamic_load: /path/to/rules/to/load}

This option works in conjunction with the sagan.yaml dynamic_load configurations. When a rule is triggered with this option enabled, Sagan will dynamically load the rules. This is useful for detecting new logs introduced to the system where rules are not enabled. For more information, see https://quadrantsec.com/about/blog/dynamic_rules_with_sagan/

dynamic_load: $RULE_PATH/oracle.rules;

12.15. email

email: {email address}

If present in a rule, Sagan will e-mail the event to the email address supplied.

email: bob@example.org;

Note: This requires Sagan to be compiled with libesmtp support.

12.16. external

external: {path/and/program};

When a signature triggers with the external option, the external target is executed. The external program can be in any language you desire. Data is passed from Sagan via stdin to the external program. The information that is passed is the signature ID, the message (msg), the classtype, drop, priority, data, time, source IP, source port, destination IP, destination port, facility, syslog priority, liblognorm JSON and the syslog message.

external: /usr/local/bin/myprogram.py

12.17. syslog_facility

syslog_facility: {sylog facility}

Searches only messages from a specified facility. This can be multiple facilities when seperated with an ‘|’ (or) symbol.

facility: daemon;

12.18. flexbit

flexbits: set, {flexbit name}, {expire time};

This option is used in conjunction with flexbits: unset, isset, isnotset. This allows Sagan to “track” through multiple log events to trigger an alert. For example, let say you want to detect when “anti-virus” has been disabled but is not related to a system reboot. Using the flexbit set you can turn on a flexbit when a system is being rebooted. Our flexbit set would look like this:

flexbits: set, windows_reboot, 30;

We are “setting” a flexbit named “windows_reboot” for 30 seconds. This means that thw “windows_reboot” fleflexbit will “expire” in 30 seconds. The flexbit set automatically records the source and destination of the message that triggered the event. It is important to point out, the source and destination address is what Sagan has normalized through parse_src_ip, parse_dst_ip or liblognorm.

flexbits: {unset|isset|isnotset},{by_src|by_dst|both|reverse|none},{flexbit name}

This option works in conjunction with the flexbit set option. In the flexbit set example above, we are trying to detect when a systems “anti-virus” has been disabled and is not related to a system reboot. If Sagan detects a system reboot, it will set flexbit “windows_reboot”. Another rule can us the presence, or lack thereof, to trigger an event. For example:

flexbits: isnotset, by_src, windows_reboot;

This means, if the “windows_reboot” flexbit is not set (ie - it did not see any systems rebooting), trigger an event. The by_src tells Sagan that the trigger ( isnotset) is to be tracked by the “source” IP address. by_src, by_dst, both and none are valid options.

More examples:

flexbits: isset, both, myflexbit;

If the flexbit “myflexbit” isset, then trigger an event/alert. Track by the source of the log message.

flexbits: isnotset, both, myflexbit;

If the flexbit “myflexbit” isnotset, then trigger an event/alert. Track by both the source and desination of the message.

flexbits: unset, both, myflexbit;

This unset removes an flexbit from memory. In this example, unset is removing a flexbit “myflexbit” if the source and destination math (both).

Example of flexbit use can be found in the rules https://wiki.quadrantsec.com/twiki/bin/view/Main/5001880 and https://wiki.quadrantsec.com/twiki/bin/view/Main/5001881 . The first rule (5001880) “sets” a flexbit is a Microsoft Windows account is “created”. The second rule (5001881) alerts an account is “enabled”, but the flexbit has not (isnotset) set. In this example, it’s normal for a users account to be “created and then enabled”. However, there might be an anomaly if an account goes from a “disabled” and then “enabled” state without being “created”.

flexbits: noalert;

This informs Sagan to preform a flexbit operation, such as set or unset, but do not generate an alert. This is useful when used in conjunction with the set and unset flexbit keywords.

12.19. fwsam

fwsam: {src|dst}, {number} {second|minute|hour|day|week|month|year}

This informs Sagan that if the rule is successfully trigged, the source or destination IP address should be automatically firewalled via the “Snortsam” facility.

fwsam: src, 1 day;

This would firewall the offending source for 1 day. For more information about Snortsam, see: http://www.snortsam.net

12.20. syslog_level

syslog_level: {syslog level};

Seaches only message from a specified syslog level. This can be multiple levels when seperated by a ‘|’ (or) symbol.

level: notice;

12.21. meta_content

meta_content: "string %sagan% string",$VAR;

This option allows you to create a content like rule option that functions with variable content. For example, let’s say you want to trigger on the strings “Usernname: bob”, “Username: frank” and “Username: mary”. Without meta_content, this example would reguire three seperate rules with content keywords. The meta_content allow you make one rule option with multiple variables. For example:

meta_content: “Username|3a| %sagan%”, $USERS;

Note: The |3a| is the hexidecimal representation of a ‘:’ .

The %sagan% variable is populated with the values in $USERS. To populate the $USER variable, the sagan.conf would have the following variable declaration:

var USERS [bob, frank, mary]

If Sagan detects “Username: bob”, “Username: frank” or “Username: mary”, an event will be triggered.

Like content the ! can be applied. The ! is a “not” operator. For example:

meta_content:!”Username|3a| %sagan%”, $USERS;

This will only trigger an event if the content is not “Username: bob”, “Username: frank” or “Username: mary”. That is, the content must not have any of the values.

The %sagan% portion of meta_content is used to specify “where” to put the $USERS defined variable. For example:

meta_content: “Username|3a| %sagan% is correct”, $USERS;

Will look for “Username: bob is correct”, “Username: frank is correct” and “Username: mary is correct”.

12.22. meta_depth

meta_depth: {depth value}

Functions the same as depth for content but for meta_content. The meta_depth keyword allows the rule writer to specify how far into a log line Sagan should search for the specified patterns from a given offset.

For example, if $VAR is set to “mary, frank, bob”:

meta_content: “%sagan%”, $VAR; meta_depth: 10;

This would start searching at the begining of the log line (default meta_ offset: 0) and search only 10 bytes deep for the term “mary”, “frank” or “bob”.

Example with offset and depth used together:

meta_content: “bob”; meta_offset: 5; meta_depth: 10;

Sagan will start searching for the term “mary”, “frank” or “bob” when it gets to 5 bytes into the log line (see meta_offset). It will only search for “mary”, “frank” or “bob” after the offset for 10 bytes.

This function is identical to Snort’s “depth” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.23. meta_distance

meta_distance: {distance value}

Functions the same as distance for content but for meta_content. The meta_distance keyword allows the rule writer to specify how far into a log line Sagan should ignore before starting to search for the specified patterns relative to the end of the previous pattern match.

For example, if $VAR1 is set to “GET” and “POST” and $VAR2 is set to “download” and “upload”:

meta_content:”%sagan%”, $VAR1; meta_depth: 4; meta_content:”%sagan%”, $VAR2; meta_distance:10;

This will cause Sagan to look for the word “GET” or “POST” within the first 4 bytes (meta_depth) of the log line. The next meta_content will start looking for the term “download” or “upload” 10 bytes away from the previous meta_depth. The above would match on the term “GET /content/downloads” but not “GET /download”. The ” /content/” (10 bytes) is skipped over in the distance.

This function is identical to Snort’s “distance” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.24. meta_offset

meta_offset: {offset value};

Functions the same as offset for content but for meta_content. The meta_offset keyword allows the rule writer to specify where to start searching for a patterns within a log line. This is used in conjunction with content.

For example, $VAR is set to “mary”, “frank” and “bob”.

meta_content: “%sagan%”, $VAR; meta_offset: 5;

This informs meta_content to start searching for the term “mary”, “frank” or “bob” after it is 5 bytes into the log line.

This function is identical to Snort’s “offset” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.25. meta_nocase

This makes the previous meta_content option case insensitive.

meta_content: “Username: “, $USERS; meta_nocase;

If $USERS is populated with “bob”, “frank” and “mary”, meta_content will ignore the case. That is, “Username: mary” and “Username: MARY” will be detected. Without the meta_nocase, meta_content is case sensitive.

12.26. meta_within

meta_within: {within value};

Functions the same as within for content but for meta_content. The within keyword is a meta_content modifier that makes sure that at most N bytes are between patterns matches using the meta_content keyword.

For example, $VAR1 is set to “GET” and “POST”, while $VAR2 is set to “downloads” and “uploads”;

meta_content:”%sagan”, $VAR1; meta_depth:4; meta_content:”%sagan%”, $VAR2; meta_distance:10; meta_within:9;

The first meta_content would ony match on the world “GET” or “POST” if it is contain within the first 4 bytes of the log line. The second meta_content looks for the term “downloads” or “uploads” if it is a meta_distance of 10 bytes away from the meta_depth. From the meta_distance, only the first 9 bytes are examined for the term “downloads” or “uploads” (which is 9 bytes).

This function is identical to Snort’s “within” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.27. msg

msg: "human readable message";

The “human readable” message or description of the signature.

msg: “Invalid Password”;

12.28. nocase

nocase

Used after and in conjuction with the “content” option. This forces the previous content to search for the {search} string reguardless of case.

content: “sagan”; nocase;

This would search for the term “sagan” reguardless of it’s case (ie - Sagan, SAGAN, etc).

12.29. normalize

normalize;

Informs Sagan to “normalize” the syslog message using the LibLogNorm library and Sagan “rulebase” data.

12.30. offset

offset: {offset value};

The offset keyword allows the rule writer to specify where to start searching for a pattern within a log line. This is used in conjunction with content.

For example:

content: “bob”; offset: 5;

This informs content to start searching for the term “bob” after it is 5 bytes into the log line.

This function is identical to Snort’s “offset” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html

12.31. parse_dst_ip

parse_dst_ip: {destination position}

Uses Sagan’s dynamic IP parsing to locate the “destination” address within a syslog message.

parse_dst_ip: 2;

The second IP address found within the syslog message will be used as the destination address. This is useful when LibLogNorm fails, is to difficult use or the syslog message is dynamic.

12.32. parse_port

parse_port;

Attempts to determine the “source port” used from the contents of a syslog message. For example, Bind/DNS messages look something like; “client 32.97.110.50#22865”. The “22865” is the source port. Sagan will attempt to extract and normalize this information.

12.33. parse_proto

parse_proto;

Attempts to determine the protocol in the syslog message. If the syslog message contains terms in the “protocol.map” (for example, ICMP, UDP, TCP, etc), Sagan assigns the protocol to the assigned value. See fields assigned as “message” in the protocol.map.

12.34. parse_proto_program

Attempts to determine the protocol by the program generating the message. Values are assigned from the “protocol.map” (program fields). For example, if the program is “sshd” and the parse_proto_program option is used, TCP is assigned.

12.35. parse_hash

parse_hash: {md5|sha1|sha256};

Parses a hash out of a log message.

parse_hash: sha256;

12.36. parse_src_ip

parse_src_ip: {source position};

Uses Sagan’s dynamic IP parsing to locate the “source” address within a syslog message.

parse_src_ip: 1;

The first IP address found within the syslog message will be used as the source address. This is useful when LibLogNorm fails, is to difficult use or the syslog message is dynamic.

12.37. pcre

pcre: "{regular expression}"

“Perl Compatible Regular Expressions” (pcre) let Sagan search syslog messages using “regular expressions”. While regular expressions are powerful, they do require slightly more CPU to use. When possible, use the “content” option.

pcre: “/broken system|breaking system/i”;

Looks for the term “broken system” or “breaking system” reguardless of the strings case.

12.38. priority

priority: {priority};

Sets the prority of a alert/signature.

priority: 1;

If priority is set, it will over ride the classtype priority.

12.39. program

program: {program name|another program name}

Search only message that are from the {program}. For example:

program: sshd;

This will search the syslog message when it is from “sshd”. This option can be used with multiple OR’s. For example:

program: sshd|openssh;

This will search the syslog message when the program that generated it is “sshd” OR “openssh”.

12.40. reference

reference: {reference name}, {reference url}

Sets a reference for the signature/alert. These can be pointers to documentation that will provide more information reguarding the alert.

reference: url, www.quadrantsec.com;

If the signature/alert is triggered, the reference will be “http://www.quadrantsec.com”.

reference: cve,999-0531;

Will lookup CVE 999-0531 from http://cve.mitre.org/cgi-bin/cvename.cgi (from the references.config file).

12.41. rev

rev: {revision number};

Revision number of the rule. Increment this when a rule is changed.

rev: 5;

Revision number 5 of the rule.

12.42. sid

sid: {signature id};

“sid” is the signature ID. This has to be unique per signature.

sid: 5001021;

Sagan signatures start at 5000000. To view the “last used” signature, see https://github.com/beave/sagan-rules/blob/master/.last_used_sid

12.43. syslog_tag

syslog_tag: {syslog tag};

Informs Sagan to only search syslog message with the specified tag. This can be multiple tags when seperated with an ‘|’ (or) symbol.

tag: 2d;

12.44. threshold

threshold: type {limit|threshold}, track {by_src|by_dst}, count {number of event}, seconds {number of seconds}

This allows Sagan to threshold alerts based on the volume of alerts over a specified amount of time.

threshold: type limit, track by_src, count 5, seconds 300;

Sagan will limit the amount of alerts via the source IP address if the count exceeds 5 within a 300 second (5 minute) period.

12.45. within

within: {within value};

The within keyword is a content modifier that makes sure that at most N bytes are between pattern matches using the content keyword.

For example:

content:”GET”; depth:3; content:”downloads”; distance:10; within:9;

The first content would ony match on the world “GET” if it is contain within the first 3 bytes of the log line. The second content look for the term “downloads” if it is a distance of 10 bytes away from the depth. From the distance, only the first 9 bytes are examined for the term “downloads” (which is 9 bytes).

This function is identical to Snort’s “within” rule option. For more information see: http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html