红队问「能不能打进去」,蓝队问「有没有看见」,紫队问的是两者对不对得上: 我打出去的这一下,是不是恰好那一条规则该响;打完之后日志里到底有没有它。 这一页把这件事做成一张可执行的设计稿 —— 三条攻击、每条一个明确的预期 SID、一套 grep 验证方法, 再把还没验的部分写成「未验证」,而不是写成「命中 0」。
| 规则在哪 | detections/suricata/xk-lab.rules(7 条生效 + 1 条抑制型默认注释,SID 1000101–1000108)、
detections/wazuh/xk-lab_rules.xml(3 条,100400/100401/100402)。
两份都只做了语法验证:一次性 --rm 容器里 suricata -T 与
wazuh-analysisd -t,都是 exit 0,生产影响为零。 |
|---|---|
| 基线分母 | srv2 /opt/suricata/logs/eve.json,2026-10-11 09:23 ~ 2026-10-18 12:00 UTC,
共 1,471,376 行:alert 293,482 / flow 612,516 / http 283,796 / ssh 142,926 / dns 132,086 / tls 6,801。阈值全部是从这份基线里统计出来的窗口分布,不是拍脑袋定的;改阈值前先重跑计数。 |
| 什么算「验过」 | 一次自测只算成立,必须同时满足三件事:①规则文件真的在生产规则目录里且被加载;
②发起攻击后 grep 到对应 signature_id / rule.id;
③负样本对照不报(只打单端口的源不该触发「多端口」规则)。
缺第①条,grep 返回 0 说明的是没加载,不是没命中 —— 这两种结论完全相反。 |
| 本篇不引什么 | docs/Amadeus-项目总文档-20261017.md 的 §19 记的是 2026-10-18 的站点改动
(首屏聚合五块 / 发布链假失败 / 手机顶栏),里面没有任何 SID 与检测口径,
所以本页的数字一律不引它,只引 detections/README.md 与它引用的 eve.json 计数。 |
一句话口径:紫队自测的产物不是「拦住了多少」,而是 「哪条规则该响 / 实际响没响 / 没响是因为规则没加载还是真的没匹配上」这三件事分开记录。
靶场的公网入口已整体撤下,SSH 面是蜜罐 :2222,所以要先 SSH 隧道进去或用内网机器发起。
这三条选的是「行为维度」而不是「载荷维度」—— 因为真实攻击里对方最爱伪装的东西(banner、UA)最不可靠。
| # | 攻击 | 发起命令(示例) | 预期命中 | 为什么是它 |
|---|---|---|---|---|
| 1 | SSH 爆破 | for i in $(seq 1 10); do ssh -p 2222 [email protected] true; sleep 25; done | 1000103 | 判据是会话速率(同源 300s 内 ≥10 次 SYN 到 :2222),匀速十次/五分钟就够;不是失败次数 |
| 2 | 端口扫描 | nmap -sS -Pn -n -p 21,22,23,2222,3306 10.0.0.10 | 1000104 1000107 | 判据是端口要不同(60s 内 ≥3 个诱捕端口);打 DB 口再叠一条外部源直连规则 |
| 3 | Web 探测 | for u in /.env /.git/config /config.json; do curl -s -o /dev/null "http://10.0.0.10:8081$u"; done再换 UA: curl -A 'zgrab/0.x' … | 1000105 1000106 | 路径一条就够(意图明确),UA 那条只打标不封禁(UA 可伪造) |
负样本必须一起跑:只打单端口、打几千次也不该触发 1000104 / 100401。
真实对照就在基线里 —— 179.63.84.194 的 2,752 次全落在 1433 一个口上,规则设计上它不该被「多端口」规则报出来。
只测「该响的响了」是自欺,还要测「不该响的没响」。
| 规则 | 什么条件下该响 | 基线里的依据(真实计数) | 已知误报 |
|---|---|---|---|
| 1000101 | SSH 客户端 banner 以 Go 开头 | ssh 事件 142,926 中 software_version:"Go" 75,390 条(52.7%) | 自研 Go 巡检脚本 / CI 健康检查 |
| 1000102 | banner 以 libssh/libssh2 开头 | libssh_0.9.3 38,955 条;Cowrie 侧 SSH-2.0-libssh2_1.11.1 3,275 次 | 备份/发布脚本、授权扫描器 |
| 1000103 | 同源 300s 内 ≥10 次 SYN 到 :2222 | 1,448 个 (源,300s) 窗口:max 29 / p99 27 / mean 8.18;≥20 只有 42 个(2.9%) | 内部自查脚本($EXTERNAL_NET 已排除)、蜜罐重启补偿连接 |
| 1000104 | 同源 60s 内 ≥3 个诱捕端口(21/22/23/1433/2222/3306/6379/9080/27017) | 53 个 (源,分钟) 窗口、275 个外部源;47.96.106.187 一分钟 9 个端口 | 背景测绘(Censys/Shadowserver 类),建议打标不封禁 |
| 1000105 | HTTP GET .env 家族 / .git/config / .git/HEAD / web.config / config.json | .env* 262 次;/.git/config 13;/config.json 7;/web.config 6;/phpmyadmin/scripts/setup.php 9 | CI 拉 .env 做存在性校验;客户授权渗透 |
| 1000106 | UA 命中 Go Web Pentest Tool / l9explore / zgrab / CensysInspect / zmap / Infrawatch / Nmap NSE / AttackWatch-PoC-Scanner | 155 / 96 / 72 / 26 / 6 / 23 / 16 / 10 次 | 资产测绘、第三方合规探针;只打标,不作封禁依据 |
| 1000107 | 外部源 SYN 直连 1433/3306/6379/9200/27017 | 1433 告警 1,628 条 / 136 源;3306 756 / 230 源;6379 129 源;27017 67 源 | 云厂商端口探活(量极小,白名单排除) |
| 1000108 | 抑制型:$HOME_NET → 169.254.0.4:80 established 走 pass(默认注释、opt-in) | 293,482 条 alert 里 278,405 条(94.9%)是这一条出站 POST /ca_report.cgi | 写错方向会把真实 C2 一起静音,故默认不启用 |
| 100400 | 同源 300s 内 ≥20 次 cowrie.session.connect | 2026-10-17 单日 14,147 会话;窗口 max 29 / p99 27;≥20 仅 42 窗(2.9%),top 195.178.110.218(29)、45.148.10.161(28) | 内部自查脚本;10.0.0.10 需白名单 |
| 100401 | 同源 60s 内触及 ≥3 个不同蜜罐端口 | flow 侧 53 个 (源,分钟) 窗口、275 个外部源;对照组 179.63.84.194 2,752 次全单端口 1433(本条不该误报它) | 背景测绘;docker 网关 172.19.0.1 |
| 100402 | 蜜罐 MSSQL 收到凭据(credentials 键存在,已进认证阶段) | credentials 事件 2,753 条(mssqld 2,750 / mysqld 3),其中 2,750 条来自 179.63.84.194 | 蜜罐连通性自测(带假凭据),需排除 |
为什么 1000103 是 10/300s 而不是 20/300s:20 抓的是脉冲型源(29 次/5min 那种), 10 抓的是「稳、慢、持久」的源。真实源里 ≥20 的窗口只占 2.9%,用 20 会把大部分慢速爆破漏掉 —— 这不是阈值写错,是精度与覆盖率的分层取舍,自测时要按你想验的那一层选阈值。
第一步永远是确认规则被加载(否则后面的 0 没有意义):
# 语法验证(一次性容器,等价且不碰生产;README §3 记的就是这条) docker run --rm --network none --pull=never -v /tmp/xkrules:/rules:ro \ --entrypoint suricata jasonish/suricata:latest -T -S /rules/xk-lab.rules # 结果:Suricata version 8.0.7 / Configuration provided was successfully loaded / exit 0 # 再看规则文件在不在生产目录(不在 ⇒ 后面 grep 出 0 是「没部署」) ls -l /opt/suricata/rules/ ; ls -l /var/ossec/etc/rules/
第二步才是对账(Suricata 看 signature_id,Wazuh 看 rule.id):
# Suricata:先数条数,再只看自己这台源,避免被别人的扫描混进来 grep -c '"signature_id":1000103' /opt/suricata/logs/eve.json grep '"signature_id":1000103' /opt/suricata/logs/eve.json | grep '"src_ip":"10.0.0.20"' grep '"signature_id":1000104' /opt/suricata/logs/eve.json | tail -n 5 # 1000103 的输入证据在 flow 面:会话速率 grep '"dest_port":2222' /opt/suricata/logs/eve.json | grep '"event_type":"flow"' | tail -n 50 # Wazuh:alerts.json 一行一个 JSON grep '"id":"100400"' /var/ossec/logs/alerts/alerts.json | tail -n 3 grep -c '"id":"100401"' /var/ossec/logs/alerts/alerts.json
判读规则:grep -c 非 0 且 src_ip 是自己 → 规则真的响了;
为 0 但规则已加载 → 规则没匹配上(要去看阈值和方向);
为 0 且规则没加载 → 未验证,不是「没命中」。
| 规则 | 部署状态 | 发起过的攻击 | grep 结果 | 结论 |
|---|---|---|---|---|
| 1000101 / 1000102 | 未部署 | 未发起 | — | 未验证(不能写成「没命中」) |
| 1000103 | 未部署 | 未发起 | — | 未验证 |
| 1000104 / 1000107 | 未部署 | 未发起 | — | 未验证 |
| 1000105 / 1000106 | 未部署 | 未发起 | — | 未验证 |
| 1000108 | 默认注释、未启用 | 不需要发起(抑制型) | — | 未验证(opt-in) |
| 100400 / 100401 / 100402 | 未部署 | 未发起 | — | 未验证 |
所以答案是:看不见。 不是平台没看见我,是还没把眼睛装上去 ——
两份规则文件仍在 detections/ 旁路目录,没进 /var/ossec/etc/rules/ 与
/opt/suricata/rules/,也没 reload 过 suricata / wazuh-analysisd。
这一栏留白不是偷懒,是此刻唯一诚实的写法:没有非 0 的 grep,就没有「命中」这个词。
填这一栏的顺序:①部署两份文件 → ②suricata -T / wazuh-analysisd -t 通过后 reload →
③按上面三条打 → ④grep 记数(含负样本对照)→ ⑤只把有 grep 支撑的行改成「命中」或「没命中」。
| 失败次数型规则会空转 | 已部署的 100110(120s 内 5 次登录失败)在 2026-10-17 全天 Cowrie 只有
19 条 login.failed 对 14,061 条 login.success,
alerts.json 里 "id":"100110" 计数 0。
机器人拿泄露口令表,一枪就中 —— 新规则才改成按会话速率判(100400)。
自测时只发错误口令,测的其实是这条空转规则。 |
|---|---|
| 94.9% 的告警是自家的 | 293,482 条 alert 里 278,405 条是 10.0.0.10 → 169.254.0.4:80 POST /ca_report.cgi。
自测前先想清楚:你 grep 出来的「命中」可能全是云主机心跳;
1000108 就是为消掉它写的,但它是 opt-in,写错方向会把真实 C2 一起静音。 |
| Wazuh XML 的三种写法 | ① JSON 解码事件不能用 <same_source_ip/>(内部 srcip 为空,规则永不触发)
→ 用 <same_field>src_ip</same_field>;
② <if_matched_group> 不写尾随逗号、<group> 要写;
③ 同一 rule 里 <decoded_as> 与 <regex> 不能共存
(Syntax error on tag 'regex' 会让 analysisd 拒绝启动)。 |
公开:规则 id、判据(窗口 / 阈值 / 端口组)、每条规则背后的真实计数与窗口分布、
发起命令的形状、验证用的 grep、以及「未部署 ⇒ 未验证」这个结论本身。
不公开:生产环境的完整规则文件内容、真实服务器地址之外的内部拓扑、蜜罐真实口令与凭据、
以及任何还没部署就宣称「已生效」的说法 —— 最后这条不是安全问题,是诚信问题。
数字出处:detections/README.md(§0 侦察表、§1/§2 规则表、§3 语法验证、§4 边界)
与它引用的 /opt/suricata/logs/eve.json 统计。
规则本体在 detections/suricata/xk-lab.rules 与 detections/wazuh/xk-lab_rules.xml。
配套笔记见 《我打的攻击,我的平台能看见吗》。