1614 字
8 分钟
Suricata 和 Snort3 流量分析实战:从 PCAP 到告警

最近在做网络安全的 lab,需要用 Suricata 和 Snort3 对 PCAP 文件做流量分析。整个过程踩了不少坑,尤其是 Snort3 和 Snort2 的配置差异,以及 sticky buffer 这个概念。把完整的排查过程记录下来,方便以后回看。

Suricata:用 jq 过滤 EVE JSON 日志#

Suricata 默认的 JSON 日志输出叫 EVE(Extensible Event Format),文件名通常是 eve.json。它把所有事件——alert、http、dns、tls、flow、ssh 等——都写进同一个 JSON 文件里,每行一个 JSON 对象。

这就带来了一个问题:文件里各种事件混在一起,直接看根本没法用。这时候 jq 就派上用场了。

过滤 HTTP 事件#

目标很简单:从 /var/log/suricata/old_eve.json 里只提取 HTTP 事件。

Terminal window
jq 'select(.event_type=="http")' /var/log/suricata/old_eve.json

select() 是 jq 的过滤函数,只保留满足条件的 JSON 对象。输出结果:

{
"timestamp": "2023-07-06T08:34:35.859970+0000",
"flow_id": 1252204100696793,
"event_type": "http",
"src_ip": "xxx.xxx.xxx.xxx",
"dest_ip": "xxx.xxx.xxx.xxx",
"dest_port": 80,
"http": {
"hostname": "adv.epostoday.uk",
"url": "/app.php",
"http_method": "GET",
"status": 200,
"length": 423
}
}

一共过滤出 3 条 HTTP 事件,全部属于同一个 flow(flow_id: 1252204100696793)。从源 IP 到目标 adv.epostoday.uk 的 80 端口,请求了 /app.php/favicon.ico

TIP

flow_id 是 Suricata 给每个网络流分配的唯一标识。同一个 TCP 连接中的所有事件共享同一个 flow_id,用它可以快速关联同一会话的所有日志。

启用 HTTP 日志并分析 PCAP#

Suricata 除了 EVE JSON,还有一个专门的 HTTP 日志模块——http-log。默认是关闭的,需要手动启用。

打开 /etc/suricata/suricata.yaml,找到 http-log 部分:

/etc/suricata/suricata.yaml
- http-log:
enabled: no # 改成 yes
filename: http.log
append: yes

enabled: no 改成 enabled: yes。用 sed 一行搞定:

Terminal window
sudo sed -i '/http-log:/{n;s/enabled: no/enabled: yes/}' /etc/suricata/suricata.yaml

这个 sed 命令的意思是:找到包含 http-log: 的行,跳到下一行(n),然后把 enabled: no 替换成 enabled: yes。之所以要用这种写法,是因为 YAML 文件里可能有多个 enabled: no,直接全局替换会出问题。

然后用 Suricata 离线分析 PCAP 文件:

Terminal window
sudo suricata -r /home/htb-student/pcaps/suspicious.pcap

-r 参数让 Suricata 读取 PCAP 文件而不是监听网卡。处理完之后,/var/log/suricata/http.log 就生成了:

/var/log/suricata/http.log
12/24/2022-17:31:06.242839 adv.epostoday.uk[**]/app.php[**]Mozilla/5.0 ...
12/24/2022-17:31:11.899970 adv.epostoday.uk[**]/app.php[**]Mozilla/5.0 ...
12/24/2022-17:31:12.305197 adv.epostoday.uk[**]/favicon.ico[**]Mozilla/5.0 ...

http.log 是行格式的日志,用 [**] 分隔字段。可以看到请求的 PHP 页面就是 app.php

和 EVE JSON 相比,http.log 更轻量,适合快速浏览 HTTP 流量概况。但如果需要结构化查询或者和其他事件类型关联分析,还是 EVE JSON + jq 更灵活。

Snort3:规则、DAQ 和 PCAP 分析#

接下来切换到 Snort3。和 Suricata 的 YAML 配置不同,Snort3 用 Lua 做配置语言,主配置文件叫 snort.lua

Snort3 vs Snort2 的配置差异#

这是我踩的第一个坑。直接跑 snort -c /etc/snort/snort.conf 报错找不到文件——因为 Snort3 根本不用 snort.conf

Snort3 的配置体系:

  • snort.lua — 主配置文件,定义 HOME_NET、模块参数、检测引擎选项等
  • snort_defaults.lua — 默认参数,被 snort.lua 引用
  • .rules 文件 — 规则文件,语法和 Snort2 基本兼容但有扩展

在我操作的环境里,配置文件在 /root/snorty/etc/snort/snort.lua。里面 HOME_NET 默认设为 'any'

运行 Snort 分析 PCAP#

分析 wannamine.pcap,加载 local.rules 里的规则:

Terminal window
sudo snort -c /root/snorty/etc/snort/snort.lua \
--daq-dir /usr/local/lib/daq \
-r /home/htb-student/pcaps/wannamine.pcap \
-R /home/htb-student/local.rules \
-A cmg

