能使用 flow 关键字限制规则在 established、to_server、to_client 等会话状态下匹配。
上一课你完成了从规则到验证的闭环:把恶意域名名单转成 dns.query 规则、用 tls.sni 编写可疑 TLS 域名规则,再用 suricata -r 离线回放 PCAP,对照 fast.log 与 eve.json 确认命中。那几类规则判断的是"连接去往哪里、协议字段长什么样";而这一课要回答"这条连接里,谁在对谁说话"。你已会用 content 匹配载荷里的字节。可同一个字节串,往往既出现在请求里,也出现在响应里。攻击者把命令发给服务器,载荷走向 to_server;服务器把执行结果回显给客户端,同样的字符串走向 to_client。只看载荷,分不清是谁在说话。
flow 关键字补上这个维度。它写在规则选项的括号内,让 Suricata 只检查某个会话方向、某个建立状态下的包。规则头里的方向箭头(->)描述源到目的,前面已经学过;flow 描述的是相对客户端与服务器的通信方向。两者配合使用。
核心概念
flow:给匹配加上会话方向与状态
生活类比:想象一段电话通话。你不仅要知道对方说了什么,还要知道是谁在说,以及电话是否已经接通。“客户端在说话”“服务器在说话”“接通之后的对话”——三件事彼此独立,又经常一起使用。
技术定义:flow 是规则选项括号内的关键字,多个值用逗号分隔,例如 flow:to_client,established;。to_server 限定匹配必须出现在发往服务器的通信中;to_client 限定匹配必须出现在发往客户端的通信中;established 限定只检查已建立连接中的包,从而减少无效匹配、降低引擎负载。三个值可以单独用,也可以组合,组合时用逗号连接。
flow 不负责匹配什么字节——那是 content 的职责;它只决定这段匹配发生在哪个方向、哪个状态。
方向判断最常见的用途,是区分请求与响应。Coralogix 的说明很直接:“如果我们要检测表明服务器已被入侵的服务器响应,用 flow 指出这段内容出现在 to_client 方向就很有用。”
很多攻击链路里,关键证据恰恰出现在响应方向:命令执行的回显、被篡改页面的返回、异常的错误页。这些字节串在请求方向几乎没有正常出现的理由。Suricata 文档自带一条现成的告警规则,可以拿来对照:
alert ip any any -> any any (msg:"GPL ATTACK_RESPONSE id check returned root"; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:2100498; rev:7;)它没有 flow,也不限定协议,任何方向出现“uid=0(root)”都会告警。按同样的检测意图,收窄成 TCP 规则,并加上方向与状态:
alert tcp any any -> any any (msg:"ATTACK_RESPONSE uid=0(root) to client"; flow:to_client,established; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:100001; rev:1;)改动只在选项区:多了 flow:to_client,established;。现在,只有已建立 TCP 连接中、服务器发往客户端方向的包出现该内容时,才会告警。
flow 与你已学的 content 修饰符各管各的。depth、offset、distance、within 决定某一段 content 在载荷里的匹配位置;flow 决定整条规则检查哪些包。两者不冲突,可以放心叠用。例如先由 flow:to_client,established; 过滤出已建立的响应包,再由 content 与 depth、within 在剩余包里精确定位特征。
一个实用的阅读习惯:看到一条陌生规则,先看括号里的 flow,确认它检查的方向与状态,再看 content 与修饰符。方向与状态先过滤掉大量无关包,剩下的载荷匹配才更有意义。
established 让 Suricata 只检查已建立连接中的包。Coralogix 的表述是:“这有助于把 Suricata 的负载降到最低。”大型流量里,握手包与半开连接占比不低,多数检测内容并不会出现在那里。方向约束同样减少无效匹配:to_server 与 to_client 把匹配锁在其中一个通信方向,同一段字节串不再在错误的对话方向里反复试探。规则更窄,告警更准,引擎更轻。
本课的验证全部使用本地授权 PCAP:你自己捕获的流量,或靶场提供的文件。离线回放不接触生产网络。未经授权,不要在任何真实网络上试验规则——无论是你自己的规则还是文档示例。
验证方向与状态约束
- 1
准备一份本地授权 PCAP(靶场流量或自建请求/响应流量),并确认 suricata.yaml 中 fast.log 输出已启用。
- 2
在自定义规则文件中加入两条规则:一条带 flow:to_client,established;,一条不带 flow 作对照(只改 msg 与 sid)。
textalert tcp any any -> any any (msg:"ATTACK_RESPONSE uid=0(root) to client"; flow:to_client,established; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:100001; rev:1;) alert tcp any any -> any any (msg:"对照:uid=0(root) 任意方向"; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:100002; rev:1;) - 3
用你之前学过的测试模式(-T)检查两条规则的语法。若报错,先修正再继续。
- 4
离线回放:suricata -r path-to-pcap(把 path-to-pcap 换成 PCAP 的实际路径)。
- 5
查看 fast.log 与 eve.json,记录两条规则的告警分布。
- 6
把规则里的 flow:to_client 改成 flow:to_server,重新回放,观察告警如何变化。
你应该看到
语法检查通过。回放后 fast.log 出现两条规则的告警:若 PCAP 中该字节串同时出现在请求与响应方向,带 flow 的规则只在响应方向(to_client)告警,对照规则两个方向都告警;若它只出现在请求方向,带 flow 的规则完全不告警。改成 to_server 后,告警方向随之反转。这些差异就是方向与状态约束的效果。
遇到问题:同一 PCAP 回放,规则加上 flow:to_client,established; 后不再产生任何告警。
可能原因
- 匹配内容实际出现在请求方向(客户端发往服务器),而规则只允许 to_client 方向。
- PCAP 只有握手或单方向流量,没有构成 established 会话,established 把整段流量排除在外。
- fast.log 未在 suricata.yaml 中启用,或回放命令的输出路径与配置不一致。
排查步骤
- 用 Wireshark 检查该内容所在包的方向:由服务器发出的用 to_client,由客户端发出的用 to_server。在 Wireshark 里分析流量、再据此写规则,是 Suricata 官方论坛推荐的做法。
- 确认 PCAP 包含完整双向会话;若只有单方向流量,先去掉 established 只保留方向值,并在规则注释里说明取舍。
- 检查 suricata.yaml 中 fast.log 输出设置与回放命令的路径参数,必要时直接看 eve.json 确认告警是否生成。
自检:要检测“服务器被入侵后的响应内容”,规则应使用哪组 flow 值?为什么?
应使用 flow:to_client,established;。to_client 把匹配限定在服务器发往客户端的通信中,客户端请求里出现相同字节串时不会误报;established 限定只检查已完成握手的已建立连接中的包,握手包与孤立包不参与匹配,从而减少无效匹配并降低 Suricata 的负载。