能使用 distance、within 描述内容间相对位置,并用 pcre 编写复杂正则匹配。
上一课收尾时,你用 nocase 堵住大小写变体漏报,用 depth 与 offset 把匹配框进精确窗口,并在测试模式与离线回放中对比 fast.log 的目标 sid 行数,确认命中减少而目标告警保留。但窗口再精确,单条 content 也只能确认“某个字符串出现了”。真实攻击特征往往是组合:检测攻击响应时,uid=0(root) 比单独的 root 更有说服力;检测命令注入时,cmd= 后面跟着 /bin/sh 才是完整特征。上一课的 offset 与 depth 相对缓冲区起点做绝对定位;本节换一种思路:让第二个匹配相对第一个匹配定位。distance 与 within 负责这种相对位置,pcre 负责 content 表达不了的字符模式。三者都是载荷内容匹配,配合使用才能既准又快。
核心概念
distance 与 within:两个内容之间的相对位置
类比:想象在一排书里找两本书。第一本 A 已经确定了位置,现在要找 B。你可以规定:B 必须紧挨着 A;B 必须离 A 不超过三本的距离;B 必须离 A 至少两本。三种规定对应三种不同的“相对位置”。distance 与 within 就是 Suricata 表达这种相对位置的修饰符,跟在 content 之后,作用于紧随其后的下一个 content。
技术定义:
- distance:N —— 下一个 content 的匹配起点,距上一个 content 匹配结束至少 N 字节;N 为 0 表示紧接在上一匹配之后。
- within:N —— 下一个 content 的匹配起点,距上一个 content 匹配结束最多 N 字节。
- 同时使用 distance:N; within:M; —— 起点落在上一匹配结束之后 N 到 M 字节的区间内,N 是下限,M 是上限。
注意:这里的距离从上一个匹配的结束位置起算,而不是从缓冲区开头。offset 与 depth 相对缓冲区起点做绝对定位,distance 与 within 相对上一个匹配的终点做相对定位。每个 distance/within 只作用于书写顺序上紧随其后的那一个 content。
下面这条规则把两个修饰符组合使用。第一个 content 命中 login 后,第二个 content admin 的起点必须落在 login 结束位置之后 1 到 8 字节之间:
alert tcp any any -> any any (msg:"login followed by admin within 8"; content:"login"; content:"admin"; distance:1; within:8; sid:200001; rev:1;)只有 distance 没有 within 时,第二个匹配可以出现在缓冲区任意远处,等于没有上限——这正是误报的来源之一。写规则时尽量同时给出上下限。相比两个互不相关的 content,distance/within 表达了“相邻”这一顺序语义,能挡掉特征串散落两处的误报。相对定位的三个要点:距离从上一匹配结束起算;每个修饰符只作用于其后紧跟的 content;调试时先收紧区间确认命中,再逐步放宽。
核心概念
pcre:用正则表达 content 表达不了的字符规律
类比:content 是找一个固定短语,pcre 是填一张带空格的模板。固定短语表达不了“三个数字后跟一个字母”这种规律,模板可以。pcre 即 Perl Compatible Regular Expressions(Perl 兼容正则表达式),用正则描述字符规律:字符类、量词、锚定、分组与捕获都能表达。它写在双引号内,以 / 开始、以 / 结束,中间是正则,末尾可带修饰符。捕获分组会把匹配到的一段内容记下来,供规则内进一步使用。
pcre 能覆盖 content 的很多场景,但正则引擎需要逐字节回溯,运算代价远高于 content 的快速匹配。因此 pcre 的定位是精化:先用 content 锁定候选流量,再用 pcre 收紧判断,而不是让 pcre 单独扛起整条规则。Suricata 文档在 Payload Keywords 一节为 pcre 单列了修饰符说明(8.9.21.2 节),需要时以官方文档为准。
推荐写法:content 负责预过滤,pcre 负责精化。下面这条规则先要求出现 cmd=,再用 pcre 判断其后是否是 /bin/sh 一类的 Shell 路径:
alert tcp any any -> any any (msg:"cmd followed by shell path"; content:"cmd="; pcre:"/cmd=\/bin\/(ba|z)?sh/"; sid:200002; rev:1;)正则里 \/ 是转义后的分隔符,(ba|z)? 是可选分组,sh 限定结尾。pcre 字符串用双引号包住,正则内部的 / 写成 /。没有 content 开路的纯 pcre 规则,缺少可预过滤的固定内容,每个包都要跑正则,吞吐会明显下降。先用 content 把候选集缩小,pcre 只对候选做最终判断。
pcre 是重量级运算,逐字节回溯会拖慢吞吐。Suricata 文档把快速筛选类关键字归为 Prefiltering Keywords:content 等固定内容能参与预过滤,纯 pcre 规则无法受益。三个习惯:能写 content 就先写 content;pcre 只做边界与字符规律的最终判断;每条规则里尽量让 pcre 前面有 content 开路。写完后用测试模式与离线回放验证,不要直接在产线流量上调试。
动手验证:distance/within 与 pcre
- 1
把下面两条规则写入你的本地规则文件(路径以 suricata.yaml 的 rule-files 配置为准,例如 /etc/suricata/rules/local.rules):
shellalert tcp any any -> any any (msg:"position test uid=0 root"; content:"uid=0"; content:"root"; distance:0; within:10; sid:200101; rev:1;) alert tcp any any -> any any (msg:"pcre test shell path"; content:"cmd="; pcre:"/cmd=\/bin\/(ba|z)?sh/"; sid:200102; rev:1;) - 2
运行测试模式校验语法。-T 的用法上一课已练习,这里加上 -S 指定你的规则文件:
shellsudo suricata -T -c /etc/suricata/suricata.yaml -S /etc/suricata/rules/local.rules - 3
准备包含目标特征的 PCAP。论坛与实战教程都建议先在 Wireshark 里检查流量、确认攻击串与间距再写规则;使用你自己授权的抓包文件,其中应出现
uid=0(root)或cmd=/bin/sh之类的字符串。 - 4
离线回放并确认日志输出。论坛提示过:忘记在 suricata.yaml 中开启 fast.log 就看不到告警。确认 fast 输出已启用后回放:
shellsudo suricata -r attack.pcap -S /etc/suricata/rules/local.rules -l /tmp/suricata-test - 5
检查告警:
shellgrep -E "20010[12]" /tmp/suricata-test/fast.log
你应该看到
fast.log 中出现带 sid 200101 与 200102 的行,msg 与规则一致;对不含 uid=0(root) 或 cmd=/bin/sh 特征的流量,两条规则均不告警。eve.json 中也能看到对应的 alert 事件。
遇到问题:规则通过 -T 校验,回放 PCAP 后 fast.log 里没有你的 sid。
可能原因
- 样本流量中不存在目标字符串,或大小写不一致而规则未加 nocase。
- distance/within 区间过窄,真实样本中两段内容的间距超出上限。
- pcre 分隔符与括号未转义:正则以 / 为界,内部的 / 要写成 /,括号按正则规则转义。
- 规则文件未被 suricata.yaml 的 rule-files 引用,或 fast.log 未启用;报错时按日志提示逐一核对路径。
排查步骤
- 回到 Wireshark 复查样本,确认目标字符串与真实间距,再调整 distance/within 数值。
- 二分定位:先只保留 content 去掉 pcre,确认 content 能命中;再逐步加回 pcre,每次改动都用 -T 校验。
- 逐项核对 suricata.yaml:rule-files 是否包含你的文件、fast 日志是否开启、路径是否与报错日志一致。
- 确认回放命令的 -l 目录与 -S 文件路径正确,eve.json 可作第二验证来源。
自检:规则写作 content:"A"; content:"B"; distance:3; within:8;,B 的匹配起点必须落在 A 匹配结束位置之后的什么范围?
B 的起点必须落在 A 结束位置之后 3 到 8 字节之间:distance:3 是最小偏移(下限),within:8 是最大偏移(上限),不足 3 或超过 8 都不命中。距离从上一匹配的结束位置起算,与 offset/depth 相对缓冲区起点不同。pcre 用于表达 content 表达不了的字符规律,应配合 content 预过滤使用以控制性能。