Now we have a basic understanding of how Suricata uses rules, let's take
a closer look at the structure of the rule itself.
A rule is simply a pattern of content matches that map to
an action, such as an alert or a drop.
The patterns can be defined in a variety of ways and you can find extensive
information in the documentation. While you will likely consume most of your
rules from vendors such as the ET Open set, it will come in handy to not only be
able to write custom rules from time to time,
but also in being able to comprehend what a rule is telling you
about your traffic when investigating an alert.
Suricata has been designed to process thousands of rules against high
volumes of network traffic. While we won't be getting into scaling your
Suricata deployment in this discussion,
knowing how much traffic your sensor can handle is very
important production consideration.
Not only does this require understanding your network traffic value,
but also how much data you can store, the retention timelines you
desire, and even underlying hardware considerations,
including if you need specialized network interface cards.
The goal with an alert is to give you a pointer to something
specific in your network traffic. Suricata's metadata will then
help you build context around those alerts.
So there's really no getting around it.
Looking at a rule for the first time can be a bit overwhelming.
Here we see a rule as it would be downloaded from sources like ET
Open. It is this text, which is human‑readable, that is loaded and
parsed by the Suricata engine.
The engine itself is quite complex and how a rule is evaluated
against network traffic an important concept,
especially if you find yourself writing rules on a regular basis.
However,
keep in mind that our goal right now is the basics,
so let's break this down a bit.
There are multiple fields that make up a rule. First is the action.
This tells Suricata what to do if the rule matches on network
traffic. Alert is the most common, in which the engine will log
an alert event but do nothing further.
Pass passes the packet, and in case of TCP,
the rest of the flow. Drop will drop the packet and generate
an alert, and reject sends an RST ICMP unreached error to the
sender of the matching packet.
There are a few more actions,
but these are the most common. Protocol tells Suricata where to look. TCP,
UDP, ICMP, or IP, there are also several application
layer protocols such as HTTP and TLS.
Next comes the source, which is defined as IP addresses in a number
of formats such as a single IP, CIDR notation,
even an array‑like collection of IP formats. Here you can see the HOME_NET
variable, which is defined in Suricata's configuration file.
The keyword any, after HOME_NET, is for the ports.
So this rule is saying match on any port from HOME_NET IPs.
If you were looking for a specific value,
you could define it here, either a single port number or range of ports,
a series of individual or specific ports.
Next is the EXTERNAL_NET variable, which is also defined in the configuration.
This is often just the negation of the HOME_NET addresses.
Of course, this makes sense, if it's not HOME_NET, it must be EXTERNAL.
These definitions are important, as it tells Suricata through the rules
how to look at traffic and when to generate alerts. Messing this up can
cause you trouble in your deployments.
The arrow defines the direction.
So in this case, we're talking about traffic going
from HOME_NET to EXTERNAL_NET.
We've already discussed most of these common fields, as you can use this
information while investigating alerts to gain deeper insight.
The msg field is usually the most important, as it will
likely be the most prominent part of the alert event in
your visualizations and dashboards.
You can also use the sid, or signature ID, when looking for specific
rules, each rule will have a sid and it needs to be unique against
all the rules Suricata is loading. Rev is for the revision and used
by tools like suricata‑update.
If a ruleset does happen to have rules with duplicate sid's,
the one with a higher rev can be used. And finally, classtype.
It's a way to organize rules with a similar purpose.
A few more common rule fields, of these, content is one of the
most important. Content will tell Suricata what to look for in
the packet to perform its matching.
We'll get into the finer details later in the course, but you'll learn
about things like sticky buffer and content modifier.
If writing a rule, this will be at the core of how your rule works.
If investigating a rule,
you'll look at the content keywords to understand what it matched on. Flow is
also important, as it determines if the flow should be in a certain state
before checking for content matches. With what we now know, let's revisit a
rule that can be found in the ET Open ruleset.
It begins with the alert action and is looking at HTTP traffic
going from HOME_NET to EXTERNAL_NET on any port. Using the HTTP
protocol tells Suricata that any HTTP flow,
regardless of what port. This helps tremendously as you don't have to worry
about HTTP requests over nonstandard ports being missed. You should also
realize that this does not look into HTTPS traffic, that is HTTP over a TLS
session, unless you have a way to decrypt the traffic on‑premise. Flow is
defined as established, to_server, which tells Suricata not to process this
rule further until, in the case of a TCP session, which is needed for HTTP,
the three‑way handshake is complete. For other protocols like UDP,
established is considered when traffic is seen from both sides. Now for the
matching. The urilen must be one, the http.method must be GET, hosts must
be api.ipify.org, and that's pretty much it.
Fast pattern says to check the proceeding content
matched first, so the HTTP host.
This is done to signify what the rure writer thinks
is the most unique piece of data.
If that's not there, don't check anything else and save CPU time.
We now have some of the rule basics down.
Let's take a look at more rules from the ET Open ruleset and
generate some alerts with malicious traffic.