mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
308 lines
9.9 KiB
ReStructuredText
308 lines
9.9 KiB
ReStructuredText
Flow Keywords
|
|
=============
|
|
|
|
flowbits
|
|
--------
|
|
|
|
Flowbits consists of two parts. The first part describes the action it
|
|
is going to perform, the second part is the name of the flowbit.
|
|
|
|
There are multiple packets that belong to one flow. Suricata keeps
|
|
those flows in memory. For more information see
|
|
:ref:`suricata-yaml-flow-settings`. Flowbits can make sure an alert
|
|
will be generated when for example two different packets match. An
|
|
alert will only be generated when both packets match. So, when the
|
|
second packet matches, Suricata has to know if the first packet was a
|
|
match too. Flowbits marks the flow if a packet matches so Suricata
|
|
'knows' it should generate an alert when the second packet matches as
|
|
well.
|
|
|
|
Flowbits have different actions. These are:
|
|
|
|
flowbits: set, name
|
|
Will set the condition/'name', if present, in the flow.
|
|
flowbits: isset, name
|
|
Can be used in the rule to make sure it generates an alert when the
|
|
rule matches and the condition is set in the flow.
|
|
flowbits: toggle, name
|
|
Reverses the present setting. So for example if a condition is set,
|
|
it will be unset and vice-versa.
|
|
flowbits: unset, name
|
|
Can be used to unset the condition in the flow.
|
|
flowbits: isnotset, name
|
|
Can be used in the rule to make sure it generates an alert when it
|
|
matches and the condition is not set in the flow.
|
|
flowbits: noalert
|
|
No alert will be generated by this rule.
|
|
|
|
Example:
|
|
|
|
.. image:: flow-keywords/Flowbit_3.png
|
|
|
|
When you take a look at the first rule you will notice it would
|
|
generate an alert if it would match, if it were not for the 'flowbits:
|
|
noalert' at the end of that rule. The purpose of this rule is to check
|
|
for a match on 'userlogin' and mark that in the flow. So, there is no
|
|
need for generating an alert. The second rule has no effect without
|
|
the first rule. If the first rule matches, the flowbits sets that
|
|
specific condition to be present in the flow. Now with the second rule
|
|
there can be checked whether or not the previous packet fulfills the
|
|
first condition. If at that point the second rule matches, an alert
|
|
will be generated.
|
|
|
|
It is possible to use flowbits several times in a rule and combine the
|
|
different functions.
|
|
|
|
It is also possible to perform an `OR` operation with flowbits with `|` op.
|
|
|
|
Example::
|
|
alert http any any -> any any (msg: "User1 or User2 logged in"; content:"login"; flowbits:isset,user1|user2; sid:1;)
|
|
|
|
This can be used with either `isset` or `isnotset` action.
|
|
|
|
flow
|
|
----
|
|
|
|
The flow keyword can be used to match on direction of the flow, so to/from
|
|
client or to/from server. It can also match if the flow is established or not.
|
|
The flow keyword can also be used to say the signature has to match on stream
|
|
only (only_stream) or on packet only (no_stream).
|
|
|
|
So with the flow keyword you can match on:
|
|
|
|
to_client
|
|
Match on packets from server to client.
|
|
to_server
|
|
Match on packets from client to server.
|
|
from_client
|
|
Match on packets from client to server (same as to_server).
|
|
from_server
|
|
Match on packets from server to client (same as to_client).
|
|
established
|
|
Match on established connections.
|
|
not_established
|
|
Match on packets that are not part of an established connection.
|
|
stateless
|
|
Match on packets that are and are not part of an established connection.
|
|
only_stream
|
|
Match on packets that have been reassembled by the stream engine.
|
|
no_stream
|
|
Match on packets that have not been reassembled by the stream
|
|
engine. Will not match packets that have been reassembled.
|
|
only_frag
|
|
Match packets that have been reassembled from fragments.
|
|
no_frag
|
|
Match packets that have not been reassembled from fragments.
|
|
|
|
Multiple flow options can be combined, for example::
|
|
|
|
flow:to_client, established
|
|
flow:to_server, established, only_stream
|
|
flow:to_server, not_established, no_frag
|
|
|
|
The determination of *established* depends on the protocol:
|
|
|
|
* For TCP a connection will be established after a three way
|
|
handshake.
|
|
|
|
.. image:: flow-keywords/Flow1.png
|
|
|
|
* For other protocols (for example UDP), the connection will be
|
|
considered established after seeing traffic from both sides of the
|
|
connection.
|
|
|
|
.. image:: flow-keywords/Flow2.png
|
|
|
|
|
|
flowint
|
|
-------
|
|
|
|
Flowint allows storage and mathematical operations using variables. It
|
|
operates much like flowbits but with the addition of mathematical
|
|
capabilities and the fact that an integer can be stored and
|
|
manipulated, not just a flag set. We can use this for a number of very
|
|
useful things, such as counting occurrences, adding or subtracting
|
|
occurrences, or doing thresholding within a stream in relation to
|
|
multiple factors. This will be expanded to a global context very soon,
|
|
so users can perform these operations between streams.
|
|
|
|
The syntax is as follows::
|
|
|
|
flowint: name, modifier[, value];
|
|
|
|
Define a var (not required), or check that one is set or not set.
|
|
|
|
::
|
|
|
|
flowint: name, < +,-,=,>,<,>=,<=,==, != >, value;
|
|
flowint: name, (isset|isnotset);
|
|
|
|
Compare or alter a var. Add, subtract, compare greater than or less
|
|
than, greater than or equal to, and less than or equal to are
|
|
available. The item to compare with can be an integer or another
|
|
variable.
|
|
|
|
________________________________________
|
|
|
|
For example, if you want to count how many times a username is seen in
|
|
a particular stream and alert if it is over 5.
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Counting Usernames"; content:"jonkman"; \
|
|
flowint: usernamecount, +, 1; noalert;)
|
|
|
|
This will count each occurrence and increment the var usernamecount
|
|
and not generate an alert for each.
|
|
|
|
Now say we want to generate an alert if there are more than five hits
|
|
in the stream.
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"More than Five Usernames!"; content:"jonkman"; \
|
|
flowint: usernamecount, +, 1; flowint:usernamecount, >, 5;)
|
|
|
|
So we'll get an alert ONLY if usernamecount is over five.
|
|
|
|
So now let's say we want to get an alert as above but NOT if there
|
|
have been more occurrences of that username logging out. Assuming this
|
|
particular protocol indicates a log out with "jonkman logout", let's
|
|
try:
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Username Logged out"; content:"logout jonkman"; \
|
|
flowint: usernamecount, -, 1; flowint:usernamecount, >, 5;)
|
|
|
|
So now we'll get an alert ONLY if there are more than five active
|
|
logins for this particular username.
|
|
|
|
This is a rather simplistic example, but I believe it shows the power
|
|
of what such a simple function can do for rule writing. I see a lot of
|
|
applications in things like login tracking, IRC state machines,
|
|
malware tracking, and brute force login detection.
|
|
|
|
Let's say we're tracking a protocol that normally allows five login
|
|
fails per connection, but we have vulnerability where an attacker can
|
|
continue to login after that five attempts and we need to know about
|
|
it.
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Start a login count"; content:"login failed"; \
|
|
flowint:loginfail, notset; flowint:loginfail, =, 1; noalert;)
|
|
|
|
So we detect the initial fail if the variable is not yet set and set
|
|
it to 1 if so. Our first hit.
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Counting Logins"; content:"login failed"; \
|
|
flowint:loginfail, isset; flowint:loginfail, +, 1; noalert;)
|
|
|
|
We are now incrementing the counter if it's set.
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"More than Five login fails in a Stream"; \
|
|
content:"login failed"; flowint:loginfail, isset; flowint:loginfail, >, 5;)
|
|
|
|
|
|
Now we'll generate an alert if we cross five login fails in the same
|
|
stream.
|
|
|
|
But let's also say we also need alert if there are two successful
|
|
logins and a failed login after that.
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Counting Good Logins"; \
|
|
content:"login successful"; flowint:loginsuccess, +, 1; noalert;)
|
|
|
|
Here we're counting good logins, so now we'll count good logins
|
|
relevant to fails:
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Login fail after two successes"; \
|
|
content:"login failed"; flowint:loginsuccess, isset; \
|
|
flowint:loginsuccess, =, 2;)
|
|
|
|
Here are some other general examples:
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Setting a flowint counter"; content:"GET"; \
|
|
flowint:myvar, notset; flowint:maxvar,notset; \
|
|
flowint:myvar,=,1; flowint: maxvar,=,6;)
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"Adding to flowint counter"; \
|
|
content:"Unauthorized"; flowint:myvar,isset; flowint: myvar,+,2;)
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"when flowint counter is 3 create new counter"; \
|
|
content:"Unauthorized"; flowint:myvar, isset; flowint:myvar,==,3; \
|
|
flowint:cntpackets,notset; flowint:cntpackets, =, 0;)
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"count the rest without generating alerts"; \
|
|
flowint:cntpackets,isset; flowint:cntpackets, +, 1; noalert;)
|
|
|
|
::
|
|
|
|
alert tcp any any -> any any (msg:"fire this when it reach 6"; \
|
|
flowint: cntpackets, isset; \
|
|
flowint: maxvar,isset; flowint: cntpackets, ==, maxvar;)
|
|
|
|
|
|
stream_size
|
|
-----------
|
|
|
|
The stream size option matches on traffic according to the registered
|
|
amount of bytes by the sequence numbers. There are several modifiers
|
|
to this keyword:
|
|
|
|
::
|
|
|
|
> greater than
|
|
< less than
|
|
= equal
|
|
!= not equal
|
|
>= greater than or equal
|
|
<= less than or equal
|
|
|
|
Format
|
|
|
|
::
|
|
|
|
stream_size:<server|client|both|either>, <modifier>, <number>;
|
|
|
|
Example of the stream-size keyword in a rule::
|
|
|
|
alert tcp any any -> any any (stream_size:both, >, 5000; sid:1;)
|
|
|
|
flow.age
|
|
--------
|
|
|
|
Flow age in seconds (integer)
|
|
|
|
Syntax::
|
|
|
|
flow.age: [op]<number>
|
|
|
|
The time can be matched exactly, or compared using the _op_ setting::
|
|
|
|
flow.age:3 # exactly 3
|
|
flow.age:<3 # smaller than 3 seconds
|
|
flow.age:>=2 # greater or equal than 2 seconds
|
|
|
|
Signature example::
|
|
|
|
alert tcp any any -> any any (msg:"Flow longer than one hour"; flow.age:>3600; flowbits: isnotset, onehourflow; flowbits: onehourflow, name; sid:1; rev:1;)
|
|
|
|
In this example, we combine `flow.age` and `flowbits` to get an alert on the first packet after the flow's age is older than one hour. |