参数说明:

  • -c — 指定主配置文件
  • --daq-dir — DAQ(Data Acquisition)模块路径,Snort3 通过 DAQ 抽象层读取数据源
  • -r — 读取 PCAP 文件
  • -R — 加载额外的规则文件(不在 snort.lua 中 include 的规则)
  • -A cmg — 告警输出格式,cmg 会显示规则信息、包头和载荷

local.rules 里的第一条规则很简单:

local.rules
alert icmp any any -> $HOME_NET any (msg:"ICMP test"; sid:1000001; rev:1; classtype:icmp-event;)

这条规则会对所有进入 HOME_NET 的 ICMP 包触发告警。

运行结果:sid:1000001 触发了 234 次wannamine.pcap 里有大量 ICMP 流量,这在挖矿相关的网络行为中不算意外——矿池通信和网络探测都可能产生 ICMP 包。

Log4Shell 规则与 Sticky Buffer#

这是最有意思的部分。local.rules 里有一条被注释掉的 Log4Shell 检测规则:

#alert http any any -> any any (msg:"Log4shell Attempt Detected";
content:"|24 7b|jndi|3a|ldap|3a 2f 2f|",fast_pattern, nocase;
sid:10000098; rev:1;)

这条规则检测的是 Log4Shell(CVE-2021-44228)的经典 payload 模式:${jndi:ldap://。content 里用十六进制表示了特殊字符:|24 7b|${|3a|:|2f 2f|//

问题来了:去掉注释直接跑,对着 log4shell.pcap 一个告警都不触发。为什么?

因为这条规则缺少 sticky buffer 声明。

什么是 Sticky Buffer#

在 Snort3 中,sticky buffer 是一个关键概念。它告诉检测引擎应该在数据包的哪个部分去匹配 content。

没有 sticky buffer 时,content 默认匹配原始 payload。但对于 HTTP 协议,Snort3 的 http_inspect 模块会把请求拆解成各个字段——URI、header、body 等。如果你想匹配特定字段,必须用对应的 sticky buffer。

常用的 HTTP sticky buffer:

Keyword匹配范围
http_uriHTTP URI
http_headerHTTP 头部(包括 User-Agent、Host 等)
http_client_bodyHTTP 请求体
http_methodHTTP 方法(GET、POST 等)
NOTE

Sticky buffer 这个名字的意思是:一旦声明了某个 buffer,后续所有 content 匹配都”粘”在这个 buffer 上,直到声明下一个 buffer。这是 Snort3 替代 Snort2 PCRE 修饰符(B, U, P, H 等)的新机制。

修复规则#

题目说 payload 在 User-Agent 里。User-Agent 是 HTTP header 的一部分,所以需要在 content 前加上 http_header

alert http any any -> any any (msg:"Log4shell Attempt Detected";
http_header;
content:"|24 7b|jndi|3a|ldap|3a 2f 2f|",fast_pattern, nocase;
sid:10000098; rev:1;)

加上 http_header; 之后重新跑:

Terminal window
sudo snort -c /root/snorty/etc/snort/snort.lua \
--daq-dir /usr/local/lib/daq \
-r /home/htb-student/pcaps/log4shell.pcap \
-R /home/htb-student/local.rules \
-A cmg

这次触发了 15 条告警。攻击者在 HTTP 请求的 User-Agent 字段里嵌入了 ${jndi:ldap://...} payload,试图触发目标服务器上 Log4j 的 JNDI 查找,从而实现远程代码执行。

WARNING

实际环境中 Log4Shell 的 payload 会有各种混淆变体,比如 ${${lower:j}ndi:ldap://...} 或嵌套 ${env:...}。单纯匹配明文 pattern 不够,生产环境需要更复杂的规则集。

总结#

这次 lab 覆盖了两个主流开源 IDS 的核心工作流:

Suricata 部分

  • jq select() 过滤 EVE JSON 是最常用的分析手段
  • http-log 模块默认关闭,需要手动启用
  • -r 参数做离线 PCAP 分析

Snort3 部分

  • 配置从 snort.conf 迁移到了 snort.lua(Lua 语言)
  • DAQ 抽象层需要用 --daq-dir 指定模块路径
  • Sticky buffer 是 Snort3 规则编写的核心概念——不指定 buffer,HTTP 层的 content 匹配就不会命中
  • http_header 覆盖所有 HTTP 头部字段,包括 User-Agent

最大的收获是理解了 sticky buffer。Snort2 时代用 PCRE 修饰符来指定匹配位置,语法晦涩。Snort3 改用 sticky buffer 后语义更清晰,但也意味着写规则时必须显式声明匹配范围,否则规则就是哑的。

Suricata 和 Snort3 流量分析实战:从 PCAP 到告警
https://blog.lishuyu.top/posts/suricata-snort流量分析实战/
作者
猫猫魔女
发布于
2026-03-26
许可协议
CC BY-NC-SA 4.0