ThePicpixel/Triage

GitHub: ThePicpixel/Triage

基于 LangGraph 构建的智能体恶意软件研判 pipeline,自动完成静态与动态分析并通过本地 LLM 综合生成结构化判定结论。

Stars: 0 | Forks: 0

# triage 一个基于 [LangGraph](https://github.com/langchain-ai/langgraph) 构建的智能体恶意软件研判 pipeline。它接收任意二进制文件,运行静态分析(熵、字符串、导入表、节),有条件地在沙箱中进行动态/行为分析(CAPEv2、capa、YARA、pcap、内存取证),并通过本地 LLM(Ollama)综合生成结构化判定结论 —— `benign` / `suspicious` / `malicious` / `inconclusive` —— 同时附带证据和 MITRE ATT&CK 映射。 ## 工作原理 ``` ┌─────────┐ sample ───► │ intake │ sha256, file type └────┬────┘ │ ┌──────────▼──────────┐ │ static_analysis │ parallel fan-out (Send API): │ (subgraph) │ entropy · strings · imports · sections └──────────┬──────────┘ │ ┌──────▼──────┐ │ triage_gate │ conditional edge: weighted finding score ≥ 0.5? └──┬───────┬──┘ suspicious │ │ benign-enough │ │ ┌──────────▼─────┐ │ │ dynamic_runner │ │ subgraph: detonate (CAPE) → capa · yara · pcap · memory └──────────┬─────┘ │ │ │ ┌▼───────▼┐ │ report │ LLM (Ollama) synthesizes a structured Verdict └─────────┘ ``` 状态(`triage/state.py`)是一个贯穿每个节点的单一 `TypedDict`。并行 worker 通过 `add` reducer 向共享的 `findings` 列表中追加内容,并且每个子图(`static`、`dynamic`、`memory`)将其 dict 浅合并到状态中,而不会覆盖同级数据。每当静态风险评分较低时,将跳过动态分析;如果引爆失败或超时,会降级为一条发现结果,而不是导致 graph 崩溃 —— 报告节点始终会运行。 ## 环境要求 - Python **3.11+** - 本地运行 [Ollama](https://ollama.com/),并拉取了模型(默认为 `llama3.1:8b`,但也已在 `gemma4:e4b` 上测试过)—— 用于最终判定结论的合成 - 可选的外部工具,如果缺失,每个都会优雅降级为低严重程度的“不可用”发现: - `PATH` 路径下的 [`capa`](https://github.com/mandiant/capa) —— ATT&CK 技术提取 - `PATH` 路径下的 [`yr`](https://github.com/VirusTotal/yara-x) (YARA-X) —— 规则匹配 - [Volatility 3](https://github.com/volatilityfoundation/volatility3) (`python3 -m volatility3.cli`) —— 内存取证 - `libmagic`(`python-magic` 所需;在 Debian/Ubuntu 上使用:`apt install libmagic1`,在 macOS 上使用:`brew install libmagic`) - 一个可访问的 [CAPEv2](https://github.com/kevoreilly/CAPEv2) REST API,用于实际引爆(请参阅下方的[沙箱 / 实验环境设置](#sandbox--lab-setup)) —— 没有它,`detonate` 会优雅超时,并且 pipeline 依然会仅根据静态分析结果生成报告 ## 安装说明 ``` python3 -m venv venv source venv/bin/activate pip install -e ".[dev]" # 拉取用于报告综合的 model ollama pull llama3.1:8b ``` ## 配置说明 所有配置均通过环境变量进行;每一个都有合理的默认值。 | 变量 | 默认值 | 用途 | |---|---|---| | `OLLAMA_MODEL` | `llama3.1:8b` | 报告节点用于合成判定结论的模型 | | `REPORTS_DIR` | `reports` | 写入 `.md` 和 `.json` 报告的目录 | | `CAPE_URL` | `http://localhost:8000` | CAPEv2 REST API 的基础 URL | | `CAPE_MAX_POLLS` | `60` | 等待引爆完成时的最大轮询尝试次数 | | `CAPE_POLL_INTERVAL` | `5` | 轮询之间的秒数 | ## 用法 ### 单一样本 ``` from triage.graph import graph result = graph.invoke({"file_path": "/path/to/sample", "findings": []}) print(result["verdict"]) # structured Verdict dict print(result["report_path"]) # reports/-.md print(result["json_path"]) # reports/-.json ``` 每次运行都会将一份 Markdown 报告(判定结论、置信度、关键证据、ATT&CK 表、IOC)和包含完整状态(发现、静态/动态/内存数据、判定结论)的 JSON 转储写入 `REPORTS_DIR`。 ### 批量 / 语料库模式 对目录中的每个文件运行 graph 并获取 CSV 汇总: ``` python -m triage.batch samples/ # defaults to reports/ for output python -m triage.batch samples/ my_output_dir ``` 这将写入 `my_output_dir/batch-.csv`,每个样本占一行:`filename, sha256, classification, confidence, report_path, error`。在处理过程中抛出异常的样本会被记录为 `classification=error`,而不是中止整个批处理。 ### 冒烟测试 EICAR 测试字符串(`samples/eicar.com`)是一种安全、标准的方式,用于端到端测试整个 pipeline,而无需处理真实的恶意软件: ``` from triage.graph import graph result = graph.invoke({"file_path": "samples/eicar.com", "findings": []}) ``` ## 沙箱 / 实验环境设置 动态分析需要在 `CAPE_URL` 处可访问的正在运行的 CAPEv2 实例。 **安全规则,不可妥协:** - 切勿在隔离环境(没有真实网络路由的容器/虚拟机)之外运行未知的二进制文件。 - 在每个样本之间获取/恢复干净的引爆环境 —— 切勿重复使用“脏”的 worker。 - 仅分析您获得授权可以处理的样本。 ## 测试说明 ``` pytest ``` 需要 `samples/eicar.com` 或 `samples/minimal.exe` 的测试会在这些文件不存在时自动跳过。外部工具(CAPE、capa、yara-x、Volatility)在测试中均被模拟 —— 运行测试套件不需要真实的沙箱。 ## 项目布局 ``` triage/ ├── state.py # TriageState TypedDict, Finding & Verdict pydantic models ├── graph.py # top-level graph: static → triage_gate → (dynamic |) → report ├── batch.py # corpus mode: directory in, CSV of verdicts out ├── nodes/ │ ├── intake.py # hash + file-type identification │ ├── static.py # static subgraph, parallel fan-out over static tools │ ├── triage_gate.py # conditional edge: weighted finding score │ ├── dynamic.py # dynamic subgraph: detonate then fan out to capa/yara/pcap/memory │ └── report.py # LLM verdict synthesis + Markdown/JSON report rendering └── tools/ ├── static_tools.py # file_identity, compute_entropy, extract_strings, parse_imports, parse_sections └── dynamic_tools.py # detonate (CAPE), run_capa, scan_yara, analyze_pcap, analyze_memory samples/ # EICAR + minimal PE test fixtures reports/ # generated .md / .json triage reports (gitignored) tests/ # pytest suite, mocks external tools/sandbox ``` ## 发现与判定结论 schema 每个分析器都会输出归一化的发现结果,这些结果将输入到研判关卡和最终报告中: ``` {"source": "entropy", "signal": "high_entropy_section", "severity": "high", "evidence": "section '.text' entropy=7.42", "weight": 0.5} ``` 报告节点要求 LLM 严格基于这些发现给出答案,并选择 `inconclusive` 而不是进行猜测: ``` class Verdict(BaseModel): classification: Literal["benign", "suspicious", "malicious", "inconclusive"] confidence: float # 0–1 summary: str key_evidence: list[str] mitre_attack: list[str] # ATT&CK technique IDs, from capa iocs: list[str] # IPs, domains, hashes ```
标签:AI风险缓解, DAST, LangGraph, 云安全监控, 恶意软件分析, 沙箱检测, 逆向工具, 静态分析