Aaditya-Nepal00/CANDOR
GitHub: Aaditya-Nepal00/CANDOR
CANDOR 是一款基于 SIFT Workstation 的自主 DFIR 代理,通过强制置信度标注和确定性校验解决 AI 取证报告中幻觉结论与经验证发现难以区分的问题。
Stars: 0 | Forks: 0
# CANDOR
**附带推理与置信度标注的 DFIR 输出**
AI 取证代理生成的报告中,幻觉结论与经验证的发现看起来如出一辙。CANDOR 强制要求每个发现进入报告前必须带上一个认知标签 —— CONFIRMED(已确认)、INFERRED(已推断)、SUSPECTED(已怀疑)或 UNKNOWN(未知) —— 采用的是确定性的 schema 校验和基于规则的交叉关联,这是 LLM 无法绕过的。
每个发现都带有四种置信度分类之一。LLM 可以降级置信度。它不能升级置信度。
## 针对真实证据的验证
NIST CFReDS "Hacking Case" 是一个公开的取证训练数据集:一台运行 Windows XP SP1 的 Dell Latitude CPi,磁盘镜像属于被称为 "Mr. Evil" 的嫌疑人。在分析开始之前,两个 E01 分段都已针对 NIST 发布的值进行了 SHA-256 校验,随后以只读方式挂载。
```
4Dell Latitude CPi.E01 96bebe80f00541bf28fbc2ef0b02b580082ee6ad58837e991852ae66f077ec31
4Dell Latitude CPi.E02 46bd09821dbb64675e5877d0ad7ec544a571fad5a3fd7fc3f0c3a16278887db5
```
CANDOR 自主运行了 11 分钟。最终统计:**18 个发现 —— 7 个 CONFIRMED,3 个 INFERRED,3 个 SUSPECTED,5 个 UNKNOWN。** 初始扫描在第 11 分钟时结束;随后代理在无人工干预的情况下,将其自身的死胡同建议作为后续任务执行了。
## 演示:NIST CFReDS 调查
### 证据验证与代理启动
CANDOR 不需要提示就知道该先运行什么。`CLAUDE.md` 定义了一个固定的调查顺序;代理遵循该顺序并记录每一步 —— 哪些工具运行了,哪些在重试三次后失败了,以及为什么每个发现会被归入相应的置信度分类。
### 最终报告
HTML 报告是一个独立的单文件,没有外部依赖 —— 可以离线打开,复制到任何地方。顶部的置信度细分明确告诉审查者应该把时间花在哪里。SUSPECTED 和 UNKNOWN 的发现带有从 `dead_ends.json` 提取的具体、可操作的后续步骤。
### SID 归因与反取证
SID `S-1-5-21-2000478354-688789844-1708537768-1003` (Mr. Evil, RID 1003) 出现在 Service Control Manager 日志中,显示在 15:34:01Z 启动了 NetGroup Packet Filter Driver (WinPcap NPF) —— 比 Ethereal 的首次捕获早了 63 秒。该归因已通过独立途径得到确认:提及 `N-1A9ODN6ZXK4LQ\Mr. Evil` 的 Userenv Event ID 1517,以及跨越 7 个以上站点的 IE 浏览器 cookie。
三种独立的取证痕迹类型将同一网络活动置于一个 6 秒的时间窗口内:Application 事件日志中的 EAPOL 事件、System 事件日志中的 WZC 服务记录,以及 NetStumbler 的 `.pf` 创建时间戳。任何单一来源都不足以证明。自动化关联器无法自行确认这一对 —— 它的时间戳邻近规则要求两个来源都必须来自产生输出的工具,而其中两个工具当时已经失败了。代理根据 CLAUDE.md §7 手动执行了交叉关联,记录了全部三个来源,并指出证据已超出关联器自身的 RULE_CONFIRMED 阈值。该发现像其他发现一样经过了 `tag_finding` —— 确定性标注器给出了 CONFIRMED,因为所有三个来源都是在独立记录中对同一事件的直接观察。建立在这些事件基础上的推导出的三阶段攻击叙述被标记为 INFERRED,而不是 CONFIRMED:相互印证的观察和构建的因果链会获得不同的徽章,这种区分来自于分类阶梯,而不是代理的自行决定。
### 关联器行为与死胡同执行
关联器针对 `amcache_without_prefetch` 触发了 RULE_SUSPECTED(HIGH 严重性)。代理将其识别为误报:Amcache 是 Windows 8+ 的产物 —— 它在 XP 上的缺失是预期行为,而非反取证。代理记录了人类可用于验证的特定 registry key,然后继续执行。
`EvtxECmd` 在传统的 `.Evt` 格式上 3 次尝试全部失败 —— 诚实的 UNKNOWN。然后代理执行了它自己的死胡同建议:`evtexport -m all`。SecEvent.Evt 在分配的空间和恢复的 slack 空间中均返回了零条记录。SysEvent.Evt(141 个事件)和 AppEvent.Evt(41 个事件)使用同一工具在相同的日期范围内解析正常。
### 解决死胡同与自我诊断
运行期间,完整性防护机制被触发:case 目录上出现 `hash_before != hash_after`。代理在继续之前停止并进行了调查 —— 完全符合指示。根本原因:`server.py:190` 将 `case_dir` 同时作为 log2timeline 的源和输出目标传递,因此 `timeline.plaso` 被写入了正在计算哈希的目录内。代理验证了单个证据文件的哈希值完好无损,将这种不匹配的根本原因归结为服务器设计问题,而不是证据被篡改,然后才继续执行。修复方案位于 git 历史记录中。
### 事实/解释的分离
这是 CANDOR 核心论点的一个具体示例。
SecEvent.Evt 在分配的空间和恢复的 slack 空间中均返回了零条记录。SysEvent.Evt(141 个事件)和 AppEvent.Evt(41 个事件)使用同一工具在相同的日期范围内解析正常。
零记录结果被标记为 **CONFIRMED** —— 工具运行正常,且输出是直接可观察到的。“安全日志被故意清除”被标记为 **INFERRED** —— 该结论需要跨来源的解释。零记录的安全日志与清除行为相符;但如果没有其他证据印证,它也可能与其他解释相符。事实获得绿色徽章。解释保持为黄褐色徽章。读者可以重新运行 `evtexport` 来验证事实。而解释则被明确标记需要人工审查。
这种分离之所以存在,仅仅是因为 schema 强制要求这样做。如果没有分类阶梯,这两个声明就会以相同的置信度出现在报告中 —— 这正是 CANDOR 旨在解决的问题。
## 架构
MCP server 位于 LLM 和宿主 OS 之间的信任边界上。代理仅通过具有类型的 API endpoint 与目标环境进行交互 —— 它没有 shell 访问权限,没有任意命令执行权限,也没有对证据目录的写入权限。十个只读工具通过 stdio 暴露;每个工具在操作前后都会对证据进行哈希处理,并将这两个哈希值嵌入到结构化响应中。
## 四种置信度分类
**CONFIRMED** —— 工具运行没有错误,且发现可以在原始输出中直接观察到。无需解释。在本次运行中:SID RID 1003 被记录在 Service Control Manager 中,显示其在 15:34:01Z 启动了 NPF 驱动程序,这在 System 事件日志中直接观察到。SecEvent.Evt 返回 0 条记录,已通过 `evtexport` 在分配的空间和 slack 空间中验证。81 个 `.pf` 文件已通过哈希验证完好无损。
**INFERRED** —— 输出有效,但得出结论需要串联线索。在本次运行中:“安全日志被故意清除”。SecEvent.Evt 有 0 条记录,而 SysEvent 和 AppEvent 使用同一工具在相同时期内解析正常。这种不对称性指向了被清除的结论 —— 但指向并不是直接观察,因此该结论不会获得绿色徽章。
**SUSPECTED** —— 输出不完整、存在警告,或发现与另一来源相矛盾。在本次运行中:EvtxECmd 在传统的 `.Evt` 格式上失败,但 Plaso 通过其 EVT 插件从相同文件中恢复了部分事件数据。虽然成功提取但由于覆盖不完整,结果被标记为 SUSPECTED —— 存在真实数据,但缺失部分已被记录在案。
**UNKNOWN** —— 工具重试 3 次后失败,或证据缺失。在本次运行中:Amcache 返回 UNKNOWN —— Amcache 是 Windows 8+ 的产物,在 XP 上理应缺失。符合预期的缺失仍然会被记录;reasoning 字符串记录了原因。同样为 UNKNOWN 的还有:重试 3 次后 `PECmd.py` 依然不在 PATH 中。代理使用 MFT `$SI` 时间戳作为执行代理,指出了精度的降低,并通过哈希验证了 81 个 `.pf` 文件完好无损。
## 证据完整性
每次 MCP 工具调用都会在操作前后对目标证据进行哈希处理。这两个哈希值都会出现在代理处理的结构化输出中。`CLAUDE.md` 指示代理验证它们是否匹配,如果不同则停止并调查,然后再继续。LLM 无法跳过此检查 —— 哈希值存在于它接收的数据中,而不是它可以随意覆盖的建议。
三种实现方式处理不同的证据形态:
- **单个文件** (`Amcache.hve`, `$MFT`, `Security.evtx`):`_sha256()` 直接对文件进行哈希处理。
- **目录** (`Windows/Prefetch/`):`_hash_directory()` 按排序顺序遍历所有 `.pf` 文件,对每个文件进行哈希处理,并将它们组合成一个复合的 `filename:sha256` 清单。
- **整个 case 目录树** (`log2timeline`):`_hash_directory()` 使用递归 `**/*` glob 和 30 秒超时。超时 → `hash_before`/`hash_after` 为 `None`,并在错误字段中附带说明。
任何修改都会在结构化输出中产生可见的哈希不匹配 —— 证据被篡改而不被发现是不可能的。在本次调查中发生的实时哈希触发(如上所述)证明,该防护机制会在实际运行中触发,而不仅仅是在测试中。
## CANDOR 无法做到的事
**无法解析网络捕获。** 没有集成 pcap、Zeek 或 Suricata。其架构支持添加这些功能 —— 编写一个 `@mcp.tool()` 函数,构建命令,调用 `_run()` —— 但目前并不具备。
**它信任底层工具。** 如果 `amcache.py` 存在解析错误并返回看起来合理的错误输出,CANDOR 会将其标记为 CONFIRMED。验证器检查的是输出结构和工具行为,而不是工具的正确性。带有错误数据的干净运行依然会获得绿色徽章。
**最终叙述仍由 LLM 撰写。** 置信度标签、schema 检查和关联规则限制了代理可以令人信服地声明的内容。最终分析由 AI 撰写。对 SUSPECTED 和 UNKNOWN 发现进行人工审查不是可选项 —— 报告会确切告诉您哪些项目需要审查。
**四个分类,没有严重性子级别。** 轻微的 stderr 警告和截断一半的输出都会被归入 SUSPECTED。reasoning 字符串解释了它们之间的区别;如果您需要更细致的颗粒度,可以扩展标注器的分类阶梯。
**标注器对描述的证据进行分类。** `tag_finding` 读取代理提交的发现文本。如果代理将综合推理误描述为直接观察,可能会获得不该有的更高分类。审计追踪是制衡手段 —— 每个发现都引用了其原始的工具调用,因此可以将描述与实际输出进行核对 —— 但这是事后检测,而非预防。
**NIST 案例几乎肯定在 LLM 的训练数据中。** CFReDS Hacking Case 是一个著名的公开数据集,在全球范围内的执法培训中都有使用。CANDOR 的缓解措施是:每个 CONFIRMED 发现都必须追溯到运行期间生成的实际工具输出 —— 包含哈希值。死胡同的后续操作使用 `evtexport` 重新实时验证了关键主张,而不是轻信回忆起的事实。如果实时工具输出与训练数据的记忆相矛盾,熟悉训练数据也无济于事。
## 测试数据
证据可在 [cfreds.nist.gov/all/NIST/HackingCase](https://cfreds.nist.gov/all/NIST/HackingCase) 获取。
文件:
- `4Dell Latitude CPi.E01` — `96bebe80f00541bf28fbc2ef0b02b580082ee6ad58837e991852ae66f077ec31`
- `4Dell Latitude CPi.E02` — `46bd09821dbb64675e5877d0ad7ec544a571fad5a3fd7fc3f0c3a16278887db5`
在 SIFT 上以只读方式挂载:
```
sudo ewfmount "4Dell Latitude CPi.E01" /mnt/ewf
sudo mmls /mnt/ewf/ewf1
sudo mount -t ntfs -o ro,loop,offset=32256 /mnt/ewf/ewf1 /mnt/case_disk
```
偏移量 32256 = 位于扇区 63 × 512 字节处的 NTFS 分区,如 mmls 所示。
在给定相同发现输入的情况下,置信度分类是确定性的;代理的调查路径在不同运行中可能会有所不同,但每一个分类决策都可以从引用的工具输出中重现。
## 入门指南
### 前置条件
- **SANS SIFT Workstation** — [sans.org/tools/sift-workstation](https://www.sans.org/tools/sift-workstation)。如果没有它,每次磁盘取证调用都会返回 UNKNOWN。
- **atility3** — `pip install volatility3`。仅在需要进行内存取证时才需要。
- **Claude Code** 并带有 claude.ai Pro 订阅或 Anthropic API 额度。
- **Python 3.10+**
### 安装
```
git clone https://github.com/Aaditya-Nepal00/Candor-sift
cd Candor-sift
pip install 'mcp[cli]'
```
注册 MCP server 时请使用 **绝对路径** —— 如果 Claude 是从其他工作目录启动的,使用相对路径会悄无声息地失效:
```
claude mcp add candor-sift -- python /absolute/path/to/Candor-sift/mcp_server/server.py
claude mcp list
```
### 运行调查
将证据放入一个 case 目录中。对于磁盘取证:`Amcache.hve`、`$MFT`、位于 `Windows/Prefetch/` 下的 Prefetch 文件、位于 `Windows/System32/winevt/Logs/` 下的事件日志。对于内存取证:在根目录下放置一个 `*.raw`、`*.mem`、`*.vmem` 或 `*.dmp` 文件 —— CANDOR 会根据扩展名自动检测它。
```
claude
```
```
Investigate the case at cases/001/ following the CANDOR protocol.
Run all forensic tools in sequence, tag every finding,
cross-correlate the results, and generate the final report.
```
MCP 工具 `generate_candor_report` 返回已保存报告的绝对路径;代理在调用时传入 `output_dir` 并决定将其写入何处。如果要在没有 Claude Code 的情况下运行(默认为 `/candor_out/`):
```
python agent/loop.py --case cases/001 --output cases/001/candor_out
```
独立循环使用硬编码的 `memory.raw` 文件名,并在重试时循环尝试 `.mem` 和 `.dmp`。通过 MCP 调用的 `get_memory()` 会根据扩展名自动检测;MCP 是具有更智能回退行为的路径。
## 项目结构
```
Candor-sift/
├── agent/
│ ├── CLAUDE.md # System prompt — investigation sequence and rules (111 lines)
│ ├── tagger.py # Epistemic confidence classifier, keyword ladder (216 lines)
│ ├── validators.py # Deterministic schema checks per tool (208 lines)
│ ├── correlator.py # Rule-based cross-correlation engine (254 lines)
│ ├── reporter.py # HTML report generator, zero dependencies (185 lines)
│ ├── loop.py # Standalone agent loop with retry logic (257 lines)
│ └── dead_ends.json # Configurable next-step advisories per confidence class (21 lines)
├── mcp_server/
│ └── server.py # MCP server exposing 10 tools over stdio (424 lines)
├── docs/
│ ├── diagrams/ # Architecture SVG
│ └── screenshots/ # Demo screenshots from NIST CFReDS investigation
├── .gitignore
├── LICENSE
└── README.md
```
## 许可证
MIT — 请参阅 LICENSE
![]() Evidence verification and mount NIST E01/E02 hashes verified against published SHA-256 values; image mounted read-only before the first tool call |
![]() Autonomous investigation sequencing The agent works through the fixed CANDOR sequence: amcache → prefetch → MFT → event logs → correlate → report |
![]() Final report: 18 findings across four confidence classes 7 CONFIRMED · 3 INFERRED · 3 SUSPECTED · 5 UNKNOWN — with prioritized, deduplicated dead-end advisories for every amber and red item |
![]() SID attribution and zero security records Service Control Manager links SID S-1-5-21-...-1003 (Mr. Evil, RID 1003) to NPF driver load at 15:34:01Z; SecEvent.Evt: 0 records across allocated and slack space |
![]() Three-source corroboration AppEvent EAPOL events, SysEvent WZC service stop/restart, and NetStumbler .pf creation timestamp all converge to the same 6-second window at 15:12:37–15:12:43Z |
![]() Correlator false-positive handling RULE_SUSPECTED fires on amcache_without_prefetch (HIGH); agent identifies expected absence for Windows XP and documents the registry check to verify |
![]() Dead-end executed as follow-up Agent runs its own dead-end advisory — evtexport -m all — and returns zero records from SecEvent.Evt, confirming the CONFIRMED finding for zero security records |
![]() Dead ends resolved and integrity issue self-diagnosed Follow-up dead-ends resolved; mid-run hash mismatch diagnosed as server design issue rather than evidence tampering |
标签:AI智能体, DLL 劫持, 大语言模型, 库, 应急响应, 数字取证, 自动化分析, 自动化脚本, 跨站脚本, 逆向工具







