实现多阶段攻击的 flowbits 关联规则

约 35 分钟

能编写 flowbits 规则链,在授权 PCAP 中检测先探测后利用的多阶段攻击。

上一课结束时,你已经能对同一条流维护 flowbits 具名布尔状态:set 写入、isset 检查、toggle 翻转,并用 flowbits:noalert 把前置条件静默记录下来,让分散在不同包里的多阶段行为关联成一条告警;两阶段规则示例、离线回放验证和常见故障排查也都跑通了一遍。这节课把这套机制搬到真实场景——多阶段攻击不会一次到位,攻击者先探测,再利用,行为散落在不同包里,任何单独一段都不足以告警。

你拿到的 multi-stage-attack.pcap 是课程提供的授权捕获,只用于离线回放。

任务是把两段行为串成一条链路:第一段静默记录,第二段才告警。set 与 isset 的语义你已在上一课掌握,这节课把它们组装成规则链。

工作流沿用已学做法:先用 Wireshark 分析流量,再写规则,用 -r 回放,最后核对 fast.log 与 eve.json。论坛把路径写得很直白:下载含攻击的 PCAP,离线用 -r path-to-pcap 回放,检查 fast.log 是否识别出威胁。视频的流程一样:先看协议统计,定位可疑请求,再据此写规则。

开始前确认两件事。第一,suricata.yaml 里 fast.log 输出已启用,论坛特别提醒过这个坑。第二,规则文件与第一课一致;默认配置下是 /var/lib/suricata/rules/suricata.rules。

核心概念

flowbits 规则链:先登记,后告警

类比:访客第一次刷卡,门禁只登记,不响铃。他要进入机房时,门禁先查登记记录,有记录才响铃。登记与告警是两次独立动作,但共享同一张卡。

定义:flowbits 规则链由两条规则组成,共享同一个 flowbits 名称。第一条规则匹配探测特征,执行 flowbits:set 打上标记,并用 noalert 抑制告警——只登记,不响铃。第二条规则匹配利用特征,先用 flowbits:isset 检查标记,标记存在才产生 alert——先查登记,再响铃。

两条规则必须落在同一个 flow 上。flowbits 状态与 flow 绑定:探测与利用不在同一 TCP 会话时,第一条规则的标记对第二条规则不可见,isset 永远不成立。noalert 是告警关键字,它让命中不产生告警事件,但 flowbits:set 照常执行。

场景拆解如下。两个特征字符串已固定在课程 PCAP 中,你用 Wireshark 自行核对,不要只信规则文本。

探测阶段:攻击者向受害 Web 服务发送 HTTP 请求,URI 中含 probe2024。这一步单独出现时只是低危噪音,不值得刷屏。 利用阶段:同一 TCP 会话内,攻击者再次发送 HTTP 请求,URI 中含 exploit2024。这是真正要告警的动作。

变量沿用默认语义:$HOME_NET 是受害侧,$EXTERNAL_NET 是攻击侧。两条规则都匹配 to_server 方向的已建立连接。

核对方法照搬视频思路:先看协议统计,过滤出 HTTP,逐条查看请求内容,确认两个特征确实出现在 PCAP 里。同时记下两次请求的 IP 与端口,后面核对 flow_id 要用。

编写并验证 flowbits 规则链

  1. 1

    用 Wireshark 打开课程 PCAP,按视频中的方法定位 HTTP 流量,确认探测请求 URI 含 probe2024、利用请求 URI 含 exploit2024。记下两次请求的 IP 与端口。

  2. 2

    在规则文件末尾追加第一条规则:匹配探测特征,设置标记 probe_seen,用 noalert 抑制告警。

    suricata
    alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"LAB multi-stage probe detected"; flow:to_server,established; http.uri; content:"probe2024"; nocase; flowbits:set,probe_seen; noalert; classtype:attempted-recon; sid:2006101; rev:1;)
  3. 3

    追加第二条规则:匹配利用特征,用 flowbits:isset 检查标记,标记存在才告警。

    suricata
    alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"LAB multi-stage exploitation after probe"; flow:to_server,established; http.uri; content:"exploit2024"; nocase; flowbits:isset,probe_seen; classtype:attempted-admin; sid:2006102; rev:1;)
  4. 4

    用第一课的测试模式校验两条规则的语法,报错先修正。

    bash
    sudo suricata -T -c /etc/suricata/suricata.yaml -S /path/to/your-rules
  5. 5

    回放课程 PCAP,命令格式与论坛一致;-S 只加载你的规则文件,避免默认规则集干扰。

    bash
    sudo suricata -r /path/to/multi-stage-attack.pcap -S /path/to/your-rules
  6. 6

    查看 fast.log:应只有 sid 2006102 的一条告警,sid 2006101 因 noalert 不出现。为空则先检查 suricata.yaml 的 fast.log 输出。

    bash
    sudo tail -n 20 /var/log/suricata/fast.log
  7. 7

    用 jq 从 eve.json 提取告警事件,核对 flow_id 是否等于 Wireshark 中探测所在 TCP 流的五元组。flowbits 状态不直接显示,验证分两步:flow_id 归属同一 flow,再做第 8 步反向验证。

    bash
    cat /var/log/suricata/eve.json | jq 'select(.event_type=="alert") | {sig_id: .alert.signature_id, flow_id, src_ip, dest_ip, dest_port}'
  8. 8

    反向验证:注释掉第一条规则的 flowbits:set 或整条规则,重新回放,第二条规则不应告警;恢复后再次回放,告警复现。这证明告警依赖第一阶段标记。

你应该看到

回放课程 PCAP 后,fast.log 只出现 sid 2006102 的一条告警,sid 2006101 不产生告警事件;eve.json 中该告警的 flow_id 与探测所在 TCP 流一致。注释掉 flowbits:set 后重放,sid 2006102 不再告警,恢复后告警复现。整体满足:仅在第二阶段命中告警,且关联到同一 flow。

遇到问题:回放后 fast.log 没有 sid 2006102 的告警,或出现了本不该出现的 sid 2006101。

可能原因

  • 特征字符串与 PCAP 实际内容不符:大小写、URL 编码或换行导致 content 未命中。
  • 第一条规则没有命中,probe_seen 从未被设置。
  • 两条规则不在同一 flow:协议、方向或端口设置不一致。flowbits 状态按 flow 隔离,跨 flow 的 isset 永远不成立。
  • fast.log 输出未在 suricata.yaml 中启用。

排查步骤

  1. 回到 Wireshark 复核特征字符串,从 PCAP 复制原始字节粘贴进 content。
  2. 临时去掉第一条规则的 noalert,重放确认它能命中;确认后再加回 noalert。
  3. 核对两条规则的协议、方向与端口,确保它们匹配同一 TCP 流。
  4. 检查 suricata.yaml 的 fast.log 输出设置,论坛提示过这是常见坑。
自检:第一条规则为什么必须加 noalert?如果探测与利用发生在两个不同的 TCP 流,第二条规则还会告警吗?为什么?

noalert 让第一条规则命中时不产生告警事件,只执行 flowbits:set 登记,探测噪音不会刷屏。第二条规则用 flowbits:isset 检查标记,标记存在才告警。flowbits 状态与 flow 绑定:探测与利用不在同一 flow 时,标记对第二条规则不可见,isset 不成立,第二条规则不会告警。这正是把两次匹配绑定到同一会话的机制。