网络入侵检测Suricat之流(flow)检测
摘要
本文详细介绍了Suricata中规则中用于流(flow)检测的关键字使用方法,包括 flowbits、flow、flowint、stream_size、flow.age、flow.pkts 和 flow.bytes。这些关键字通过精准关联多包检测、标记流状态、统计流量数据等功能,帮助检测多步骤攻击、减少误报,并实现对网络流量的精细化分析和管理。
目录
- 摘要
- flowbits
- flow
- flowint
- flow.age
- flow.pkts
- flow.bytes
- stream_size
flowbits
流比特(Flowbits)能够精准关联多包检测,把分散于多个数据包里的相关信息连接起来,只有特定多个数据包依序满足对应条件才触发警报,以此准确检测多步骤攻击并避免误报;同时其还具备灵活标记流状态的功能,当数据包匹配特定规则,就对所属流进行标记,记录下特定状态或特征,为后续规则判断处理提供支撑;还可通过如set
(设置)、isset
(检查是否设置)、toggle
(切换)等多种动作,精细控制警报生成,构建复杂规则逻辑,有效减少无用警报 。
下面是 flowbits 不同的动作(action)介绍:
flowbits: set, name
set 表示设置流(flow)的状态,name 表示状态的名称。包含该规则字段表示假若数据包命中当前规则,设置流 name 名称对应的状态。
flowbits: isset, name
isset 表示判断当前流(flow)的 name 名称对应的状态是否设置,name 表示状态的名称。包含该规则字段表示假若数据包命中当前规则,先判断当前流的 name 对应的状态是否已设置,如果设置,则当前规则执行告警操作,如果未设置,则不执行告警。
flowbits: toggle, name
toggle(切换)表示判断当前流(flow)的 name 名称对应的状态是否设置,如果设置,则切换为未设置,否则切换为设置,name 表示状态的名称。包含该规则字段表示假若数据包命中当前规则,判断当前流的 name 对应的状态是否已设置,如果设置,则将状态切换为未设置,如果未设置,则将状态切换为设置。
flowbits: unset, name
unset 表示取消设置流(flow)的状态,name 表示状态的名称。包含该规则字段表示假若数据包命中当前规则,取消设置流 name 名称对应的状态。
flowbits: isnotset, name
isnotset 表示判断当前流(flow)的 name 名称对应的状态是否未设置,name 表示状态的名称。包含该规则字段表示假若数据包命中当前规则,先判断当前流的 name 对应的状态是否未设置,如果未设置,则当前规则执行告警操作,如果设置,则不执行告警。
flowbits: noalert
noalert 表示不产生告警。包含该规则字段表示假若数据包命中当前规则,不产生告警。
使用示例:
alert http any any -> any any (msg: "Logged In User"; content:"userlogin"; flowbits:set,userlogin; flowbit:noalert; sid: 3016017; rev: 1;)
alert http any any -> any any (msg: "Logged In User Saying Blah"; flowbits:isset,userlogin; content:"Blah"; sid: 3016018; rev: 1;)
上面规则的含义:
- 当报文命中第一条规则时,会将当前流userlogin 对应的状态置位,且不会产生告警。
- 当报文命中第二条规则时,首先判断当前流userlogin 对应的状态是否置位,如果置位,则产生告警,如果未置位,则不产生告警。
注意:
- name 字段对应的名称是大小写敏感的。
- 在一条规则中可以多次使用流比特(flowbits)并组合不同的功能。
- 可以使用管道符(“|”)对flowbits 的 name 执行 **或 **操作。
alert http any any -> any any (msg:"User1 or User2 logged in"; content:"login"; flowbits:isset,user1|user2; sid:1;)
flow
“flow”(流)关键字可用于匹配流的方向,即流向客户端、来自客户端,或者流向服务器、来自服务器。它还能够匹配流是否已建立(连接状态)。此外,“flow” 关键字还可以用于表明签名必须仅匹配于经过重组的流(only_stream,即仅在流层面匹配),或者仅匹配于单个数据包(no_stream,即不考虑流重组,仅在数据包层面匹配)。
下面是该关键字可使用的值:
to_client
匹配从 Server 发向 Client 的数据包。
to_server
匹配从 Client 发向 Server 的数据包。
from_client
匹配从 Client 发送 Server 的数据包(和 to_server 一致)。
from_server
匹配从 Server 发向 Client 的数据包(和 to_client 一致)。
established
匹配连接已建立后的数据包。
- 对于 TCP 协议来说,三次握手是否完成是连接建立的依据。
- 对于 UDP 和其它协议来说,两端都有流量发送交互是连接建立的依据。
not_established
匹配连接建立之前的数据包,或者不属于任何已成功建立连接状态的数据包。
stateless
匹配属于某个流(flow)的数据包,无论连接状态如何。
only_stream
仅对由流引擎重组后的数据包进行匹配。例如,在网络通信中,当数据以数据包的形式传输时,流引擎会根据一定的规则将这些数据包重新组装成完整的信息流,“only_stream” 就是针对这些已重组的信息流中的数据包进行匹配操作,而不会对那些尚未经过重组或者不属于完整流的单个数据包进行处理。
no_stream
匹配未被流引擎重组的数据包。不会匹配已被重组的数据包。
only_frag
匹配经过 ip 分片重组的数据包。
no_frag
匹配未经过 ip 分片重组的数据包。
流选项的字段值是可以组合使用的,示例如下:
flow:to_client, established
flow:to_server, established, only_stream
flow:to_server, not_established, no_frag
flowint
Flowint 允许使用变量进行存储和数学运算。它的运行方式与流比特(flowbits)很相似,但增加了数学运算能力,并且它可以存储和操作整数,而不只是设置标志位。我们可以利用 Flowint 来做许多非常有用的事情,比如统计出现次数、对出现次数进行加减法运算,或者根据多个因素在一个流内进行阈值判断。而且,Flowint 功能很快将扩展到全局范围,以便用户能够在不同的流之间执行这些操作 。
下面是 flowint 字段格式:
flowint: name, modifier[, value];
name:表示变量的名称。
modifier: 操作符号,可以是+,-,=,>,<,>=,<=,==, !=,如果是这类符号,后面需要配合 value 值的使用。此外,还可以是isset,notset,isnotset,如果是此类符号,后面可省略 value。
value:表示整型变量值。
使用示例:
alert tcp any any -> any any (msg:"Counting Usernames"; content:"jonkman"; flowint: usernamecount, +, 1; noalert;)
上面规则的含义是,如果当前流(flow)中报文命中该规则,则对usernamecount 变量执行加 1 操作,且不产生告警。
alert tcp any any -> any any (msg:"More than Five Usernames!"; content:"jonkman"; flowint: usernamecount, +, 1; flowint:usernamecount, >, 5;)
上面规则的含义是,如果当前流(flow)中报文命中该规则,则对usernamecount 变量执行加 1 操作,如果usernamecount 变量值大于 5,则执行告警操作。
alert tcp any any -> any any (msg:"Counting Usernames"; content:"jonkman"; flowint: usernamecount, +, 1; noalert;)
alert tcp any any -> any any (msg:"Username Logged out"; content:"logout jonkman"; flowint: usernamecount, -, 1; flowint:usernamecount, >, 5;)
上面的规则的含义是,如果用户jonkman 登录了很多次,等检测到登出时,登录次数大于五则进行告警,和上面第二个规则功能相同,只是登出时才会告警。
下面我们构造一个异常场景,要求每个连接允许五次登录失败,但存在一个漏洞,攻击者在五次尝试后仍可继续登录,我们在知晓这个漏洞的情况下设计告警规则来产生该异常告警:
alert tcp any any -> any any (msg:"Start a login count"; content:"login failed"; flowint:loginfail, notset; flowint:loginfail, =, 1; noalert;)
alert tcp any any -> any any (msg:"Counting Logins"; content:"login failed"; flowint:loginfail, isset; flowint:loginfail, +, 1; noalert;)
alert tcp any any -> any any (msg:"More than Five login fails in a Stream"; content:"login failed"; flowint:loginfail, isset; flowint:loginfail, >, 5;)
上面规则的含义如下:
- 命中第一条规则时,判断
loginfail
变量是否定义,如果未定义则设置初始值为 1,不告警。 - 命中第二条规则时,判断
loginfail
变量是否定义,如果定义,则对该变量执行加 1 操作,不告警。 - 命中第三条规则时,判断
loginfail
变量是否定义,如果定义且值大于 5,则执行告警操作,否则不告警。
flow.age
该关键字用于标识流(flow)的时长,通过记录流开始的时间,然后在每个数据包到来时,用当前时间减去流开始的时间来得到当前流的年龄(时长),单位为秒,且取整数值。注意,该关键字不是等到流结束时统计检测,而是在每个包的传输过程中都统计检测。
格式如下:
flow.age: [op]<number>
下面是该关键字的使用示例:
flow.age:3 # 限定流时长为3s
flow.age:<3 # 限定流时长小于3s
flow.age:>=2 # 限定流时长大于等于2s
下面是规则示例:
alert tcp any any -> any any (msg:"Flow longer than one hour"; flow.age:>3600; flowbits: isnotset, onehourflow; flowbits: set, onehourflow; sid:1; rev:1;)
上面规则的含义时当流存在的时长为 3600s(即 1h)时,第一个流数据包则产生告警,后面的数据包则不再产生告警。
flow.pkts
该关键字用于标识流(flow)上传输的数据包数量。注意,该关键字不是等到流结束时统计检测,而是在每个包的传输过程中都统计检测。
下面是该关键字的格式:
flow.pkts:<direction>,[op]<number>
下面是该关键字的使用示例:
flow.pkts:toclient,3 # 当前流,发送给客户端的数据包数量为3
flow.pkts:toserver,<3 # 当前流,发送给服务端的数据包数量小于3
flow.pkts:either,>=2 # 当前流,两端传输的数据包数量大于等于2
下面是规则的使用示例:
alert ip any any -> any any (msg:"Flow has 20 packets in toclient dir"; flow.pkts:toclient,20; sid:1;)
上面规则的含义是,当前流(flow)中发送给客户端的报文数量为 20 时产生告警。
flow.bytes
该关键字用于标识流(flow)上传输的数据包总量(字节数)。注意,该关键字不是等到流结束时统计检测,而是在每个包的传输过程中都统计检测。
下面是该关键字的格式:
flow.bytes:<direction>,[op]<number>
下面是该关键字的使用示例:
flow.bytes:toclient,3 # 当前流,发送给客户端的数据包大小总量为3字节
flow.bytes:toserver,<3 # 当前流,发送给服务端的数据包大小总量小于3字节
flow.bytes:either,>=2 # 当前流,两端传输的数据包大小总量大于等于2字节
下面是规则的使用示例:
alert ip any any -> any any (msg:"Flow has less than 2000 bytes in toserver dir"; flow.bytes:toserver,<2000; sid:1;)
上面规则的含义是,当前流(flow)中发送给服务端的报文总量小于 2000 字节时产生告警。
stream_size
“流大小(stream size)” 选项会根据通过序列号所记录的字节数量来对网络流量进行匹配。下面是该关键字的格式:
stream_size:<server|client|both|either>, <modifier>, <number>;
其中modifier 关键字支持>,<,=,!=,>=,<=。
下面是规则示例:
alert tcp any any -> any any (stream_size:both, >, 5000; sid:1;)
上面规则的含义是,客户端和服务器双向的流量字节数超过 5000 字节时,就会触发警报。