10.12. 流关键字

10.12.1. flowbits

flowbits由两部分组成。第一部分描述要执行的操作,第二部分是flowbit的名称。

一个流包含多个数据包。Suricata将这些流保存在内存中。更多信息请参阅 suricata-yaml-flow-settings

flowbits可以确保当例如两个不同的数据包匹配时生成警报。只有当两个数据包都匹配时才会生成警报。因此,当第二个数据包匹配时,Suricata需要知道第一个数据包是否也匹配。flowbits会在数据包匹配时标记流,这样Suricata就"知道"当第二个数据包也匹配时应生成警报。

flowbits有不同的操作,包括:

flowbits: set, name

如果存在,将在流中设置条件/'名称'。

flowbits: isset, name

可用于规则中,确保当规则匹配且流中设置了该条件时生成警报。

flowbits: toggle, name

反转当前设置。例如,如果条件已设置,则取消设置,反之亦然。

flowbits: unset, name

可用于取消流中的条件设置。

flowbits: isnotset, name

可用于规则中,确保当规则匹配且流中未设置该条件时生成警报。

flowbits: noalert

此规则不会生成警报。

示例:

../_images/Flowbit_3.png

查看第一条规则时,您会注意到如果没有末尾的'flowbits: noalert',它会在匹配时生成警报。

此规则的目的是检查'userlogin'的匹配并在流中标记。因此,无需生成警报。第二条规则在没有第一条规则时无效。如果第一条规则匹配,flowbit会在流中设置该特定条件。现在可以检查第二条规则,看前一个数据包是否满足第一个条件。如果第二条规则现在匹配,将生成警报。

Note

flowbit名称区分大小写。

Note

可以在一条规则中多次使用flowbits并组合不同的功能。

Note

可以使用 | (管道)对flowbits执行 OR 操作。

alert http any any -> any any (msg:"User1或User2登录"; content:"login"; flowbits:isset,user1|user2; sid:1;)

10.12.2. flow

flow关键字可用于匹配流的方向,即到/从客户端或到/从服务器。它还可以匹配流是否已建立。flow关键字还可用于表示签名必须仅在流上匹配(only_stream)或仅在数据包上匹配(no_stream)。

使用flow关键字可以匹配:

to_client

匹配从服务器到客户端的数据包。

to_server

匹配从客户端到服务器的数据包。

from_client

匹配从客户端到服务器的数据包(与to_server相同)。

from_server

匹配从服务器到客户端的数据包(与to_client相同)。

established

匹配已建立的连接。

not_established

匹配不属于已建立连接的数据包。

stateless

匹配属于流的数据包,无论连接状态如何。(这意味着不被视为流的一部分的数据包不会匹配)。

only_stream

匹配已被流引擎重组的数据包。

no_stream

匹配未被流引擎重组的数据包。不会匹配已被重组的数据包。

only_frag

匹配从片段重组的数据包。

no_frag

匹配未从片段重组的数据包。

可以组合多个flow选项,例如:

flow:to_client, established
flow:to_server, established, only_stream
flow:to_server, not_established, no_frag

established 的确定取决于协议:

  • 对于TCP,连接将在三次握手后建立。

    ../_images/Flow1.png
  • 对于其他协议(例如UDP),在从连接的两端看到流量后,连接将被视为已建立。

    ../_images/Flow2.png

10.12.3. flowint

flowint允许使用变量进行存储和数学运算。它的操作类似于flowbits,但增加了数学功能,可以存储和操作整数,而不仅仅是设置标志。我们可以将其用于许多非常有用的用途,例如计数出现次数、增加或减少出现次数,或在流中根据多个因素进行阈值设置。这将很快扩展到全局上下文,以便用户可以在流之间执行这些操作。

语法如下:

flowint: name, modifier[, value];

定义变量(非必需),或检查是否设置了变量。

flowint: name, < +,-,=,>,<,>=,<=,==, != >, value;
flowint: name, (isset|notset|isnotset);

比较或修改变量。可用的操作包括加、减、比较大于或小于、大于或等于、小于或等于。比较的对象可以是整数或另一个变量。


例如,如果您想计算特定流中用户名出现的次数,并在超过5次时发出警报。

alert tcp any any -> any any (msg:"计数用户名"; content:"jonkman"; \
      flowint: usernamecount, +, 1; noalert;)

这将计算每次出现并增加变量usernamecount,且不会为每次生成警报。

现在假设我们希望在流中超过五次匹配时生成警报。

alert tcp any any -> any any (msg:"超过五个用户名!"; content:"jonkman"; \
      flowint: usernamecount, +, 1; flowint:usernamecount, >, 5;)

因此,我们只会在usernamecount超过5时收到警报。

现在假设我们希望如上所述收到警报,但如果该用户名注销的次数更多则不发出警报。假设此特定协议用"jonkman logout"表示注销,让我们尝试:

alert tcp any any -> any any (msg:"用户名注销"; content:"logout jonkman"; \
      flowint: usernamecount, -, 1; flowint:usernamecount, >, 5;)

因此,我们现在只会在该特定用户名有超过五个活动登录时收到警报。

这是一个相当简单的例子,但我相信它展示了这样一个简单功能对规则编写的强大作用。我看到了许多应用,如登录跟踪、IRC状态机、恶意软件跟踪和暴力登录检测。

