aadarshkadam067/DetectionForge
GitHub: aadarshkadam067/DetectionForge
DetectionForge是一个检测即代码管道,通过测量精确度和召回率来自动化测试和转换Sigma检测规则。
Stars: 0 | Forks: 0
# DetectionForge
由 **Aadarsh Kadam** 创建 · [github.com/aadarshkadam067](https://github.com/aadarshkadam067)
[](rules/)
[](reports/results.json)
[](reports/layer.json)
[](reports/)
[](https://github.com/aadarshkadam067/detectionforge/actions/workflows/ci.yml)
## 这是什么
DetectionForge 是一个检测即代码流水线,它将 SIEM 规则视同软件:受版本控制,针对真实的 OTRF 攻击捕获数据进行自动测试,并自动转换为三种 SIEM 后端。每一条规则都附带基于**1,648 个事件语料库**(1,164 个进程创建事件 + 484 个注册表事件)的精确度和召回率测量,这些事件均来自 OTRF Security-Datasets ZIP 包——不含合成事件或手动构建的测试数据。该流水线包含**20 条规则,覆盖 8 种战术下的 21 种 ATT&CK 技术,平均精确度为 0.997**,并在 Splunk SPL、Elastic EQL 和 Microsoft Sentinel KQL 三种后端实现了 **60/60 的转换成功率**。(第 21 种技术是 T1027 混淆文件——T1059.001 的 PowerShell `-EncodedCommand` 规则同时合法覆盖了 T1059.001 和 T1027。)
每条规则的精确度数值被分类为**实际获取**(14 条规则——良性语料库包含同类型事件,且规则正确排除了它们)或**结构性缺失**(6 条规则——精确度 = 1.000 是因为语料库中没有规则可能匹配的事件;基于 T1218 LOLBIN 集群原理发布)。分类信息记录在 `dist/data/dashboard_meta.json` 中,按规则列出。另有三种技术被记录为第 5 阶段的延迟项,而非以结构保证的测量值发布(见下文*第 5 阶段待办事项*)。相同的标准处处适用:1.000 这个数字绝不会在没有来源证明的情况下显示。
## 仪表盘
该仪表盘将检测语料库、精确度测量值和记录的差距呈现为一个单一的静态网站。通过任何 HTTP 服务器提供 `dist/` 目录服务——无需构建步骤,无需后端,运行时读取四个 JSON 契约文件。

*概述——关键指标。实际获取与结构性缺失的划分以与平均精确度数值相同的显著度呈现;侧边栏中的诚实度指示器展示了 14/6 的划分、转换成功计数和误报数量。*

*规则——可排序、可过滤、可展开的 20 条规则总表。T1059.001 的 0.933 精确度和单一误报会内联高亮显示,而非四舍五入隐藏。可按分类或日志源过滤;展开任意行可查看 Sigma 源代码及三种转换后的后端查询。*

*覆盖范围——ATT&CK 战术列网格。包含 26 个直接技术单元格和 16 个父级汇总单元格(虚线)。每个非汇总单元格都带有其分类标签;T1059.001 和 T1027 因为底层规则存在误报而带有 FP 背景。*

*趋势——单次快照状态。`first_snapshot_date` 等于 `built_date`,因此每个图表将当前值显示为带单一标记的水平线。后续的 `forge build` 运行将追加数据点,迷你图会自动展宽。*

*差距——三种延迟的技术及其类别(类别 1 / 类别 3)、前置条件类型和 `dashboard_meta.json` 中的原因。结构性缺失清单在此视图下方继续展示。是命名列出的,而非隐藏。*
## 5 分钟验证
```
git clone https://github.com/aadarshkadam067/DetectionForge.git
cd DetectionForge
python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev,convert]'
python scripts/update_attack_data.py # generates forge/data/attack_techniques.json
forge run # lint → test → convert → score → build
```
或在 Docker 中运行完整流水线:
```
docker compose -f docker/docker-compose.yml run --rm forge
```
预期的 `forge test` 摘要:
```
20 rules — mean precision 0.997 mean recall 1.000 mean F1 0.998
```
预期的 `forge convert` 摘要:
```
60/60 conversions succeeded — success rate 100.0% (threshold 95%)
```
ATT&CK 覆盖层被写入 `reports/layer.json`,并可直接通过*打开现有层 → 从本地上传*加载到公开的 Navigator 中: 。
### 本地查看仪表盘
位于 `dist/` 的静态测量仪表盘使用 `dist/data/` 目录中的 JSON 契约文件。**必须先运行 `forge run`**——这些 JSON 文件未提交(它们是构建输出),因此新克隆的仓库在流水线填充 `dist/data/` 前只会显示一个空白仪表盘。然后:
```
forge run # populates dist/data/*.json
cd dist && python3 -m http.server 8000 # any static server works
# 打开 http://127.0.0.1:8000/
```
如果你在运行流水线前打开仪表盘,它会显示一个明确的错误卡片并指向此命令——这是设计如此,以便失败模式是清晰可读的,而不是一个静默的空白界面。
该仪表盘使用了浏览器内 Babel 转换,这会在页面加载时产生一个控制台警告。这是有意的——它保留了 [PDR §9](PDR.md)中记录的无构建步骤部署特性:一个可生产部署的静态资产,零工具链依赖。仪表盘以纯 HTML/CSS/JSX 形式直接从磁盘读取。
## 架构
```
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ rules/*.yml │ │ data/attack/ │ │ data/benign/... │
│ (Sigma rules) │ │ (TP fixtures) │ │ data/registry_baseline │
└────────┬─────────┘ └────────┬─────────┘ └────────────┬─────────────┘
│ │ │
└──────────┬───────────┴────────────┬─────────────┘
▼ ▼
┌───────────────┐ ┌───────────────┐
│ forge lint │ │ forge test │
│ (Stage 1) │ │ (Stage 2) │
└───────┬───────┘ └───────┬───────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ forge convert │ │ forge score │
│ (Stage 3) │ │ (Stage 4) │
└───────┬───────┘ └───────┬───────┘
│ │
▼ ▼
┌─────────────────────────┐ ┌────────────────────────┐
│ reports/converted// │ │ reports/results.json │
│ splunk.spl │ │ reports/layer.json │
│ elastic.eql │ │ reports/conv_matrix... │
│ sentinel.kql │ │ │
└────────────┬────────────┘ └────────────┬───────────┘
│ │
└─────────────┬──────────────┘
▼
┌────────────────┐
│ forge build │
│ (Stage 5) │
└────────┬───────┘
▼
┌────────────────────────────────┐
│ dist/data/ │
│ dashboard_meta.json │
│ results.json │
│ conversion_matrix.json │
│ layer.json │
└────────────────────────────────┘
```
| 阶段 | 命令 | 输入 | 输出 |
|---|---|---|---|
| 1. 检查 | `forge lint` | `rules/**/*.yml` | 通过/失败及错误信息 |
| 2. 测试 | `forge test` | 规则 + 测试数据 + 基线 | `reports/results.json` |
| 3. 转换 | `forge convert` | 规则 | `reports/converted//{splunk.spl, elastic.eql, sentinel.kql}` + `reports/conversion_matrix.json` |
| 4. 评分 | `forge score` | `reports/results.json` | `reports/layer.json` (ATT&CK Navigator v4.5) |
| 5. 构建 | `forge build` | 所有报告 + 规则 YAML 文件 + `_meta.yml` | `dist/data/{dashboard_meta, results, conversion_matrix, layer}.json` |
**关于阶段 5 的说明。** `forge build` 仅生成 JSON 数据构件——不生成 HTML。位于 `dist/` 的仪表盘是已提交的源代码(HTML/CSS/JSX,无构建步骤),在运行时读取这些 JSON 文件。有关设计决策和重构理由,请参阅 [PDR §9](PDR.md) 和旅程 [条目 4.9](docs/journey/PROJECT_JOURNEY.md)。
## 测量
### 精确度 / 召回率表(全部 20 条规则)
`Class` 列区分了**实际获取**的精确度(语料库包含同类型事件,且规则正确排除了它们——区分是真实的)和**结构性缺失**(语料库中没有规则可能匹配的事件——精确度 = 1.000 反映的是缺失,而非区分;基于 T1218 LOLBIN 集群原理发布)。有关每条规则的分类审计,请参阅旅程 [条目 4.7](docs/journey/PROJECT_JOURNEY.md)。
| 技术 | 战术 | 日志源 | P | R | F1 | TP | FP | 类别 |
|---|---|---|---|---|---|---|---|---|
| T1003.001 | 凭证访问 | process_creation | 1.000 | 1.000 | 1.000 | 2 | 0 | 实际获取 |
| T1003.002 | 凭证访问 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 结构性缺失 |
| T1021.006 | 横向移动 | process_creation | 1.000 | 1.000 | 1.000 | 3 | 0 | 实际获取 |
| T1033 | 发现 | process_creation | 1.000 | 1.000 | 1.000 | 2 | 0 | 实际获取 |
| T1053.005 | 持久化 | process_creation | 1.000 | 1.000 | 1.000 | 2 | 0 | 实际获取 |
| T1055.001 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 实际获取 |
| T1059.001 | 执行 | process_creation | 0.933 | 1.000 | 0.965 | 14 | 1 | 实际获取 |
| T1059.005 | 执行 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 结构性缺失 |
| T1087.001 | 发现 | process_creation | 1.000 | 1.000 | 1.000 | 8 | 0 | 实际获取 |
| T1105 | C2 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 实际获取 |
| T1136.001 | 持久化 | process_creation | 1.000 | 1.000 | 1.000 | 4 | 0 | 实际获取 |
| T1218.001 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 结构性缺失 |
| T1218.004 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 结构性缺失 |
| T1218.005 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 2 | 0 | 结构性缺失 |
| T1218.010 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 实际获取 |
| T1218.013 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 结构性缺失 |
| T1220 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 实际获取 |
| T1547.001 | 持久化 | registry_event | 1.000 | 1.000 | 1.000 | 3 | 0 | 实际获取 |
| T1548.002 | 权限提升 | process_creation | 1.000 | 1.000 | 1.000 | 1 | 0 | 实际获取 |
| T1562.004 | 防御规避 | process_creation | 1.000 | 1.000 | 1.000 | 4 | 0 | 实际获取 |
**汇总:** 20 条规则 · 平均精确度 0.997 · 平均召回率 1.000 · 平均 F1 0.998 · 14 条实际获取 / 6 条结构性缺失。
**语料库:** **跨两种日志源的 1,648 个事件** ——
- **1,164 个事件的进程创建基线**(Sysmon EID 1)来自 121 个 OTRF 原子及复合 Windows ZIP 包,基于 `(Image, CommandLine, ParentImage, Computer)` 去重,并通过结构性攻击事件排除过滤器([ADR-005](docs/adr/005-benign-pool-excludes-fixture-duplicates.md))进行了筛选。
- **484 个事件的注册表基线**(Sysmon EID 12/13)来自 4 个 OTRF 源 ZIP 包(`empire_wmi_local_event_subscriptions_elevated_user`、`empire_schtasks_creation_execution_elevated_user`、`covenant_dcom_iertutil_dll_hijack`、`empire_dcom_shellwindows_stager`),并预先排除了 `\Microsoft\Windows\CurrentVersion\Run\` 和 `\Microsoft\Windows\CurrentVersion\RunOnce\` 路径(标签化公理——精确排除规则所检测的目标)。在第 4 阶段第 3 天通过 ADR-006 的每日志源基线路由构建。
总共 48 个结构性过滤器签名(46 个 process_creation + 3 个 registry_event,已处理重叠)。所有 20 条规则均达到其 `expected.precision` 阈值;唯一的非 1.000 数值(T1059.001 的 0.933)是下文 *T1087.001 — 修复前后* 中记录的跨技术误报对称应用的结果。详细信息见旅程 [条目 4.3 / 4.6 / 4.7](docs/journey/PROJECT_JOURNEY.md)。
### 多 SIEM 转换矩阵
所有 20 条规则 × 3 种后端 = **60/60 次转换成功 (100%)**。
后端:pySigma-backend-splunk 2.1.0, pySigma-backend-elasticsearch 2.0.2, pySigma-backend-kusto 1.0.1。两种日志源类别均能干净转换:
| 日志源 | 规则数 | 转换成功 |
|---|---|---|
| `process_creation` | 19 | 57/57 |
| `registry_event` | 1 (T1547.001) | 3/3 |
| **总计** | **20** | **60/60** |
每条规则、每种后端的状态以及实际生成的查询字符串在 `reports/conversion_matrix.json` 和 `reports/converted//{splunk.spl, elastic.eql, sentinel.kql}` 中。
注册表日志源的尾部反斜杠语义在三种后端均已验证正确——在 `\Run\` 条件下,`\CurrentVersion\Run\` 不匹配 `\RunTime` 或 `\RunOnce`。验证详见 [`docs/measurements/phase4-day3-registry-harness.md`](docs/measurements/phase4-day3-registry-harness.md)。
### T1087.001 — 修复前后的缺陷(方法论证据)
测试工具在语料库扩展期间捕获了一个规则缺陷:T1087.001 的 `CommandLine|contains: user` 条件匹配了来自 T1136.001(创建账户)的 `net user /add` 命令,产生了两个跨技术误报。该缺陷在检测时(第 3 阶段第 2 天)被记录,在测量中被故意保留,并在后续提交中修复——提供了一个可复现的前后对比差异。
| 状态 | 精确度 | 召回率 | F1 | 误报数 | 缺陷 |
|---|---|---|---|---|---|
| 修复前 (第 2 天) | 0.800 | 1.000 | 0.889 | 2 | `user` 关键字匹配了 `net user /add` |
| 修复后 (第 4 天) | 1.000 | 1.000 | 1.000 | 0 | `filter_account_creation: CommandLine\|contains: ' /add '` |
该修复是一个 4 行的 YAML 变更;测试工具测量了改进效果。这正是本项目旨在演示的迭代改进循环。完整的证据链在 [`docs/measurements/phase4-day3-registry-harness.md`](docs/measurements/phase3-day4-t1087-fix.md)。
## 方法论
### 语料库设计
所有正例测试数据(TP 事件)均来自真实的 OTRF Security-Datasets 捕获,从不来自合成或手动编写的事件。数据集的选择记录了接受/拒绝决策及原因于 [`data/captures/_decisions.md`](data/captures/_decisions.md)——这是“你如何选择你的测试数据?”的证据记录。
**1,164 个事件的进程创建基线**由 121 个 OTRF 原子及复合 Windows ZIP 包(第 4 阶段第 1 天语料库构建)组装而成,使用 `scripts/rebuild_baseline.py` 基于 `(Image, CommandLine, ParentImage, Computer)` 去重。然后,结构性攻击事件过滤器([ADR-005](docs/adr/005-benign-pool-excludes-fixture-duplicates.md))移除了 `hash(Image, CommandLine, ParentImage)` 与任何正例测试数据匹配的事件。随着新的攻击测试数据加入,过滤器会重新运行;随着跨数据集攻击类型泄露被捕捉,基线会幂等地缩小。
**484 个事件的注册表基线**(Sysmon EID 12/13)在第 4 阶段第 3 天由 `scripts/extract_registry_baseline.py` 从四个 OTRF 源 ZIP 包中构建。提取过程预先排除了 `\Microsoft\Windows\CurrentVersion\Run\` 和 `\Microsoft\Windows\CurrentVersion\RunOnce\` 路径(标签化公理——规则的检测目标正是被排除的内容;不多不少)。然后,在测试数据生成后,广义的结构性过滤器使用 `(TargetObject, Details, Image)` 作为身份签名再次应用。详见 [ADR-006](docs/adr/006-per-logsource-baseline-routing.md) 了解每日志源的路由决策,以及 [`docs/measurements/phase4-day3-registry-harness.md`](docs/measurements/phase4-day3-registry-harness.md) 了解构建记录。
**精确度仅对那些基线包含与其选择的日志源相同类型事件的规则才有意义。** 测试工具通过 ADR-006 强制执行这一点:`forge/__init__.py` 定义了 `BASELINE_MAP`(一个 `类别 → 文件` 注册表)和 `REGISTERED_LOGSOURCE_CATEGORIES`(其键集)。在测试时,没有显式 `fixtures.negative` 的规则会根据其 `logsource.category` 自动路由。在检查时,一条没有负例测试数据且类别未注册的规则在被测量前就会被拒绝(见 `tests/test_lint.py` 中的三个守卫用例)。多日志源扩展现在是“添加一个基线文件 + 添加一个分派表条目”——无需其他更改。
**关于旧数据的说明。** 第 4 阶段之前的 README 报告了 1,481 个事件的基线。该数字是一个原始连接计数,包含了 419 个跨数据集重复事件;第 4 阶段之前语料库的正确唯一事件数大约为 1,051。第 4 阶段的重构修正了这一点并添加了注册表基线。有关详细说明,请参见项目旅程中的条目 4.3。
### 规则格式
规则使用 Sigma 格式编写,并带有 `forge:` 扩展块,用于指定测试数据路径、预期指标和多 SIEM 标志。所有规则在检查时都经过 Pydantic v2 验证。
```
# 示例 — rules/windows/credential_access/T1003_001_lsass_dump_comsvcs.yml
detection:
selection:
EventID: 1
Image|endswith: '\rundll32.exe'
CommandLine|contains|all:
- 'comsvcs'
- 'MiniDump'
condition: selection
forge:
fixtures:
positive: data/attack/T1003.001/events.json
negative: data/benign/workstation_baseline.json
expected:
precision: 0.95
recall: 1.00
multi_siem: true
```
测试工具 (`forge/test_harness.py`) 使用手工编写的字段匹配器评估规则与测试事件,该匹配器支持四种修饰符:`|endswith`、`|contains`、`|contains|all`、`|startswith`。pySigma 仅用于 `forge convert`(查询字符串生成),不用于评估——有关设计决策和原型验证结果,请参阅 [ADR-002](docs/adr/002-rule-evaluation-backend.md)。
### 多 SIEM 转换
`forge convert` 通过 pySigma 将每条 Sigma 规则编译为三种后端的查询字符串。当前所有规则使用的 Sigma 修饰符集能干净地映射到每种目标语言:
| Sigma 修饰符 | SPL | EQL | KQL |
|---|---|---|---|
| `\|endswith` | `field="*value"` | `field:"*value"` | `field endswith "value"` |
| `\|contains` | `field="*value*"` | `field:"*value*"` | `field contains "value"` |
| `\|contains\|all` | 重复的字段子句 (AND) | `(f:"*a*" and f:"*b*")` | `(f contains "a" and f contains "b")` |
| `\|startswith` | `field="value*"` | `field:"value*"` | `field startswith "value"` |
| `not filter` | `NOT (field IN (...))` | `not (field like~ (...))` | `not((field endswith "..."))` |
未检测到转换损失。全部 51 次转换成功。T1003.001 的转换后查询示例在 [`docs/examples/T1003_001_converted_queries.md`](docs/examples/T1003_001_converted_queries.md) 中。
## 已知局限性 & 第 5 阶段待办事项
### 记录的延迟项(三种技术,三种前置条件类型)
三种技术被记录为延迟项,而非以结构保证的精确度数值发布。每种都有一个明确的解锁前置条件。
| 技术 | 类别 | 前置条件类型 | 原因 |
|---|---|---|---|
| T1037.001 登录脚本 (UserInitMprLogonScript) | 类别 3 | **仅语料库** | 484 个事件的注册表基线包含 0 个涉及 `\Environment\` 路径的事件——精确度将是结构性保证的。解锁方式是扩展语料库,而非改进测试工具。 |
| T1546.003 WMI 事件订阅 | 类别 1 | 测试工具 + 语料库 | Empire 的 WMI 持久化通过 WMI 命名空间 API(Sysmon EID 19/20/21)进行,而非注册表写入。解锁需要一个新的 `wmi_event` 基线和分派表条目。 |
| T1110.003 密码喷洒 | 类别 1 | 测试工具 + 语料库 + 聚合 | 规则基于 EID 4625(登录失败)选择,当前测试工具中没有基线。解锁需要一个登录事件基线、分派表条目以及聚合逻辑来统计窗口期内每个源的失败次数。 |
**三种延迟类别**(类别 1 日志源不匹配,类别 3 语料库现实性)在旅程 [条目 4.5](docs/journey/PROJECT_JOURNEY.md) 中定义。
T1037.001 是最容易解锁的——测试工具已经支持 `registry_event`。第 5 阶段第 1 天的顺序:将 T1037.001 作为语料库扩展任务处理,然后处理另外两个的测试工具端工作。
**原则:** 只有当良性基线包含与规则选择的日志源相同类型、且数量足够大以合理产生误报的事件时,规则的精确度数值才有意义。发布一条其精确度将是结构性保证的规则,与本项目应用于所有其他规则的标准相矛盾。
### 单事件测试数据的注意事项
八条规则的正例测试数据仅有三个或更少的事件。召回率是针对这些事件的二进制测量——在每种情况下都是 1.000,但这并不意味着涵盖了所有现实世界的变体。有关 OTRF 源数据集和捕获的变体,请参阅 `data/attack//` 中每条规则的 `_meta.yml`。
### 仪表盘 / 表示层
`forge build`(阶段 5)将四个 JSON 契约文件写入 `dist/data/`。位于 `dist/` 的静态测量仪表盘(通过 CDN 的 React,无构建步骤)在运行时读取这些文件,并呈现五个视图:概述、规则、覆盖范围、趋势、差距。实际获取与结构性缺失的分类与每个精确度数值一同发布——这是项目的诚实契约,在代码中体现。服务顺序见上文 *本地查看仪表盘*。
ATT&CK 覆盖层也通过 `reports/layer.json` 发布,可直接加载到官方 MITRE Navigator 中:
(*打开现有层 → 从本地上传*),供任何偏好规范查看器而非项目仪表盘的用户使用。
最初的 UI 延迟和重构理由记录在 [PDR §9](PDR.md) 和旅程 [条目 4.9](docs/journey/PROJECT_JOURNEY.md) 中。
## 致谢
**OTRF Security-Datasets** —— 所有正例测试事件和良性基线语料库均来自 OTRF 的开源攻击捕获库。数据集的选择、事件计数和接受/拒绝决策记录在 [`data/captures/_decisions.md`](data/captures/_decisions.md) 中。
**MITRE ATT&CK** —— 本项目中的技术和战术映射遵循 MITRE ATT&CK 框架。
**SigmaHQ** —— 规则格式和 pySigma 转换后端。
## 许可证
MIT —— 详见 [LICENSE](LICENSE)。
版权所有 (c) 2026 Aadarsh Kadam
标签:AMSI绕过, ATT&CK框架, DNS 反向解析, Elastic EQL, OpenCanary, OTRF安全数据集, Sigma规则, Splunk查询语言, 召回率测量, 多SIEM集成, 多模态安全, 威胁情报, 威胁检测, 安全数据分析, 安全规则优化, 安全规则管理, 安全运营, 实时威胁检测, 开发者工具, 微软Sentinel KQL, 扫描框架, 持续集成/持续部署, 攻击技术覆盖, 数据可视化, 检测即代码, 目标导入, 精确率测量, 网络安全, 规则版本控制, 请求拦截, 逆向工具, 隐私保护