NikVir/neo-finds-evil

GitHub: NikVir/neo-finds-evil

该项目是一个只读的 Neo4j 图谱关联层,通过 MCP 工具将跨主机和跨时间的取证证据关联能力暴露给 Claude Code 智能体,解决了单文件取证工具缺乏全局关联推理的问题。

Stars: 0 | Forks: 0

# neo-finds-evil:面向 AI 驱动 DFIR 的只读图谱关联层 **SANS "FIND EVIL!" 黑客松参赛作品。** 一个只读的 **Neo4j 图谱关联层**,旨在**扩展 [Protocol SIFT](https://github.com/teamdfir/protocol-sift)**。Protocol SIFT 为 Claude Code 智能体提供了*针对单个取证文件*的取证工具(Volatility、Plaso、Sleuth Kit、EZ Tools、YARA),虽然能对单个文件进行深度分析,但**缺乏跨主机或跨时间的记忆能力**。本项目填补了这一缺失的层级:构建了一个涵盖*所有*主机和*整个*时间线的*每个*取证文件的图谱,并作为**架构上只读的 MCP 工具**暴露给同一个智能体,使其能够进行**跨主机和跨时间的推理**(横向移动、跨主机工具复用、凭证窃取链),这是任何单一工具的输出都无法展现的。 ## 问题所在 现代 IR(事件响应)以机器速度运行,LLM 智能体驱动取证 CLI 的速度比分析师手动输入的速度更快。但每次 CLI 调用都是一座**孤岛**:`vol.py` 转储一台机器的进程;`EvtxECmd` 解析一台主机的事件日志;`log2timeline` 构建一个镜像的时间线。而在企业入侵事件中,真正有价值的证据恰恰存在于这些孤岛*之间*:同一种工具在六台主机上被重复使用,一个账户在一台主机上被暴力破解并在另一台上成功登录,凭证转储器在这里暂存,而 SAM 配置单元在那里被复制。由于关联关系从未存在于任何单一工具的输出中,基于单个取证文件的智能体从字面上**无法表达**这些问题。 `neo-finds-evil` 弥补了这一差距,它将所有取证文件标准化并整合到一个 **Neo4j 图谱**中(主机 × 进程 × 事件 × 文件 × 注册表 × 网络 × 账户 × 时间),并向智能体提供了一套经过严格审查的、基于该图谱的**只读**关联工具。 ## 参赛合规性 FIND EVIL! 要求的每个组件及其具体位置: | 必需组件 | 位置 | |---|---| | 公开代码仓库 | 本仓库,*待定:公开 GitHub 镜像(提交时添加 URL)* | | 开源许可证 | [`./LICENSE`](./LICENSE),Apache-2.0 | | 包含设置说明的 README | 本文件,[设置 / 运行](#setup--run-judge-runbook) | | 逐步运行说明 | 本文件,[设置 / 运行](#setup--run-judge-runbook) | | 功能的文字描述 | 本文件(上文)+ [`docs/`](./docs/) | | 演示视频 | *待定,提交时添加链接* | | 架构图 | [`docs/architecture.png`](docs/architecture.png)(在 [`docs/architecture.md`](docs/architecture.md) 中有描述) | | 证据数据集文档 | [`docs/evidence-dataset.md`](docs/evidence-dataset.md) | | 准确性报告 | [`docs/accuracy-report.md`](docs/accuracy-report.md) | | 智能体执行日志 | [`docs/execution-logs/`](docs/execution-logs/) | ## 架构 ![架构图](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/c5e0ab9587174128.png) *Claude Code 智能体在 Protocol SIFT 的操作规则下运行。Protocol SIFT 提供了基于只读证据挂载点的、针对单个取证文件的 Bash/Skill 工具层;本项目提供了**唯一的 MCP 服务器**:`forensics-graph`,包含五个基于 Neo4j 关联图谱的只读工具。智能体因此获得了跨主机/跨时间的推理能力,而不会获得任何针对证据或图谱的写入路径。* 完整的文字描述、数据/控制流,以及架构与提示词防护栏分析:**[docs/architecture.md](docs/architecture.md)**。 ## 这如何映射到 FIND EVIL! 必需的演示 | 必需的演示 | 证据位置 | |---|---| | **自我纠错** | 每个 MCP 工具都会返回*带有类型的*错误(`unknown_hunt`、`write_rejected`、`timeout`、`unknown_host`、`not_found` 等),并附带一条*建议替代操作*的消息,因此智能体会纠正自身的调用而不是放弃。一次**真实的、有记录的**自我纠错运行(空结果 → schema 发现 → 纠正查询 → 可追踪事件)位于 **[docs/execution-logs/](docs/execution-logs/)**;另请参阅 `docs/protocol-sift-integration.md` §5.1。 | | **准确性 / 可追溯性** | `get_event(event_id)` 可将任何发现解析回其**源 `WindowsEvent`**(channel = 源 `.evtx`),并且针对单个事件的狩猎(hunt)携带 `event_id` + `channel`,提供了一条不间断的*狩猎行 → 事件 ID → 源取证文件*链条。客观诚实的准确性自评(已确认与已推断,以及**我们捕获并纠正的错误**)位于 **[docs/accuracy-report.md](docs/accuracy-report.md)**。 | | **分析推理(跨主机 / 跨时间)** | 核心贡献。该图谱关联了所有 7 台主机及完整的时间线;`run_hunt` / `query_graph` 能够回答任何单一 SIFT 工具都无法回答的问题:横向移动、跨主机工具复用、Empire → PsExec → SAM 窃取链条。数据集及关键发现:**[docs/evidence-dataset.md](docs/evidence-dataset.md)**。 | | **架构级(非基于提示词)防护栏** | 只读保证是**服务器的属性**,而非提示词:没有注册任何写入工具,并且 `query_graph` 在 Neo4j READ 事务(`default_access_mode=READ`)中运行,无论文本内容如何,该事务都会拒绝写入。参见[下文](#the-architectural-read-only-guardrail)及 **[docs/architecture.md](docs/architecture.md)** → *安全边界*。 | ### 架构级只读防护栏 三个层级,按强度排序: 1. **不存在任何写入工具。** MCP 服务器仅注册了五个工具(`list_hunts`、`run_hunt`、`get_host_summary`、`get_event`、`query_graph`),全部为只读。智能体**不具备任何用于** `CREATE`/`MERGE`/`SET`/`DELETE` 的 MCP 接口。这是工具层面的属性,而不是要求智能体遵守的规则。 2. **`query_graph` 在 READ 事务中运行。** 这唯一的自由格式路径在开启会话时使用了 `default_access_mode=READ` 和显式读事务。**Neo4j 驱动程序/服务器会拒绝任何写入**,无论查询文本是什么。这是真正的边界。 3. **词法预检查 + 资源限制(纵深防御)。** 在执行前,经过注释去除和全词匹配的检查会拒绝 `CREATE/MERGE/DELETE/SET/REMOVE/DROP/DETACH/FOREACH`、`LOAD CSV`、`CALL { … } IN TRANSACTIONS` 以及 apoc/db 写入过程;每个查询都被包装在服务器端超时和强制性 `LIMIT` 中。(此词法防护有意**进行过度拒绝**,参见[已知局限性](#known-limitations--future-work)。) 一次真实的写入拒绝(智能体尝试执行 `MATCH (n) DETACH DELETE n`)及其审计日志记录展示在 `docs/protocol-sift-integration.md` §4.0 中。 ## 设置 / 运行(评委操作手册) ### 前置条件 - **SANS SIFT Workstation**(或任何 Linux 主机),并安装了 **Docker**。 - **Claude Code**(`claude` 位于 PATH 中)。 - **[uv](https://docs.astral.sh/uv/)**(Python 包/锁管理器)。 - **Protocol SIFT**(本项目所扩展的框架)。 ### 1. 安装 Protocol SIFT Protocol SIFT 为 DFIR 配置了 Claude Code(它将全局的 `CLAUDE.md`、权限策略和技能剧本复制到 `~/.claude/` 中)。请按照其自身的说明进行安装: ``` curl -fsSL https://raw.githubusercontent.com/teamdfir/protocol-sift/main/install.sh | bash ``` ### 2. 克隆本仓库并同步依赖项 ``` git clone neo-finds-evil && cd neo-finds-evil uv sync # installs deps incl. the `mcp` SDK and the neo4j driver ``` ### 3. 还原取证图谱 该图谱作为发布资产发布(它体积过大,且作为证据不适合存放在 git 中): ``` # 下载发布资产(包含 SRL-2018 案例的预导入 Neo4j /data 目录): mkdir -p ~/neo4j-data wget https://github.com/NikVir/neo-finds-evil/releases/download/v1.0/graph-srl2018.tar.gz # 在信任该文件之前验证其完整性: echo "dfbd512945d53845449932a76be543150bf9ac3f08dee9a8cfaa390ddc51ebb8 graph-srl2018.tar.gz" | sha256sum -c # 预期:graph-srl2018.tar.gz: OK tar -xzf graph-srl2018.tar.gz -C ~/neo4j-data # → ~/neo4j-data/for508 (databases + transactions) # 在该 data 目录上启动 Neo4j 5.x Community(需与 .mcp.json 中的 bolt/creds 匹配): docker run -d --name neo4j-forensics \ --restart unless-stopped \ -p 7474:7474 -p 7687:7687 \ -v ~/neo4j-data/for508:/data \ -e NEO4J_AUTH=neo4j/forensics123 \ -e 'NEO4J_PLUGINS=["apoc"]' \ neo4j:5-community # 等待 bolt 就绪,然后对节点数量进行完整性检查: until docker exec neo4j-forensics cypher-shell -u neo4j -p forensics123 "RETURN 1" >/dev/null 2>&1; do sleep 2; done docker exec neo4j-forensics cypher-shell -u neo4j -p forensics123 "MATCH (n) RETURN count(n) AS nodes" # 预期约 359,788 个节点 ``` ### 4. 注册 MCP 服务器 当您从本目录启动 `claude` 时,位于仓库根目录的已提交文件 **`.mcp.json`** 会自动注册该服务器: ``` { "mcpServers": { "forensics-graph": { "command": "uv", "args": ["run", "--quiet", "forensics-mcp"], "env": { "NEO4J_URI": "bolt://127.0.0.1:7687", "NEO4J_USER": "neo4j", "NEO4J_PASSWORD": "forensics123", "NEO4J_DATABASE": "neo4j", "FORENSICS_MCP_AUDIT": "investigation/mcp-audit.log" } } } } ``` 或者显式注册它: ``` claude mcp add --transport stdio forensics-graph -- uv run forensics-mcp ``` ### 5. 启动智能体并提出一个跨主机问题 ``` claude ``` 然后粘贴一个图谱能够回答,但单文件工具无法回答的问题,即 **`spsql` 跨主机追踪**: 智能体将发现 schema,运行跨主机关联,并将单个失败的登录事件(例如 `SRL-DMZFTP:74648`)解析回其源 `.evtx`,该确切追踪记录在 **[docs/execution-logs/](docs/execution-logs/)** 中。 ## MCP 工具参考 所有五个工具均为只读。源码:`src/forensics/mcp_server.py`;设计说明在 `docs/protocol-sift-integration.md` §4 中。 | 工具 | 签名 | 返回值 | |---|---|---| | `list_hunts` | `() -> [ {name, category, description} ]` | 所有 31 个经过审查的狩猎(类别包括 `credential_access`、`lateral_movement`、`persistence`、`c2_network`、`execution`)。 | | `run_hunt` | `(name: str, limit: int = 100) -> {hunt, rows, row_count, truncated, duration_ms}` | 运行**命名的、经过审查的**狩猎。验证 `name`;拒绝区分大小写的狩猎;读事务 + 服务器超时;行数有上限。这是安全的默认接口。 | | `get_host_summary` | `(hostname: str) -> {hostname, total_events, process_count, events_by_event_id[], relationship_counts{…}}` | 通过锚定计数进行主机定位。如果名称未知,则列出有效的主机。 | | `get_event` | `(event_id: str) -> {event_id, event{…all props…, host}}` | **可追溯性原语。** 将发现解析为其源 `WindowsEvent`(包括 `channel` = 源 `.evtx`)。ID 方案:`:`(例如 `SRL-DC:2952977`)。 | | `query_graph` | `(cypher: str, params=None, limit_enforced: int = 100) -> {rows, row_count, truncated, limit_applied}` | 受保护的、只读的紧急通道,用于临时推理。**拒绝写入**(READ 事务 + 词法预检查);强制性 `LIMIT`;服务器端超时。 | ## 仓库布局 ``` neo-finds-evil/ ├── .mcp.json # project-scoped MCP registration (auto-detected by Claude Code) ├── pyproject.toml # entry points: forensics-mcp (the MCP server), forensics-ingest ├── uv.lock ├── LICENSE / NOTICE # Apache-2.0 ├── README.md # this file ├── config/ │ └── exclusions.yaml # behavioral-hunt denylists/dictionaries (required by 2 hunts) ├── src/forensics/ │ ├── mcp_server.py # ★ the read-only forensics-graph MCP server (5 tools) │ ├── hunt.py # the vetted hunt catalog (QUERIES) behind run_hunt/list_hunts │ ├── neo4j_client.py # read-only query client (read-tx, timeout, retry) │ ├── ingest/ # Phase-1 pipeline that builds the graph (loaders per artifact type) │ └── … # normalization, coverage, correlation, playbooks ├── tests/ # full suite incl. test_mcp_server.py (write-rejection bypass battery) └── docs/ ├── architecture.md # components, data/control flow, SECURITY BOUNDARIES ├── architecture.png # (supplied separately) the diagram referenced above ├── evidence-dataset.md # the SRL-2018 / FOR508 dataset + graph scale + headline findings ├── accuracy-report.md # honest self-assessment: confirmed vs inferred; errors caught ├── protocol-sift-integration.md # recon of Protocol SIFT + how/why we extend it └── execution-logs/ # sanitized agent tool-execution log sample + format guide ``` ## 已知局限性与未来工作 - **Neo4j 社区版是单数据库 / 单用户的。** 社区版实际上并不提供真正的只读数据库*角色*,因此按照设计,**工具层(及 READ 事务)就是强制执行的边界**,并经过了严格的测试(参见 `tests/test_mcp_server.py`)。 - **引擎可移植性。** 狩猎使用 Cypher 表达;移植到另一个图引擎意味着需要重新编写 `QUERIES`。工具*契约*(五个 MCP 工具)是与引擎无关的。 - **聚合狩猎的可追溯性。** 针对单个事件的狩猎携带用于直接 `get_event` 追踪的 `event_id` + `channel`;聚合/计数狩猎(例如 `event_coverage`、`lateral_tool_reuse`)返回的主机和键足以支持后续的 `get_host_summary` / `query_graph`,但本质上它们不是针对单个事件的。 - **EID 10(Sysmon ProcessAccess)在此图谱中无实质内容。** `SourceImage`/`TargetImage`/`GrantedAccess` 在这次加载中并未被解析为节点属性,因此无法仅通过图谱确认针对 LSASS 的凭证访问(这是独立确定的)。参见 `docs/accuracy-report.md`。 - **`query_graph` 的词法防护会过度拒绝,包括一些合法的 DFIR 搜索。** 写入词检查会扫描整个查询文本,*包括字符串字面量*,因此如果某个纯读取操作试图在命令行中搜索反取证特征(例如 `WHERE p.commandLine CONTAINS 'vssadmin delete shadows'`、`reg delete` 或 `Set-MpPreference`),它将被拒绝(因为字面量中包含 `DELETE`/`SET`)。这是**刻意为之的**:扫描面量同样也能捕获被偷偷塞进 apoc 过程字符串参数中的写入动词,而且对于只读取证服务器而言,过度拒绝是安全的失败方向:它只会阻止读取,绝不会允许写入(真正的保障是 READ 事务,而不是词法列表)。实际的变通方法是使用受约束的接口:通过 `run_hunt`(例如 `log_cleared`、`powershell_script`)进行命名狩猎以及 `get_event` 追踪,这些都不经过词法防护。 - **4624 登录去重对时间是盲目的。** 为了合并约 160 万次例行登录,导入过程仅保留每个 `(target_user, source_ip, logon_type)` 元组的*第一个* 4624,该去重键**没有时间组成部分**。如果先前在 09:00 出现过的元组,在之后异常的时间(例如同一用户/IP/类型在凌晨 02:00 登录)再次重复,就会被丢弃,因此像 `after_hours_logons` 这样的时间狩猎只能看到首次出现的情况。这对 SRL-2018 入侵事件影响很小(外部攻击者的横向移动会产生*新*的元组,这些会被保留),但对于内部人员/下班后的场景来说是一个真实的盲点,在此及 `docs/accuracy-report.md` §1 中予以披露,而不是等到被发现。 - **vigia 基准测试的关键结论依赖于较小的样本量 (n)。** 判定准确性是基于 **3** 个 `score_against` 案例计算的,而 FPR 是基于**单个**符合条件的案例计算的,因此唯一可实现的数值较为粗糙(分别为 0/33/67/100% 和 0/100%)。评分器在其输出中披露了这一依据;在引用 100%/0% 这些数字之前,请参阅 `docs/accuracy-report.md` §6 中关于统计功效的警告。 ## 致谢 - 旨在**扩展由 Rob Lee / SANS / teamdfir 开发的 Protocol SIFT**,这是专为 **SANS SIFT Workstation** 设计的 AI 驱动的 DFIR 框架。 - 运行在 **SANS SIFT Workstation** 和 **Neo4j** 图数据库(社区版)上。 - 证据为公开发布的 **SANS FOR508** 实验室场景(Stark Research Labs / SHIELDBASE),参见 `docs/evidence-dataset.md`。所有名称/IP/IOC 均为培训场景的模拟数据,并非真实的个人或基础设施。 基于 **Apache-2.0** 许可(参见 `LICENSE`、`NOTICE`)。
标签:DLL 劫持, MCP, Neo4j, 关联分析, 大语言模型, 库, 应急响应, 数字取证, 自动化脚本, 请求拦截, 逆向工具