编写面向规避流量的防御性检测规则

约 35 分钟

能针对分片、编码与协议混淆等规避手法编写防御性检测规则,并在授权流量中验证。

上一节你已在 CentOS 上把自定义规则文件纳入 suricata.yaml 的规则文件列表,用 suricata-update 合并了威胁情报源,再以 -T 校验整套规则集,核对加载数量与自定义规则状态。规则能通过校验、按预期加载,但这只说明规则在“静止”状态下成立——攻击者想让你的规则失效。常见手段有三种:分片,把载荷拆成多个包,逐包匹配落空;编码,把特征字符串改写,签名对不上;协议混淆,把内容藏进别的字段,关键字摸不着。规则只按你写的样子找数据,流量改个形,检测就失灵。

这一节的立场是防御。你只在自己的授权靶场、本地 PCAP 与明确授权流量中练习。分片与编码在这里是你要识别并拦截的信号,不是要执行的手段。

验证闭环沿用社区推荐的流程:拿一份授权 PCAP,先在 Wireshark 里看清特征,再写规则,用 -r path-to-pcap 离线回放,最后查 fast.log 确认命中。规则上线前,先回放验证,再谈部署。

核心概念

规避防御规则:在重组与解码后的数据上设防

类比:安检口的拆件检查。攻击者把一件物品拆成多个包裹寄出,或者给物品换一套包装。安检员只检查单个包裹的外表,就会漏掉拆开的东西。可靠的安检流程会先重组拆散的包裹、揭掉包装,再检查里面的内容。你写规避防御规则,就要站到“重组与解码之后”的位置上。

定义:规避防御规则(evasion-defense-rules)是一类防御性规则,专门针对分片、编码与协议混淆等规避手法。它有两个要点。第一,匹配发生在 Suricata 解码、重组与归一化之后的数据上,而不是原始字节上;官方文档把这类匹配对象统称为归一化缓冲区。第二,在关键信号位置布点:IP 分片标志位、协议归一化缓冲区,让攻击特征无论怎样变形都落在你的规则视野内。

分片信号本身不是定罪证据,正常流量也会分片。组合式判据更可靠:把分片信号与载荷特征、协议与端口约束放在一起,用 content、flow 这些已有关键字收窄范围。

先看分片。IP 分片把一个大载荷拆成多个包,每个包只带一段。逐包匹配的规则只在第一个分片里看到特征开头,后续内容落在别的包里,规则落空。Suricata 提供两个 IP 层关键字:fragbits 匹配分片标志位,fragoffset 匹配分片偏移,二者都列在官方文档的 IP 关键字章节。

识别方法:在 Wireshark 里打开授权攻击 PCAP,看 IP 层的 MF(还有后续分片)标志与非零分片偏移。见到分片先问一句“这个流为什么分片”,再决定规则怎么收窄。抓包层面,af-packet 可开启 defrag: yes 让引擎重组后再检测;规则层面,你仍要在重组后的数据上布点。

分片检测常与状态关联配合:先用 flowbits 对分片信号置位,等后续包出现攻击特征再告警,把“可疑”与“实锤”分开。fragbits 与 fragoffset 的参数写法以你安装版本的官方文档为准。

再看编码。攻击者把特征字符串改写:百分号编码、大小写混用、多重编码。你在原始载荷上匹配明文串,改写一次就漏一次。

Suricata 的解法是归一化:对 HTTP 等协议,先把内容解码、规整,再放进归一化缓冲区供规则匹配。uricontent 这类关键字就是在归一化后的 URI 内容里找文本。你在归一化缓冲区上写一条规则,等于同时覆盖它的一堆编码变体,不必为每种写法各写一条。http 缓冲区关键字在上一节讲过,这里换一个视角:选择归一化缓冲区,本身就是一条规避防御手段。

写规则前先问自己:我想匹配“线路上长什么样”,还是“应用层看到什么”?规避防御关心后者。判断清楚了,再决定挂哪个缓冲区。

