1tsprune/PurpleProof

GitHub: 1tsprune/PurpleProof

通过标记攻击遥测数据和五级判定模型客观度量 Wazuh 规则集的 MITRE ATT&CK 真实检测覆盖率并自动生成修复建议。

Stars: 0 | Forks: 0

# PurpleProof **通过标记的攻击遥测数据衡量 Wazuh ATT&CK 覆盖率。能够捕获自身虚高的数据。** [![许可证](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ## 结论先行 针对 **Wazuh 4.14.7 原生规则集**运行(启用 8451 条规则)。共 12 张技术卡片;在排除低保真度及有缺陷的卡片后,主要测试池中有 10 张。 ``` headline (technique-specific detection): DETECTED 3 of 10 gaps: GENERIC 6 (a rule fired, but its MITRE tag / level does not match the technique — likely also fires on benign activity) PARTIAL 1 (decoder OK, no alerting rule at all) MISSED 0 (decoder never populated fields) UNSUPPORTED 0 (in-headline; card-defective / model-limits excluded) ``` **三个特定技术的检测** — T1059.001(编码的 PowerShell → 规则 92071 L12)、T1059.003(PS→cmd → 规则 92004 L4)、T1547.001(通过 reg.exe 进行 RunOnce → 规则 92302 L6)。 **工具通过发现自身错误而捕获的一个真实覆盖盲区** — **T1003.001(LSASS 内存访问)**:163 个真实的 LSASS 访问事件被清晰解码,触发了 **零** 条告警规则。该工具的早期版本(从未发布)曾将其报告为 `DETECTED`,因为它无限制地纳入了每一个 EID-10 事件,而其中一个恰好触发了一条不相关的 T1055 进程注入规则。通过添加信号谓词策略([ADR-001](docs/ADR-001-event-selection.md))并强制执行 MITRE 标签相关性,原本错误的 `DETECTED` 被纠正为 `PARTIAL` — 这才是真实状态 — 现在工具自身的建议生成器会提出一个 6 行的规则,可将判定结果转回 `DETECTED`(已通过重新运行整个循环进行验证)。完整故事请参阅 [`CHANGELOG.md`](CHANGELOG.md),逐项技术的差距分析请参阅 [`mapping/attack-coverage.md`](mapping/attack-coverage.md)。 ## 如果你是……请从这里开始 | 如果你是…… | 请从这里开始 | |---|---| | 想要了解你的规则集覆盖率数据的 **Wazuh 管理员** | [快速开始](#quick-start) | | 正在评估严密性的 **检测工程师** | [`docs/DESIGN.md`](docs/DESIGN.md) — 五种判定结果模型以及为何存在 `GENERIC` | | **紫队成员** | [`docs/PRD.md`](docs/PRD.md) — 范围、非目标以及刻意不做的事情 | | 正在评估这个想法本身 | [现有方案](#prior-art) — 现有什么以及这里到底有什么创新 | | **招聘人员 / 招聘经理** | 本 README + [`CHANGELOG.md`](CHANGELOG.md)。工程价值体现在判定模型、闭环验证器以及发现自身错误的严谨性上 — 而不在于语料库。 | | 想要贡献力量 | [`templates/technique-card.md`](templates/technique-card.md) | ## 数据流 ``` flowchart LR subgraph corpus["splunk/attack_data (Apache-2.0)"] raw["raw XmlWinEventLog
one Event per line"] end subgraph adapter["adapter (thin wrap)"] parse["parse for signal
selection (ADR-001)"] wrap["envelope wrap
{Event: raw_xml}"] end subgraph manager["Wazuh manager"] socket["queue socket
MQ char 'f'"] decoder["built-in decoder
windows_eventchannel"] rules["rule engine
(stock ruleset)"] archives["archives.json
(logall_json=yes)"] end subgraph backend["backend (classify + aggregate)"] tail["tail archives.json
marker + offset correlation"] classify["classify per event
5 outcomes"] agg["aggregate to Verdict
per technique"] end subgraph out["outputs"] cov["coverage.json"] nav["navigator-layer.json"] junit["junit.xml"] sugg["suggestions/ (v0.3)"] end raw --> parse --> wrap --> socket --> decoder --> rules --> archives --> tail --> classify --> agg agg --> cov & nav & junit agg -.->|"purpleproof suggest"| sugg ``` 适配器解析每个原始事件以进行卡片信号选择,但**原封不动地提交原始 XML 字符串** — 内置解码器会自行提取字段。该决定源于 [SPIKE-01](docs/SPIKE-01.md),其证明了 `wazuh-logtest` 根本无法解码 Windows EventChannel:基于 `wazuh-logtest` 的判定会静默坍缩为单一的 syslog 全局匹配规则。实时的队列 socket 路径是唯一能触及真实解码器的机制。 ## 部署拓扑 ``` flowchart TB subgraph operator["Operator host"] cli["purpleproof CLI"] cache["~/.cache/purpleproof/
corpus cache (LFS-fetched)"] end subgraph lab["Wazuh manager
(WSL apt / Docker / bare Linux)"] conf["/var/ossec/etc/ossec.conf
logall_json=yes"] analysisd["wazuh-analysisd
(built-in decoders + rule engine)"] queue["/var/ossec/queue/
sockets/queue
SOCK_DGRAM"] archives["/var/ossec/logs/
archives/archives.json"] etcrules["/var/ossec/etc/rules/
(only touched by --validate)"] end subgraph upstream["github.com/splunk/attack_data"] pin["commit 67fe973a
(pinned)"] lfs["Git LFS
media endpoint"] end pin -->|"blobless sparse
checkout"| cache lfs -->|"content download
sha256-verified"| cache cli -->|"read"| cache cli -->|"read (health check)"| conf cli -->|"sendto (envelopes)"| queue queue --> analysisd conf --> analysisd analysisd -->|"logall_json=yes"| archives cli -->|"tail from offset"| archives cli -.->|"install + restart
only with --validate"| etcrules etcrules --> analysisd ``` 在提交任何事件之前,会运行四个预检防护措施 — socket 可达、`logall_json=yes`、`archives.json` 可写,以及一个合成金丝雀事件能作为 `windows_eventchannel` 解码记录完成往返。如果任何防护失败,运行将被拒绝启动,并针对每个修复提供明确的提示。有关每个防护在管理器确实发生故障时正确触发的实时证据,请参阅 [`docs/PHASE-A-VERIFICATION.md`](docs/PHASE-A-VERIFICATION.md)。 ## 五种判定结果模型 每个信号事件都遵循此决策流程: ``` flowchart TD E["signal event
(selected per ADR-001)
submitted to manager"] E --> D{"decoder = windows_eventchannel
AND win.system.eventID populated?"} D -- "no" --> M["MISSED
decoder / config gap"] D -- "yes" --> R{"alerting rule fired
(level >= 1)?"} R -- "no" --> P["PARTIAL
rule gap — write one"] R -- "yes" --> LM{"level >= card.min_level
AND rule.mitre ∩ card.mitre_ids ≠ ∅?"} LM -- "no" --> G["GENERIC
wrong rule caught it —
likely also fires on benign"] LM -- "yes" --> DET["DETECTED
technique-specific rule fired"] style DET fill:#2c7a2c,color:#fff style G fill:#c78a00,color:#fff style P fill:#e0a020,color:#000 style M fill:#c2352c,color:#fff ``` `UNSUPPORTED` 是在重放**之前**根据技术卡片分配的 — 要么重放模型无法执行该技术(基于关联、时间窗口、频率),要么卡片的信号谓词在获取的数据集中匹配到零个事件(`card_defective: true`)。它**绝不是从输出中推导出来的**,也**绝不计入 MISSED**。将“我们无法测试这个”与“你的规则没有捕获到这个”混为一谈,会使得覆盖率差距朝着美化工具的方向膨胀 — 参见 [`docs/DESIGN.md`](docs/DESIGN.md#3-the-verdict-model-five-outcomes) §3。 `GENERIC` 是防止数据膨胀的安全阀 — 它负责捕获那些“好吧,严格来说某条规则确实触发了”的情况。早期版本的工具没有这种结果,并在相同的规则集下报告了 10/10 DETECTED。当前版本报告为 3/10。这两个数字都来自相同的管理器和相同的语料库;唯一的区别在于分类器的严谨度。 ## 快速开始 ``` pip install purpleproof # 指向你现有的 Wazuh manager,或者在 # lab/README.md 中设置 throwaway 实验环境(WSL apt install 是主要验证过的路径;Docker # Compose 仅供参考,尚未进行端到端验证)。 purpleproof fetch # ~5 min first time (LFS) purpleproof -v run --out ./report # runs all 12 cards # 单一技术 purpleproof -v run --technique T1059.001 --out ./report ``` ### 规则建议 — 将判定转化为修复 对于每个 `PARTIAL` 和 `GENERIC` 判定,`purpleproof suggest` 会根据卡片的信号谓词生成一个候选的 Sigma 规则 + Wazuh XML 规则。每个生成的文件都带有明确的头部声明,标明其状态为 **UNTESTED**(未经测试)、**UNREVIEWED**(未经审查)、**OVERFIT**(对单一语料库样本过拟合),并且在投入生产前需要进行调优。 ``` purpleproof suggest --coverage ./report/coverage.json --out ./suggestions # 添加 --validate 以关闭闭环: purpleproof suggest --coverage ./report/coverage.json --out ./suggestions --validate ``` 使用 `--validate` 时,每条建议都会被安装到测试管理器中,管理器会重启,技术会重新运行,并且工具会确认判定结果翻转为 `DETECTED`。移除规则的**阴性对照重跑**证明了这种翻转是由规则引起的,而不是由测试基础设施状态引起的。不能实现翻转的建议是生成器中的错误,而不是可以交付的修复。 对于 `MISSED` 判定,工具会输出一份**配置诊断 markdown 文档**而不是规则 — 如果解码器从未填充相关字段,任何规则都无济于事。 当前内置卡片的翻转率:**7 / 7 (100%)** — 完整证据请见 [`docs/PHASE-E-VERIFICATION.md`](docs/PHASE-E-VERIFICATION.md),包括闭环在达到 100% 之前捕获到的两个生成器错误。 ## 原文输出(当前版本针对 Wazuh 4.14.7 原生规则集) ``` PurpleProof — wazuh 4.14.7 T1003.001 windows_eventchannel decoded 163/163 event(s) but no... PARTIAL T1021.002 A process was created. GENERIC rule 67027 level 3 (!) also fires on benign activity T1053.005 Suspicious Windows cmd shell execution GENERIC rule 92032 level 3 (!) also fires on benign activity T1059.001 A powershell process created by WMI executed a base64... DETECTED rule 92071 level 12 T1059.003 Powershell process spawned Windows command shell instance DETECTED rule 92004 level 4 T1070.001 Suspicious Windows cmd shell execution GENERIC rule 92032 level 3 (!) also fires on benign activity T1082 Suspicious Windows cmd shell execution GENERIC rule 92032 level 3 (!) also fires on benign activity T1087 card defective: signal predicate matched 0 events... UNSUPPORTED (!) low-fidelity . card defective (0 signal events) T1218.011 Suspicious Windows cmd shell execution GENERIC rule 92032 level 3 (!) also fires on benign activity T1489 no XmlWinEventLog dataset at pinned commit UNSUPPORTED (!) low-fidelity T1490 Suspicious Windows cmd shell execution GENERIC rule 92032 level 3 (!) also fires on benign activity T1547.001 Registry entry to be executed on next logon DETECTED rule 92302 level 6 headline (technique-specific detection): DETECTED 3 of 10 gaps: GENERIC 6 PARTIAL 1 MISSED 0 UNSUPPORTED 0 excluded from headline: 2 low-fidelity, 1 card-defective ruleset=33c96eef54b8d8ed corpus=67fe973a954c generated=2026-07-31T09:17:31Z ``` 完整的逐项技术差距分析(包括 T1003.001 “在 v0.1.0 下为 DETECTED,而在适当的信号谓词下现在为 PARTIAL”的故事)请见 [`mapping/attack-coverage.md`](mapping/attack-coverage.md)。 ## 现有方案 此概念已在 Splunk 上得到验证。但在 Wazuh 上尚不存在。 | 项目 | 功能 | 为什么还需要 PurpleProof | |---|---|---| | [`splunk/attack_data`](https://github.com/splunk/attack_data) | 精心筛选并标记的攻击数据集 (Apache-2.0) | **我们会使用它。** 但不兼容 Wazuh 格式。 | | [`splunk/contentctl`](https://github.com/splunk/contentctl) | 针对 Splunk 的检测即代码构建/测试/打包 | 仅限 Splunk,绑定于 Splunk 的内容格式 | | [Splunk Attack Range](https://github.com/splunk/attack_range) | 将攻击模拟到 Splunk 的仪表化实验室 | 用于生成数据;且假定使用 Splunk | | [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team) | 技术执行 | 仅负责执行;不负责验证检测 | | [DeTT&CT](https://github.com/rabobank-cdc/DeTTECT) | 基于手动 YAML 的覆盖度评分 | 仅对你*声称*拥有的内容进行评分,未经实际测量 | | [VECTR](https://github.com/SecurityRiskAdvisors/VECTR) | 紫队演练跟踪 | 手动填充 | | [pySigma Wazuh backend](https://github.com/SigmaHQ/pySigma) / SigWaz | Sigma → Wazuh 转换 | 仅负责转换;从不验证输出是否会触发 | **具体差距在于:** 对于 Wazuh,没有任何工具能够回答*“这条规则是否针对此遥测数据触发了 — 如果没有,是因为解码器、规则本身,还是用错了规则?”* ## 局限性 坦白地说,因为一个夸大自身覆盖率的覆盖工具比没有工具更糟糕。 - **仅限无状态检测。** 频率、关联和时间窗口规则(`if_matched_sid`、聚合)无法通过重放事件序列来验证。将被报告为 `UNSUPPORTED`,绝不报告为 `MISSED`。 - **适配器保真度就是上限。** 上游数据集是为 Splunk 摄取而捕获的。将它们转换为 Wazuh agent 会发送的格式对于某些 sourcetype 来说是有损的。相关说明记录在 [`docs/DESIGN.md`](docs/DESIGN.md#4-adapter-fidelity) 中。 - **你的配置决定你的结果。** `MISSED` 可能意味着你的 Sysmon 配置过滤掉了该事件,而不是你的规则有问题。在恐慌之前请先阅读技术卡片。 - **覆盖率 ≠ 安全性。** 这衡量的是一条规则是否触发。它不能说明告警疲劳、分诊质量或是否有人查看了告警。吸收 `GENERIC` 判定的通用规则的基线噪音是 v0.4 版本的待测项。 - **语料库较小。** ATT&CK Enterprise 中的 200 多项技术里仅包含 12 项(11 项支持 + 1 项 UNSUPPORTED)。广度是路线图中的一项,而不是一项声明。 - **`wazuh-logtest` 无法解码 Windows EventChannel。** [`docs/SPIKE-01.md`](docs/SPIKE-01.md) 经验证地证明了这一点;因此后端转而注入到 analysisd 队列 socket 并读取 `archives.json`。管理器必须在 `yes` 配置下运行 — 预检程序会强制要求这一点。 - **当前卡片重度依赖 Sysmon。** Windows 安全通道的纯文本 `WinEventLog` 尚未适配(目前仅适配 `XmlWinEventLog`)。卡片 `T1021.002` 使用安全通道的 XML 变体来演示此路径。 - **建议的规则仅为候选,绝不可直接上线。** 工具在 `suggestions/` 下输出的每个文件都带有头部声明,标明其未经测试 / 未经审查 / 过拟合 / 会产生误报 / 必须进行调优。闭环验证器仅证明该规则能翻转工具自身的判定 — 并不证明该规则部署上线是安全的。 - **规则父级映射与特定 Wazuh 版本相关。** `_pick_wazuh_parent` 是针对原生 4.14.7 规则集捕获的。上游的任何微小的重新编号都可能导致建议翻转率下降。闭环验证器会将其捕获为可衡量的下降 — 但在规则加载时检查已知父级是否存在的金丝雀检查将是 v0.3.x 的后续工作。 ## 安全与法律 - 本仓库**不包含恶意软件、漏洞利用程序和任何攻击性工具**。其包含的代码仅用于读取日志文件,将其提交给由操作者控制的 Wazuh 管理器,并读取管理器自身的日志输出。 - 语料库数据获取自上游 Apache-2.0 数据集,**不得**在此重新分发,除了在 `tests/adapters/golden/` 中提交的少量(KB 级)事件片段,作为用于字节级比对的适配器测试固定数据外。归属说明请参见 [`NOTICE`](NOTICE)。 - 本仓库或其获取的任何数据集中均不包含任何雇主、客户或第三方数据。 - 工具写入 `suggestions/` 的建议规则绝不会自动应用于操作者未通过 `--validate` 明确指定的任何管理器。即使使用 `--validate`,写入操作也仅限于 `/var/ossec/etc/rules/`,并伴随着无条件的清理和重启操作。 ## 项目结构 ``` purpleproof/ ├── README.md # this file ├── CHANGELOG.md # v0.1.0 → v0.2.0 correction story + post-v0.2.0 work ├── NOTICE # Apache-2.0 attribution for splunk/attack_data ├── LICENSE # MIT ├── pyproject.toml # PyYAML is the only runtime dep; everything else stdlib ├── docs/ │ ├── PRD.md # problem, users, scope, non-goals │ ├── DESIGN.md # architecture, five-outcome model, adapter fidelity │ ├── ADR-001-event-selection.md # signal predicates, not batch sweeps │ ├── SPIKE-01.md # why wazuh-logtest doesn't work; the live-injection alternative │ ├── PHASE-A-VERIFICATION.md # live evidence the preflight guards flip correctly │ ├── PHASE-C-WSL-VERIFICATION.md # --wsl cross-boundary end-to-end │ ├── PHASE-E-VERIFICATION.md # closed-loop suggestion flip rate + generator bugs it caught │ ├── RELEASE-v0.2.0.md # release checklist (git init, tag, gh release, distribution) │ ├── ROADMAP-NEXT.md # v0.3 shipped; v0.4 benign baseline; v0.5 Sentinel │ └── PLAN.md # historical week-by-week plan (see banner) ├── templates/ │ └── technique-card.md # the repeatable unit of content ├── purpleproof/ # the package │ ├── adapters/ # thin XmlWinEventLog wrap + field-parse for selection │ ├── backends/ # WazuhBackend (queue socket + archives.json + 4 guards) │ ├── corpus/ # fetch (LFS-aware), technique cards + signal predicate DSL │ ├── suggest/ # v0.3 rule generator + closed-loop validator │ ├── report/ # coverage.json, navigator, junit, stdout │ ├── verdict.py # Outcome enum + Verdict dataclass │ └── cli.py # fetch / run / suggest ├── lab/ │ ├── README.md # WSL apt (verified) + Docker Compose (reference-only) │ └── wazuh/ │ ├── compose.yml # reference-only, never end-to-end verified │ └── spike_convert.py # historical throwaway from SPIKE-01 ├── mapping/ │ └── attack-coverage.md # measured coverage + per-technique gap analysis ├── report/ # last run's artifacts (checked in for the release evidence) │ ├── coverage.json │ ├── navigator-layer.json │ └── junit.xml ├── suggestions/ # last suggest --validate run's output + index.json ├── tests/ # 93 tests, pytest └── .github/workflows/ci.yml # unit tests on push/PR; live run on main ``` ## 路线图 - [x] **v0.1.0** — 内部版本;从未发布。报告了 10/10 DETECTED。该数据是错误的 ([`CHANGELOG.md`](CHANGELOG.md#010--unreleased-superseded-by-020))。 - [x] **v0.2.0** — 信号谓词事件选择 ([ADR-001](docs/ADR-001-event-selection.md))、五种判定结果模型、强制执行 `min_level` 和 MITRE 相关性、四个预检健康防护、Windows 安全通道适配器、验证了 `--wsl` 跨边界路径、GitHub Action。 - [x] **v0.3** *(代码已发布,等待最终版本)* — 针对 `PARTIAL`、`GENERIC`、`MISSED` 的规则建议(候选 Sigma + Wazuh XML)。闭环验证达到 7/7 的翻转率。 - [ ] **v0.4** — 良性基线:衡量吸收 `GENERIC` 判定的通用规则在日常管理活动中触发的频率。从而得出可操作的覆盖率,而不是粗略的总覆盖率。 - [ ] **v0.5** —个后端(Sentinel/KQL)。不会在 v0.4 之前进行。 **明确且永久超出范围的事项:** Web UI、数据库、调度程序、实时 SIEM 轮询、基于 agent 的执行、综合评分、发布生产环境检测规则。请参见 [`docs/ROADMAP-NEXT.md`](docs/ROADMAP-NEXT.md#parked-indefinitely)。 ## 作者 **Eky Januarta** — 安全工程:VAPT、SOC 监控、检测工程。 ## 许可证 MIT — 详见 [`LICENSE`](LICENSE)。语料库数据保留其上游的 Apache-2.0 许可证;请参见 [`NOTICE`](NOTICE)。
标签:ATT&CK覆盖率评估, OpenCanary, Wazuh, 安全运营, 扫描框架, 紫队, 规则验证, 请求拦截, 逆向工具