Writing Suricata Rules

Brief notes on writing suricata rules.

Rules work by looking for known malicious patterns (ex. strings, bytes, regex, and field values) or suspicious behavior, such as downloading a PE file when requesting an image, or uncommon port for a given protocol.

Principles

  • Read documentation.

  • Use fast_pattern

  • Avoid rules containing only regex.

  • Do simple checks first (flow, dsize, flowbits, ...)

  • Write generic rules first, then tune them.

  • Test rules on a collection of clean traffic.

Location of default rules are stored in the "suricata.yaml" configuration file.

default-rule-path: /etc/suricata/rules/
rule-files:
  - backdoor.rules
  - http.rules
  - custom.rules
  - ...

Environment variables can be used in rules.

vars:
  address-groups:
    HOME_NET: "[192.168.0.0/16,172.16.0.0/12,10.0.0.0/8]"
    EXTERNAL_NET: any
    HTTP_SERVERS: "$HOME_NET"
    SQL_SERVERS: "$HOME_NET"
    DNS_SERVERS: "$HOME_NET"

Rules are written line by line or can span multiple lines by using a backslash at the end of each line, excluding the last. Comments can be inserted using a hashtag.

# Rule using single line:
alert http $HOME_NET -> $EXTERNAL_NET $HTTP_PORTS (msg:"Login Check"; classtype:malicious-activity; sid:10000001)
# Rule using multiple lines:
alert http \
$HOME_NET -> $EXTERNAL_NET $HTTP_PORTS \
(msg:"Login Check"; classtype:malicious-activity; sid:10000001)

Rule Breakdown

# Rule on a single line
alert http $HOME_NET any -> $EXTERNAL_NET 88 (msg:"Suspicious activity"; flow:to_server,established; content:"POST"; http_method; reference:url,threats.sec.lab/New.Worm; classtype:malicious-activity; sid:10000001; rev:1;)

# Rule using multiple lines
alert http $HOME_NET any -> $EXTERNAL_NET 88 \
(msg:"Suspicious activity"; \
flow:to_server,established; \
content:"POST"; \
http_method; \
reference:url,threats.sec.lab/New.Worm; \
classtype:malicious-activity; \
sid:10000001; \
rev:1;)
  • alert: Rule action

  • http: Protocol

    • Basic: (snort compatible) tcp, udp, icmp, ip.

    • App Layer: http, ftp, tls, smb, dns, smtp, etc...

  • $HOME_NET: Source IP/range.

  • any: Source port number; any port.

  • ->: Direction (both ways: <>).

  • $EXTERNAL_NET: Destination IP/range.

  • 88: Destination port number.

  • msg:"Suspicious activity": Meta-setting containing information about the alert.

  • flow:to_server,established: Optional. Specifies direction of traffic and if connection has been established or not.

  • content:"POST": Content - matches on bytes. Printable characters, hexadecimal notation...

  • http_method: Content modifier. Instructs suricata on the type of content.

    • Other keyword modifiers include:

      • nocase: Makes content case-insensitive.

      • fast_pattern: specifies the which content to check first.

      • startswith: matches start of a buffer.

      • endswith: matches end of a buffer.

      • depth:1: number of bytes from the beginning of the payload to check.

      • offset:2: from which byte to start checking.

      • distance:3: from which byte to start checking after the previous match.

      • within:4: how many bytes will be checked after the previous match.

      • dsize:5; (dsize:>6; dsize:7<>8;): size of the packet payload.

      • pcre:"/^[a-z0-9{9}\.js$/U/": regular expression.

      • threshold: type <threshold|limit|both>, track <by_src|by_dst>, count <N>, seconds <T>: alert frequency.

  • reference:url,threats.sec.lab/New.Worm: Optional. url, md5, cve, etc. /etc/suricata/reference.config

  • classtype:malicious-activity: Information about the threat classification. /etc/suricata/classification.config

  • sid:10000001: Signature ID

    • SID allocation:

      • 1000000-1999999 reserved for local use.

      • 2000000-2099999 Emerging Threats open rulesets

      • 2100000-2103999 forked ET Versions of the Original Snort GPL Signatures

      • http://sidallocation.org/

  • rev:1: Rule revision, starts from 1.

Writing good rules

  • Read the documentation!

    • https://docs.suricata.io/en/latest/rules/index.html

  • Use keywords and modifiers to specify location and order of malicious parts, packet size, IP/port ranges, etc.

  • Avoid using very common patters or only regex.

  • Avoid using very exact patterns that can easily be changed. (ie: host name, full URI, parameter values)

Indicators to look for:

  • Look for suspicious field values.

  • A lot of requests of the same type.

  • Sensitive data sent from client.

  • Commands received from the server.

Analyzing alerts

  • Obtain artifacts.

  • Check IP/domain reputation.

  • Check alert frequency.

  • If false positive - add exclusion to the rule.

Fixing false positives

  • Compare malware and clean traffic

  • Exclude a host:

    • content:!"example.com"; http_host;

  • Find fields that do not exist in malicious traffic:

    • content:!"User-Agent:"; http_header;

  • Add more conditions: request format, data length, field order, etc.

Troubleshooting rules

  • Incorrect variable declarations in config.

  • SID is not unique.

  • Problems with traffic.

  • Remove options one-by-one.

  • Check suricata log eve.json.

Potential Issues

  • Generic rules may produce false positives.

  • Easy to circumvent precise rules.

    • Make rules as generic as possible to prevent false positives.

    • For botnets it is not very easy for attacks to significantly change protocol in each bot version.

    • Many attacks just don't care.

    • Easy to circumvent rules from open rulesets, but not paid feeds or self-written rules.

  • Not all encrypted traffic can be detected.

  • Poor performance of rules with lots of regex.

Last updated