用 DNS 与 TLS 关键字检测隧道与恶意域

约 30 分钟

能使用 dns.query 与 tls.sni、tls.cert_subject 等关键字检测恶意域名与可疑 TLS 特征。

上一课收尾时,你已经用 http_uri、http_header、http_client_body 三个缓冲区各写了一条规则,分别盯住目录穿越、扫描器 User-Agent 和请求体注入;在授权 PCAP 回放后,fast.log 与 eve.json 都验证了命中,正常流量也确认无误报。这一课把同样的打法原样搬到 DNS 与 TLS:攻击者要让恶意软件干活,得先让流量“说话”——恶意软件找服务器,会发起 DNS 查询;要藏内容,会建立 TLS 连接。这两步都留下痕迹:DNS 查询报出要找的域名,TLS 握手亮出要连的服务器。Suricata 官方规则文档为这两类协议单列了 DNS Keywords 与 SSL/TLS Keywords 两节关键字。规则质量决定引擎价值:规则写不准,要么漏掉威胁,要么被误报淹没。这一课的目标只有一个:检测恶意域名与隧道。

核心概念

dns.query:匹配查询名称

类比:DNS 像电话查号台。客户端报出要找的名字,查号台才回给号码。查号记录里最显眼的,就是被报出来的那个名字。

定义:dns.query 是 DNS 协议缓冲区关键字,匹配 DNS 查询中的查询名称(query name),也就是客户端请求解析的域名。它先声明缓冲区,再由 content 指定要找的字节,nocase 负责大小写。一次对已知恶意域名的查询本身就是告警信号——它可能发生在攻击开始之前,也可能是被控主机的持续回连。匹配思路分两层:已知坏域名用精确字符串;未知域名看结构特征,例如子域名层级多、标签又长又随机。后者可以借助已学的 pcre 作用在 dns.query 缓冲区上。结构匹配误报偏高,适合当作线索,而不是直接下结论。

核心概念

tls.sni:匹配握手中的服务器名称

类比:TLS 内容加密后像一封封好的信。寄件人必须在信封外写上收件人名字,邮局才知道往哪儿送。写在信封外的名字就是 SNI。

定义:tls.sni 是 TLS 协议缓冲区关键字,匹配 TLS 握手中客户端声明的服务器名称(Server Name Indication),也就是客户端要连接的域名。加密内容对监听者不可读,但服务器名称在握手中可见。恶意隧道常把流量伪装成普通 TLS,目的地就暴露在 SNI 里。用 tls.sni 加 content 精确匹配域名,就能在不解密的情况下回答“它连向了哪里”。注意 SNI 不是必然出现:部分客户端不发 SNI,因此没命中不代表安全,只代表这个入口没有可用信息。把 tls.sni 与 dns.query 放进同一条规则,要求 DNS 查询与后续 TLS 连接指向同一域名,可以抵消单点误报。

核心概念

tls.cert_subject:匹配证书主体

类比:服务器证书像一张身份证。tls.cert_subject 读的是身份证上“姓名”那一栏。

定义:tls.cert_subject 是 TLS 协议缓冲区关键字,匹配服务器证书的 subject 字段,即证书签发给谁。它常与 tls.sni 放进同一条规则交叉验证:SNI 声称连 A 域名,证书却发给 B 主体,这种不一致值得警惕,常见于冒用证书的隧道。证书字段同样配合 content 精确匹配主体名称。判断时别只看一个字段:SNI 与证书主体一致时,会话更可能是正常的;两者不一致才进入疑点名单。若某个会话根本没有可用的证书主体信息,先排除未完成握手的流量,再决定是否告警。

检测条件按两类目标设计。目标一:恶意域名。用 dns.query 或 tls.sni 对已知坏域名精确匹配,大小写交给 nocase。规则头里的 $HOME_NET 与 $EXTERNAL_NET 是 Suricata 内置变量,默认分别代表内网地址段与其余地址。目标二:可疑隧道。查询名出现多级长随机标签时,用已学的 pcre 在 dns.query 缓冲区上匹配特征;TLS 侧检查 SNI 与证书主体是否一致。下面是三条起步规则:

text
alert dns $HOME_NET any -> any any (msg:"已知恶意域名查询"; dns.query; content:"evil.example.com"; nocase; sid:3000101; rev:1;)

alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"可疑 TLS SNI"; tls.sni; content:"evil.example.com"; nocase; sid:3000102; rev:1;)

alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"TLS 证书主体与业务无关"; tls.cert_subject; content:"unrelated-org.com"; nocase; sid:3000103; rev:1;)

前两条分别命中 DNS 查询与 TLS 握手,同一个域名两个入口都覆盖。第三条把检测重点放在证书:当证书主体明显与业务无关时单独告警。规则沿用七段结构;域名、msg、sid 按你的环境替换。

验证规则需要一份真实流量样本。官方论坛推荐的做法是:下载一份含恶意软件活动的 PCAP,先在 Wireshark 里分析威胁行为,再按分析结果写规则,最后离线回放并用 fast.log 确认是否命中。练习样本可以来自可信的公开 PCAP 集,也可以是自己搭建授权靶场抓的包。样本只用于本地离线分析,不要放进生产网络,也不要针对未授权目标现场采集。

动手:用离线 PCAP 验证 DNS 与 TLS 规则

  1. 1

    用 Wireshark 打开一份授权 PCAP,过滤出 DNS 查询与 TLS 握手,记下真实出现的查询名称与服务器名称。

  2. 2

    在自定义规则文件夹里新建规则文件,写入下面这条 DNS 规则,域名替换成 PCAP 中的实际值。

    suricata
    alert dns any any -> any any (msg:"DNS 已知恶意域查询"; dns.query; content:"evil.example.com"; nocase; sid:3000104; rev:1;)
  3. 3

    用上一课学过的测试模式校验规则文件语法,确认没有报错再继续。

  4. 4

    对同一份 PCAP 离线回放,命令格式为 -r path-to-pcap,路径换成你的实际文件。

    bash
    sudo suricata -r path-to-pcap
  5. 5

    打开 fast.log 查看告警,确认 DNS 规则命中;若未命中,用 Wireshark 核对查询名的大小写与拼写。

  6. 6

    在同一个文件里追加两条 TLS 规则,分别匹配实际 SNI 与证书 subject,随后重新回放。

    suricata
    alert tls any any -> any any (msg:"TLS 可疑 SNI"; tls.sni; content:"evil.example.com"; nocase; sid:3000105; rev:1;)
    alert tls any any -> any any (msg:"TLS 证书主体可疑"; tls.cert_subject; content:"unrelated-org.com"; nocase; sid:3000106; rev:1;)
  7. 7

    用 jq 从 eve.json 中筛选对应 sid 的事件,核对事件里的 dns 与 tls 字段是否与规则关键字一致。

你应该看到

fast.log 中同时出现 DNS 与 TLS 规则的告警;eve.json 对应事件带有 dns.query 命中的查询名称,以及 tls 的 sni、cert_subject 字段值。整个过程只在本地 PCAP 上完成,没有向任何外部主机发包。

遇到问题:回放后,TLS 规则一条告警都没有。

可能原因

  • PCAP 中的 TLS 握手没有携带 SNI,tls.sni 缓冲区为空,规则无从匹配。
  • 规则里的域名与 PCAP 实际值不一致,或大小写、尾点不同。
  • 规则文件未被加载,或 fast.log 输出没有在 suricata.yaml 中启用。
  • 规则协议字段与实际流量不符,规则作用在了错误的协议上。

排查步骤

  1. 用 Wireshark 过滤出 TLS 握手,确认是否存在 SNI 字段及其确切内容。
  2. 按实际值修正 content 与 nocase,重新回放。
  3. 确认 fast.log 输出已在 suricata.yaml 的相应设置中启用,再运行 -r path-to-pcap。
  4. 若该流量确实没有 SNI,改用证书字段条件(例如 tls.cert_subject)重新验证。
自检:你发现内网主机建立了一条 TLS 连接:SNI 指向域名 A,服务器证书的 subject 却属于组织 B。要检测这种异常,你会组合哪两个关键字?它们分别匹配什么?

用 tls.sni 匹配客户端在握手中声明的服务器名称,用 tls.cert_subject 匹配证书 subject 字段(证书签发给谁)。把两者放进同一条规则,当 SNI 与证书主体不一致时产生告警——这是识别冒用证书隧道的典型检测条件。

选关键字的判断标准很简单:查域名的流量看 dns.query;TLS 加密会话看 tls.sni 与证书字段。规则要能回答“为什么这个域名可疑”,而不是只拼一个字符串。dns.query、tls.sni、tls.cert_subject 的完整清单与可用修饰符,见 Suricata 官方文档的 DNS Keywords 与 SSL/TLS Keywords 两节。下一课引入 flow 关键字,把这些单点告警串成多阶段检测。