假设我们正在跟踪一个通常允许每个连接五次登录失败的协议,但我们有一个漏洞,攻击者可以在五次尝试后继续登录,我们需要知道这一点。

alert tcp any any -> any any (msg:"开始登录计数"; content:"login failed"; \
      flowint:loginfail, notset; flowint:loginfail, =, 1; noalert;)

因此,我们检测初始失败,如果变量尚未设置,则将其设置为1。我们的第一次匹配。

alert tcp any any -> any any (msg:"计数登录"; content:"login failed"; \
      flowint:loginfail, isset; flowint:loginfail, +, 1; noalert;)

我们现在正在增加计数器,如果它已设置。

alert tcp any any -> any any (msg:"流中超过五次登录失败"; \
      content:"login failed"; flowint:loginfail, isset; flowint:loginfail, >, 5;)

现在,如果我们在同一流中超过五次登录失败,将生成警报。

但假设我们还需要在两个成功登录后有一个失败登录时发出警报。

alert tcp any any -> any any (msg:"计数成功登录";             \
      content:"login successful"; flowint:loginsuccess, +, 1; noalert;)

这里我们正在计算成功登录,因此现在我们将计算与失败相关的成功登录:

alert tcp any any -> any any (msg:"两次成功登录后登录失败";   \
      content:"login failed"; flowint:loginsuccess, isset;            \
      flowint:loginsuccess, =, 2;)

以下是一些其他一般示例:

alert tcp any any -> any any (msg:"设置flowint计数器"; content:"GET"; \
      flowint:myvar, notset; flowint:maxvar,notset;                           \
      flowint:myvar,=,1; flowint: maxvar,=,6;)
alert tcp any any -> any any (msg:"增加flowint计数器";                \
      content:"Unauthorized"; flowint:myvar,isset; flowint: myvar,+,2;)
alert tcp any any -> any any (msg:"当flowint计数器为3时创建新计数器"; \
      content:"Unauthorized"; flowint:myvar, isset; flowint:myvar,==,3; \
      flowint:cntpackets,notset; flowint:cntpackets, =, 0;)
alert tcp any any -> any any (msg:"计数其余部分而不生成警报"; \
      flowint:cntpackets,isset; flowint:cntpackets, +, 1; noalert;)
alert tcp any any -> any any (msg:"当达到6时触发此规则";                \
      flowint: cntpackets, isset;                                             \
      flowint: maxvar,isset; flowint: cntpackets, ==, maxvar;)

10.12.4. stream_size

stream_size选项根据序列号注册的字节数匹配流量。此关键字有几个修饰符:

>      大于
<      小于
=      等于
!=     不等于
>=    大于或等于
<=    小于或等于

格式

stream_size:<server|client|both|either>, <modifier>, <number>;

规则中stream-size关键字的示例:

alert tcp any any -> any any (stream_size:both, >, 5000; sid:1;)

10.12.5. flow.age

流的年龄(秒)(整数) 此关键字不等待流结束,但会在每个数据包上检查。

flow.age使用 无符号32位整数

语法:

flow.age: [op]<number>

可以精确匹配时间,或使用_op_设置进行比较:

flow.age:3    # 正好3
flow.age:<3   # 小于3秒
flow.age:>=2  # 大于或等于2秒

签名示例:

alert tcp any any -> any any (msg:"流超过一小时"; flow.age:>3600; flowbits: isnotset, onehourflow; flowbits: onehourflow, name; sid:1; rev:1;)

在此示例中,我们结合 flow.ageflowbits 在流的年龄超过一小时后第一个数据包上获取警报。

10.12.6. flow.pkts

流的数据包数量(整数) 此关键字不等待流结束,但会在每个数据包上检查。

flow.pkts使用 无符号32位整数 并支持以下方向:

  • toclient

  • toserver

  • either

语法:

flow.pkts:<direction>,[op]<number>

可以精确匹配数据包数量,或使用_op_设置进行比较:

flow.pkts:toclient,3    # 正好3
flow.pkts:toserver,<3   # 小于3
flow.pkts:either,>=2  # 大于或等于2

签名示例:

alert ip any any -> any any (msg:"流在toclient方向有20个数据包"; flow.pkts:toclient,20; sid:1;)

Note

Suricata还支持 flow.pkts_toclientflow.pkts_toserver 关键字分别对应 flow.pkts:toclientflow.pkts:toserver,但这不是首选语法。

10.12.7. flow.bytes

流的字节数(整数) 此关键字不等待流结束,但会在每个数据包上检查。

flow.bytes使用 无符号64位整数 并支持以下方向:

  • toclient

  • toserver

  • either

语法:

flow.bytes:<direction>,[op]<number>

可以精确匹配字节数,或使用_op_设置进行比较:

flow.bytes:toclient,3    # 正好3
flow.bytes:toserver,<3   # 小于3
flow.bytes:either,>=2  # 大于或等于2

签名示例:

alert ip any any -> any any (msg:"流在toserver方向少于2000字节"; flow.bytes:toserver,<2000; sid:1;)

Note

Suricata还支持 flow.bytes_toclientflow.bytes_toserver 关键字分别对应 flow.bytes:toclientflow.bytes:toserver,但这不是首选语法。