写两条防御性规则并在授权流量中验证

  1. 1

    准备两份 PCAP:授权攻击流量与正常背景流量。用 Wireshark 打开攻击 PCAP,记下两类特征:IP 层的分片标志(MF 位、非零分片偏移),以及 URI 里的编码写法。

  2. 2

    在自定义规则目录新建 evasion.rules,写入规则 A:在归一化 URI 缓冲区匹配攻击关键字并加 nocase。cmd 是占位关键字,请换成 PCAP 里的真实特征;方向按攻击流调整。

    suricata
    alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"ET EVASION encoded cmd in uri"; flow:to_server,established; content:"cmd"; http_uri; nocase; classtype:bad-unknown; sid:5000001; rev:1;)
  3. 3

    写入规则 B:用 fragbits 匹配设置了 MF 位(还有后续分片)的分片包。参数写法以你安装版本的官方文档为准。

    suricata
    alert ip $EXTERNAL_NET any -> $HOME_NET any (msg:"ET EVASION ip fragment seen"; fragbits:M+; sid:5000002; rev:1;)
  4. 4

    用上一节的 -T 命令校验 evasion.rules,报错则逐条修正。

    bash
    sudo suricata -T -c /etc/suricata/suricata.yaml -S /etc/suricata/rules/evasion.rules
  5. 5

    用 -r path-to-pcap 离线回放攻击 PCAP,确认 suricata.yaml 中 fast.log 已启用。

    bash
    sudo suricata -r /data/attack.pcap
  6. 6

    查看 fast.log,应出现 sid 5000001 与 sid 5000002。路径以 suricata.yaml 配置为准。

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

    回放正常背景 PCAP,确认没有这两条告警。若规则 B 误报,用第 5 章的 threshold 或收窄协议、端口。

    bash
    sudo suricata -r /data/normal.pcap

你应该看到

两条规则通过 -T 校验;回放授权攻击 PCAP 后,fast.log 中同时出现 sid 5000001(编码变体命中)与 sid 5000002(分片信号命中);回放正常背景 PCAP 不产生这两条告警。

遇到问题:规则通过了 -T 校验,但回放授权攻击 PCAP 后,fast.log 里找不到对应 sid 的告警。

可能原因

  • 规则挂在原始载荷缓冲区上找解码后的内容,或挂在归一化缓冲区上找原始形态,匹配对象错位。
  • 攻击特征跨越多个分片,逐包匹配看不到完整字符串。
  • fragbits 与 fragoffset 参数写法与安装版本不一致,规则匹配不到任何包。
  • fast.log 未在 suricata.yaml 中启用,事件只进了 eve.json。
  • PCAP 中特征与规则假设不一致:方向、端口、大小写或关键字与真实载荷不符。

排查步骤

  1. 用 Wireshark 重新核对特征在 PCAP 中的原始形态与解码形态,确认规则挂的缓冲区与之一致。
  2. 把规则挂到归一化缓冲区(如 http_uri)并加 nocase。
  3. 对照官方文档核实 fragbits 与 fragoffset 的参数写法。
  4. 检查 suricata.yaml 中 fast.log 输出是否启用,或直接查 eve.json 核对事件。
  5. 确认回放时规则文件被加载:-T 通过只代表语法正确,不代表文件进了加载路径。
自检:攻击者把特征字符串做了百分号编码并混用大小写,放进 HTTP 请求 URI。为什么在归一化缓冲区上匹配,比在原始载荷上匹配更能防住这种规避?

归一化缓冲区存放的是引擎解码、规整后的内容,百分号编码、大小写等变体在进入缓冲区前被统一成同一种表示。在归一化缓冲区上写一条规则,就能覆盖这些变体,不必为每种写法各写一条;在原始载荷上匹配,则每种变体都要单独写规则,还容易漏报。