jeffhamm-b/evtx-hunt

GitHub: jeffhamm-b/evtx-hunt

一款基于 Python 标准库的轻量级 DFIR 框架,对 EvtxECmd JSON 输出进行 JSON 规则驱动的威胁狩猎和 IOC 过滤,生成分析师时间线与覆盖范围摘要。

Stars: 0 | Forks: 0

# evtx-hunt [![tests](https://static.pigsec.cn/wp-content/uploads/repos/cas/6b/6b52945adbf8d9e421fe243515ae54cfbd3da263f16b1eabda37cdc0b797b8eb.svg)](https://github.com/jeffhamm-b/evtx-hunt/actions/workflows/tests.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 一个用于 [EvtxECmd](https://github.com/EricZimmerman/evtx) JSON 输出的轻量级 DFIR hunting 框架。 将其指向 EvtxECmd JSONL,运行一次 hunt,即可获得三项结果:分析师时间线 CSV、作为 JSONL 的原始匹配事件,以及关于扫描和匹配内容的摘要。该引擎是一个仅使用标准库的单一 Python 文件。调查能力内置于 JSON hunt 定义中——添加 hunt 无需修改任何 Python 代码。 设计原则: - **保留溯源信息。** 原始事件被原样保留。每一行 CSV 都记录了其 channel、provider 和 Event ID,以及匹配它的 hunt。 - **不作分类。** 引擎绝不自行决定什么是恶意的。Hunt 有意进行宽泛收集;审查和标记由分析师决定。 - **保持可审计性。** 仅使用标准库,无任何依赖,可一次读完。可在任何支持 Python 3.9+ 的地方运行(macOS、Linux、Windows)。 ## 快速开始 ``` git clone cd evtx-hunt # 在捆绑的 synthetic 样本上尝试 ./hunt.py rdp examples/sample_events.jsonl --outdir /tmp/demo cat /tmp/demo/rdp_summary.txt # 运行测试 python3 tests/test_hunt.py ``` 输入为 EvtxECmd JSON 输出:每行一个 JSON 对象(JSONL/NDJSON)。 ## 推荐工作流 最能节省时间的工作模式是:**为了溯源进行一次宽泛的 hunt,然后再针对您的 IOC 进行过滤性的 hunt 以供审查。** IOC pass 是您实际需要阅读的内容;而完整的 pass 是您用作凭据的内容。 ### 1. 暂存日志,然后使用 EvtxECmd 解析 有目的地暂存一组 EVTX 文件,而不是盲目解析——暂存的文件夹准确记录了哪些内容在范围内。`examples/stage_and_parse.bat` 暂存了一组一级日志集(Security、System、Application、Defender、SMB、WMI、WinRM、PowerShell、TaskScheduler、NTLM 以及完整的 RDP/TerminalServices 系列)并运行: ``` EvtxECmd.exe -d .\staged_evtx --json .\parsed --jsonf case_export_tier1_full.json --fj --met true ``` Hunt 只能找到您暂存的内容。入站 RDP 证据存在于 `TerminalServices-RemoteConnectionManager%4Operational` 和 `TerminalServices-LocalSessionManager%4Operational` 中——这很容易被遗漏,且在许多收集配置中都不存在。 ### 2. 在 hunting 前对覆盖范围进行完整性检查 先运行一次宽泛的 hunt,并在信任任何结果之前阅读摘要: ``` ./hunt.py investigation case_export.json --outdir case/full cat case/full/investigation_summary.txt ``` 检查两件事:**channel 计数**(您暂存的日志真的包含在内了吗?)以及**时间范围**(高频 channel,尤其是 Security,滚动覆盖很快——一个只覆盖过去 20 分钟的 Security 日志无法回答关于上个月的问题)。现在发现覆盖缺口只需几分钟;而在审查后才发现则会白白浪费审查的时间。 ### 3. 完整 pass — 溯源 将与您的调查相关且未过滤的 hunt 运行到 `full/` 目录中,并保留所有内容。这些是您完整、可重现的结果集:当后来对某个发现提出疑问时,完整的输出会显示当时还有什么存在以及什么不存在。 ``` for h in investigation rdp authentication persistence scheduled_tasks; do ./hunt.py $h case_export.json --outdir case/full done ``` ### 4. IOC pass — 审查 使用 `--contains-any`(OR 语义)和日期窗口,针对您的案件 IOC 再次进行过滤运行。这通常会将数千行记录缩减为几百行可供审查的记录: ``` IOC='--contains-any 192.0.2.10 --contains-any baduser --contains-any psexec --contains-any lsass' for h in investigation rdp authentication persistence scheduled_tasks; do ./hunt.py $h case_export.json --outdir case/ioc --start 2026-01-10 $IOC done ``` IOC 列表技巧,都是惨痛教训学来的: - **倾向于使用具体的子字符串。** 使用 `cmd.exe` 而不是 `cmd`;跳过像 `find` 这样会匹配到所有内容的词。匹配是大小写不敏感的子字符串匹配——最短的形式涵盖了其各种变体(`rundll32` 涵盖了 `rundll32.exe`)。 - **Event ID 不是文本 IOC。** 不要将 `4688` 放在 `--contains-any` 中——它会匹配到记录号和十六进制字符串。将案件的 Event ID 放在 `hunts/ioc_event_ids.json` 的副本中,并将其作为一个 hunt 运行,同时使用您的文本 IOC 进行过滤以控制数据量。 - **机器账户名**(`HOST$`)出现在该主机自身大量 Security 事件中。保留它们,但预计它们会进行宽松的过滤。 - 每个摘要都会记录所使用的确切过滤器和窗口——IOC pass 保持可重现。 ### 5. 审查和标记 将 IOC pass 生成的 CSV 导入到您的时间线工作簿中。每一行到达时都带有 `TBD` 标签以及完整的溯源信息(channel、provider、Event ID、匹配的 hunt)——故意将标记工作留给分析师,而不是工具的工作。对于任何感兴趣的内容,从 `full/` pass 或使用 `evtx_json_search.py` 提取周围的上下文。 ## hunt.py — hunt 引擎 ### 运行 hunts 交互式(提示输入文件和输出目录): ``` ./hunt.py credential_access ``` 非交互式: ``` ./hunt.py credential_access "" --outdir ``` ### 日期/时间过滤 使用 `--start` / `--end` 将 hunt 限制在一个时间窗口内。在写入 CSV、JSONL 和摘要之前应用。 ``` ./hunt.py rdp --outdir --start "2026-01-15" --end "2026-01-16" ./hunt.py rdp --outdir --start "2026-01-15T22:43:00Z" ``` 仅日期的值涵盖全天(`--end "2026-01-16"` 包含 2026-01-16)。同时支持处理 EvtxECmd 时间戳(`2026-01-15 22:43:29.1234567`)和 ISO/时间线时间戳(`2026-01-15T22:43:29Z`);没有时区的时间戳将被视为 UTC。当时间窗口激活时,具有无法解析的时间戳的事件将被跳过并在摘要中进行计数。 ### 后置过滤匹配项 在 hunt 逻辑运行后,按子字符串修剪结果(不区分大小写): ``` # --contains:所有值必须匹配 (AND) ./hunt.py smb --outdir --contains "192.0.2.10" --contains "ADMIN$" # --contains-any:至少一个值必须匹配 (OR) — 为 IOC 列表构建 ./hunt.py lateral_movement --outdir \ --contains-any "192.0.2.10" --contains-any "evilhost" --contains-any "baduser" ``` ### 输出 每次运行都会在输出目录中生成三个文件: **`_results.csv`** — 可直接用于分析师工作簿的时间线 CSV: ``` Status/Tag, System Name, Date/Time (UTC), Evidence Source, Activity Description, Details/Comments, User ``` `Status/Tag` 默认为 `TBD`——标记是分析师的工作。Activity 列在活动文本下方包含一行 `Hunts: ...` 溯源信息。行按时间顺序排序;事件数据中的逗号、引号、管道符和换行符都会被安全保留。 **`_results.jsonl`** — 原始匹配事件,原样保留。 **`_summary.txt`** — 包含输入文件、日期窗口、扫描的记录数、JSON 解析错误、去重前后的匹配数、抑制的重复项、按 hunt/Event ID/provider/channel 的计数,以及一个**源覆盖范围**表,该表显示了输入中存在的每个 channel 及其事件计数和首次/最后出现的时间戳。在信任结果之前,请先阅读覆盖范围表——它会揭示已滚动覆盖或保留时间短的日志(例如,一个只覆盖过去 20 分钟的 Security channel 无法回答关于上个月的问题)。 ## 内置 hunts | Hunt | 重点 | |---|---| | `investigation` | Meta-hunt:首次分流(credential_access, lateral_movement, smb, services) | | `credential_access` | LSASS 访问、MiniDump、凭据转储、Defender 检测 | | `lateral_movement` | PsExec、PAExec、WinRM、WMI、远程服务、管理共享 | | `smb` | SMB/NTLM 身份验证、失败登录、共享访问 | | `authentication` | 登录、显式凭据、特殊权限、NTLM | | `services` | 服务安装和 Service Control Manager 事件 | | `rdp` | 入站 RDP:1149 身份验证,会话创建/断开/重新连接 (21/24/25) | | `rdp_deep` | 更广泛的 RDP 会话管理活动 | | `defender` | Microsoft Defender 检测、修复、配置更改 | | `powershell` | EncodedCommand、脚本块日志、下载载体 | | `persistence` | Run 键、WMI 事件消费者、IFEO、服务持久化 | | `scheduled_tasks` | 任务注册、修改、删除、执行 | | `wmi` | 远程 WMI 和永久事件消费者 | | `winrm` | PowerShell Remoting 和 WSMan 活动 | | `firewall` | WFP 允许/阻止的连接、规则更改 | | `ioc_event_ids` | 模板:按案件的 Event ID 列表,旨在与 `--contains-any` 配合使用 | `powershell` 和 `scheduled_tasks` 被故意排除在 `investigation` 之外——这些 channel 在健康的系统上噪音很大。 **暂存提示:** hunts 只能找到 EvtxECmd 解析过的内容。为了获得 RDP 覆盖,请确保您的收集暂存了 `Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx` 和 `Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx`。检查摘要中的 channel 计数以确认预期的日志是否存在——并检查覆盖的时间范围,因为高频 channel(尤其是 Security)可能已经发生了滚动覆盖。 ## 编写 hunts 创建 `hunts/myhunt.json`(相对于 `hunt.py` 进行解析,因此从任何目录运行均可),然后运行 `./hunt.py myhunt`。 ### 正向条件 进行 AND 运算;`terms` 是任意匹配: ``` name Display name description Shown in summary activity Default Activity Description activity_map Event ID -> Activity Description event_ids Only these Event IDs providers Provider name contains any of these (case-insensitive) channels Channel name contains any of these (case-insensitive) terms Event text contains any of these (case-insensitive) ``` ### 特定于 Provider 的 Event ID Event ID 仅在每个 provider 内是唯一的——21 在每个日志中的含义都不一样。将它们配对使用: ``` "provider_event_ids": { "Microsoft-Windows-TerminalServices-RemoteConnectionManager": ["1149"], "Microsoft-Windows-TerminalServices-LocalSessionManager": ["21", "24", "25"] } ``` Provider 名称完全匹配(不区分大小写)。一对匹配项就是一个自满足的正向匹配——它不需要 `terms`。同一个 hunt 中的全局 `event_ids`/`providers`/`terms` 将作为附加的 OR 分支。`channels` 和 `exclude_*` 过滤器仍然适用。 ### 排除过滤器 优先应用;任何命中都会丢弃该 hunt 的事件: ``` exclude_terms Drop if event text contains any of these exclude_providers Drop if provider name contains any of these exclude_channels Drop if channel name contains any of these exclude_processes Drop if any EventData process/image field (ProcessName, CallerProcessName, Image, ...) contains any of these ``` 示例——`credential_access` 附带了已知的 EDR/AV lsass 访问排除项: ``` "exclude_processes": ["MsMpEng.exe", "CSFalconService.exe"] ``` 保持排除列表紧凑。排除 EDR 二进制文件是合理的;排除像 `taskmgr.exe`(攻击者用它来转储 lsass)这样的双重用途工具会隐藏证据。排除是分析师负责的每个 hunt 的调优决策。 ### Meta-hunts 带有 `include` 列表的 hunt 会运行列出的 hunts 并合并/去重结果: ``` { "name": "Investigation Triage", "include": ["credential_access", "lateral_movement", "smb", "services"] } ``` 支持嵌套 include;循环会被忽略。匹配多个 hunt 的事件只出现一次,并在 Activity 列中注明所有匹配的 hunt。 ## evtx_json_search.py — 快速手动搜索 用于在完整 hunt 之前(或代替完整 hunt)进行探索性搜索: ``` ./evtx_json_search.py "" lsass ./evtx_json_search.py "" 192.0.2.10 ./evtx_json_search.py "" "Invoke-.*" --regex ./evtx_json_search.py "" service --event-id 7045 ./evtx_json_search.py "" lsass --provider Defender --limit 20 ./evtx_json_search.py "" lsass --start "2026-01-15" --end "2026-01-16" ./evtx_json_search.py "" lsass --csv-out lsass.csv ``` 打印带有上下文片段的匹配事件;`--csv-out` 写入与 hunt.py 相同的时间线 CSV 格式。在确定进行 hunt 之前将其用于快速探索,或者在审查期间提取单个指标的完整上下文。 ## 演示 `examples/demo/` 包含了分析师如何处理此数据的完整演练 —— 一个确定性的合成安全事件(RDP 入侵 → PsExec → LSASS 转储 → Defender 检测)以及一份带有解说的 [DEMO.md](examples/demo/DEMO.md),它 遵循了真实的决策循环:检查覆盖范围,进行大范围查看,根据 指标(以及在指标未传播时根据时间)进行追踪,组装 跨 channel 的时间线,最后映射到 ATT&CK。生成数据并 跟随操作: ``` cd examples/demo python3 make_demo_data.py > demo_events.jsonl ``` ## 贡献 Hunt 定义是最简单的贡献方式——如果您为这里未涵盖的日志源(Kerberos、AppLocker、DNS、USB、MSI、Exchange 等)构建了 hunt JSON,请提交 PR。保持 hunt 的通用性:不要包含特定案件的 IOC,并在 `description` 中记录有噪音的 Event ID。完整指南请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。 ## 许可证 MIT — 见 [LICENSE](LICENSE)。
标签:Homebrew安装, PB级数据处理, Python, Windows Evtx, 安全运维, 库, 应急响应, 无后门, 时序数据库, 逆向工具