构建 DNS 与 TLS 检测规则并离线验证

约 35 分钟

能编写 DNS 恶意域与 TLS 可疑 SNI 规则,并通过 PCAP 回放与 eve.json 验证告警。

上一节我们已经在离线回放中,通过 fast.log 与 eve.json 的核对验证了检测条件的设计。现在把这些验证过的知识固化成可运行的规则,走完「分析 → 写规则 → 回放 → 核对」的完整闭环。

先看一个类比。安检员不会凭空判断谁可疑:先看录像,再对照通缉名单,最后重放录像确认抓到了谁。你也一样。先用 Wireshark 观察 PCAP,从 Statistics → Protocol Hierarchy 看协议分布,判断该优先分析哪些协议;再把恶意域名名单转成规则;接着用 suricata -r path-to-pcap 离线回放;最后核对 fast.log 的告警,并用 jq 读取 eve.json 的字段。

回放走离线模式,不依赖真实网卡。告警落在两个输出里:fast.log 是纯文本告警行,适合快速确认是否命中;eve.json 是结构化 JSON,能读到域名等字段细节。jq 是官方推荐用来查看 EVE JSON 输出的工具。

整个实验只使用本地 PCAP 与授权流量,延续上一节全程遵守的被动观察与授权边界。

把恶意域名名单写成 dns.query 规则

  1. 1

    在 Wireshark 的 Statistics → Protocol Hierarchy 确认 PCAP 里存在 DNS 与 TLS 流量,记下要检测的恶意域名。先看协议分布,再决定优先写哪类规则。

  2. 2

    新建本地规则文件。规则默认安装在 /var/lib/suricata/rules,默认规则集文件是 suricata.rules。创建 /var/lib/suricata/rules/local-dns-tls.rules,并把该文件加入 suricata.yaml 的规则文件列表;路径写错时运行会直接报错。

    bash
    sudo touch /var/lib/suricata/rules/local-dns-tls.rules
  3. 3

    名单里的每个域名转成一条 alert。沿用上一节的 dns.query 写法,content 精确匹配查询域名,nocase 忽略大小写:

    suricata
    alert dns any any -> any any (msg:"MALWARE DNS query for evil.example.com"; dns.query; content:"evil.example.com"; nocase; classtype:bad-unknown; sid:1000001; rev:1;)
  4. 4

    名单有多少个域名就写多少条规则。sid 依次递增且全局唯一,msg 里的域名与 content 保持一致。

  5. 5

    用第一课学过的测试模式检查语法,确认无 error 后再进入回放。

    bash
    sudo suricata -T -c /etc/suricata/suricata.yaml

你应该看到

测试模式无 error,规则语法通过;若启动日志提示找不到文件,说明 suricata.yaml 中的路径写错,修正后重新测试。

编写 TLS 可疑 SNI 规则

  1. 1

    在 Wireshark 中定位可疑的 TLS 握手流量。ClientHello 的 SNI 字段携带客户端要连接的域名,跟随 TCP 流查看完整交互,确认目标域名。写规则前先在 Wireshark 里检查流量,是常规做法。

  2. 2

    用 tls.sni 匹配 SNI,规则骨架与 DNS 规则相同,协议字段改为 tls。沿用上一节的 tls.sni 写法:

    suricata
    alert tls any any -> any any (msg:"SUSPICIOUS TLS SNI evil.example.com"; tls.sni; content:"evil.example.com"; nocase; classtype:bad-unknown; sid:1000002; rev:1;)
  3. 3

    方向沿用第一课学过的写法即可,SNI 由客户端发往服务器,无需新增关键字。

  4. 4

    把 TLS 规则追加到同一个 local-dns-tls.rules,sid 继续递增且不与已有规则重复。

  5. 5

    再次运行测试模式,确认两条规则都通过语法检查。

    bash
    sudo suricata -T -c /etc/suricata/suricata.yaml

你应该看到

DNS 与 TLS 规则全部通过语法检查,sid 无冲突,测试模式无 error。

回放 PCAP 并核对 fast.log 与 eve.json

  1. 1

    用离线模式回放 PCAP,命令格式就是 suricata -r path-to-pcap,path-to-pcap 换成你的实验抓包路径。

    bash
    sudo suricata -r /path/to/lab-dns-tls.pcap
  2. 2

    查看 fast.log 的新增告警行,确认 msg 与 sid 对应你写的规则。fast.log 为空时,先确认 suricata.yaml 里启用了 fast 输出——这一点官方特别提醒过。

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

    用 jq 从 eve.json 过滤 DNS 事件,核对告警中的域名。

    bash
    jq 'select(.event_type == "dns") | .dns' /var/log/suricata/eve.json
  4. 4

    同样过滤 TLS 事件,核对 tls.sni 字段与规则中的域名一致。

    bash
    jq 'select(.event_type == "tls") | .tls' /var/log/suricata/eve.json
  5. 5

    某个域名没告警时,回到 Wireshark 核对查询中的域名大小写与拼写,再检查规则 content 是否完全一致。

你应该看到

fast.log 出现对应的 DNS 与 TLS 告警行;eve.json 中 event_type 为 dns / tls 的事件分别包含 dns.query / tls.sni 字段,且值与规则中的恶意域名一致。

遇到问题:回放结束后 fast.log 没有任何新增告警,但规则已通过测试模式。

可能原因

  • suricata.yaml 中没有启用 fast 输出;官方提醒过不要忘记在 suricata.yaml 设置中启用 fast.log 输出。
  • suricata.yaml 中引用的规则文件路径不正确,运行日志会提示找不到文件。
  • PCAP 里实际没有你要检测的域名或协议,先回 Wireshark 用协议层级与 DNS/TLS 过滤器确认。
  • 域名大小写或拼写与 PCAP 中的查询不一致,content 匹配不到。

排查步骤

  1. 查看运行输出与 /var/log/suricata/suricata.log,确认规则文件被加载、fast 输出已启用。
  2. 用 jq 按 event_type 过滤 eve.json:判断是事件没产生,还是产生了但没触发告警。
  3. 在 Wireshark 用 Statistics → Protocol Hierarchy 与 DNS/TLS 过滤条件核对流量内容。
  4. 修正规则中的域名写法或启用 nocase 后重新回放。
自检:你写了一条 dns 规则和一条 tls 规则,回放后 fast.log 有告警。为了确认告警分别来自 DNS 查询与 TLS 握手,你该查看 eve.json 中的哪些字段来区分?

按事件类型过滤。event_type 为 dns 的事件看 dns.query 字段,该字段应等于 dns 规则中的恶意域名;event_type 为 tls 的事件看 tls.sni 字段,该字段应等于 tls 规则中的可疑域名。用这两个字段把两类告警分开核对即可。