summaryrefslogtreecommitdiffstats
path: root/doc/userguide/rules/flow-keywords.rst
blob: bb0269299a19e2cbececd8e91f1aa926adc32e42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
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.