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
此规则不会生成警报。
示例:

查看第一条规则时,您会注意到如果没有末尾的'flowbits: noalert',它会在匹配时生成警报。
此规则的目的是检查'userlogin'的匹配并在流中标记。因此,无需生成警报。第二条规则在没有第一条规则时无效。如果第一条规则匹配,flowbit会在流中设置该特定条件。现在可以检查第二条规则,看前一个数据包是否满足第一个条件。如果第二条规则现在匹配,将生成警报。
Note
flowbit名称区分大小写。
Note
可以在一条规则中多次使用flowbits并组合不同的功能。
Note
可以使用 | (管道)对flowbits执行 OR 操作。
alert http any any -> any any (msg:"User1或User2登录"; content:"login";
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,连接将在三次握手后建立。
对于其他协议(例如UDP),在从连接的两端看到流量后,连接将被视为已建立。
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.age 和 flowbits 在流的年龄超过一小时后第一个数据包上获取警报。
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_toclient
和 flow.pkts_toserver
关键字分别对应 flow.pkts:toclient
和 flow.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_toclient
和 flow.bytes_toserver
关键字分别对应 flow.bytes:toclient
和 flow.bytes:toserver
,但这不是首选语法。