desledishant10/mcp-witness

GitHub: desledishant10/mcp-witness

一款面向 MCP 服务器的安全扫描器,通过静态规则与动态场景自动化检测 SSRF、DNS rebinding 等漏洞并管理协调披露流程。

Stars: 0 | Forks: 0

# mcp-witness [![PyPI version](https://img.shields.io/pypi/v/mcp-witness.svg)](https://pypi.org/project/mcp-witness/) [![tests](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/ec4ee02846130844.svg)](https://github.com/desledishant10/mcp-witness/actions/workflows/tests.yml) [![ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![license: Apache 2.0](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](LICENSE) [![python: 3.11+](https://img.shields.io/badge/python-3.11+-blue)](pyproject.toml) [![findings: 12](https://img.shields.io/badge/findings-12-orange)](findings/) [![CVE-track classes: 2](https://img.shields.io/badge/CVE--track_classes-2-red)](findings/) ## 针对 PyPI 发布的 MCP 服务器的六项协调安全披露 **一项已验证的上游修复已发布。** **一项维护者确认未维护的结果。** **四项处于协调禁运期内的提交中,禁运期至 2026-08-10。** mcp-witness 是围绕 [`disclosures/`](disclosures/) 中的披露记录构建的。扫描器 —— 包含 14 条静态规则 + 7 个动态场景 + 一个能力分类器 —— 是推动这些提交的引擎。披露记录及其结果是持久存在的产出物: - **`mcp-server-fetch` v2025.4.7** — 在 EC2 上演示了 SSRF,并检索到了真实的 AWS IAM 凭证(`AccessKeyId` / `SecretAccessKey` / `Token` 三元组)。协调披露已于 2026-05-12 作为 [modelcontextprotocol/servers#4143](https://github.com/modelcontextprotocol/servers/issues/4143) 提交。由 `@kgarg2468` 提供的修复 PR [#4226](https://github.com/modelcontextprotocol/servers/pull/4226) 已于 2026-05-22 发布,包含 scheme allowlist + RFC-reserved-range denylist + 每次重定向的验证。✅ **已于 2026-05-22 独立重新验证** — 之前返回 IAM 凭证的 EC2 演示现在返回 *"Fetching private or non-public IP addresses is not allowed"*。 - **`mcp-server-http-request` v0.1.0** (statespace) — 同类 SSRF。于 2026-05-12 通过电子邮件提交,沉默超过 +30 天,随后维护者于 2026-06-11 通过 LinkedIn DM 确认未维护("not an actively maintained package")。Yank 请求待处理。 - **4× DNS rebinding** 出现在 HTTP 传输服务器中 — `mcp-streamablehttp-proxy`, `mcp-fetch-streamablehttp-server`, `fastmcp-http`, `mcp-server-fetch-sse`。均处于 2026-08-10 禁运的协调披露中。可通过 [`poc/dns-rebind/`](poc/dns-rebind/) 中的容器化测试工具(`make demo`)实现端到端的复现。 涵盖 MCP 传输边界两端(服务器向外请求 [SSRF]、浏览器向内请求 [DNS rebind])的两个 CVE 级漏洞类别。发现它们的检测器是静态规则 `MCP-S-009` (SSRF) + `MCP-S-014` v0.3 (DNS rebind,基于本次调查开发的四部分 W1–W4 系列) + 动态场景 `MCP-D-003` (SSRF)。检测器的演进本身就是审计跟踪的一部分 —— 每一个补丁都针对其促使产生的发现进行了记录。 **完整披露记录:** [`disclosures/`](disclosures/) — 状态表、方法论说明、渠道决策审计跟踪。 **单次发现的证据:** [`findings/`](findings/) — 复现 + 原始输出 + 解释,每次观察一个文件。 ## 发现记录 针对十一个 PyPI 发布的服务器的十二项已记录审计观察,收录在 [findings/](findings/) 中(状态表索引位于 [findings/README.md](findings/README.md)),外加一份 [DNS-rebinding 类别调查](findings/2026-05-12-dns-rebinding-survey.md),将其中四项定性为同一类别: | Date | Target | Test | Outcome | |---|---|---|---| | 2026-05-11 | `mcp-server-fetch` | [D-003 (direct SSRF probe)](findings/2026-05-11-MCP-D-003-fetch-direct-environment-dependent-ssrf.md) | **漏洞** — 依赖环境的 SSRF,于 2026-05-12 在 EC2 上演示(检索到了真实的 IAM 凭证);披露 [#4143](https://github.com/modelcontextprotocol/servers/issues/4143);**修复 PR [#4226](https://github.com/modelcontextprotocol/servers/pull/4226) 已于 2026-05-22 独立验证** | | 2026-05-11 | `mcp-server-http-request` | [D-003 (direct SSRF probe)](findings/2026-05-11-MCP-D-003-http-request-direct-environment-dependent-ssrf.md) | **漏洞** — 同类 SSRF 的第二个实例;**披露邮件于 2026-05-12 提交**(禁运期至 2026-08-10) | | 2026-05-12 | `mcp-streamablehttp-proxy` v0.2.0 | [S-014 (static, DNS rebinding)](findings/2026-05-12-MCP-S-014-streamablehttp-proxy-dns-rebinding.md) | **漏洞** — 127.0.0.1 + 无 Origin/Host 检查;针对其代理的任何 stdio MCP 的通用提权;**披露已于 2026-05-12 提交**(禁运期至 2026-08-10) | | 2026-05-12 | `mcp-fetch-streamablehttp-server` v0.2.0 | [S-014 (static, DNS rebinding + 0.0.0.0 + recursive SSRF)](findings/2026-05-12-MCP-S-014-fetch-streamablehttp-server-dns-rebinding.md) | **漏洞** — 0.0.0.0 + 通配符 CORS + 继承的 fetch SSRF;与 proxy 发现一同披露(禁运期至 2026-08-10) | | 2026-05-12 | `fastmcp-http` v0.1.4 | [S-014 (static, DNS rebinding)](findings/2026-05-12-MCP-S-014-fastmcp-http-dns-rebinding.md) | **漏洞** — Flask dev server 运行在 0.0.0.0 上,完全没有任何中间件;**公开 issue 披露已于 2026-06-02 提交**(禁运期至 2026-08-10) | | 2026-06-02 | `mcp-server-fetch-sse` v0.1.1 | [S-014 (static, W1+W3 — aiohttp TCPSite)](findings/2026-06-02-MCP-S-014-mcp-server-fetch-sse-dns-rebinding.md) | **漏洞** — DNS rebind + 继承的 PR-#4226 前的 SSRF;**已于 2026-06-02 通知维护者和 Anthropic Security**(禁运期至 2026-08-10) | | 2026-05-11 | `mcp-server-fetch` | [D-001 against Claude Opus 4.7](findings/2026-05-11-MCP-D-001-fetch-opus47-defense.md) | 防御 | | 2026-05-11 | `mcp-server-fetch` | [D-006 against Claude Opus 4.7](findings/2026-05-11-MCP-D-006-fetch-opus47-defense.md) | 防御 | | 2026-05-11 | `mcp-server-time` | [S-003 static, 3 hits](findings/2026-05-11-MCP-S-003-time-static-param-injection-pattern.md) | 信息(存在模式;在此部署中是良性的) | | 2026-05-11 | `mcp-server-git` | [D-002 (direct path-traversal)](findings/2026-05-11-MCP-D-002-git-direct-defense.md) | 防御(纵深防御示例) | | 2026-05-11 | `mcp-server-aidd` | [D-002 (direct path-traversal)](findings/2026-05-11-MCP-D-002-aidd-direct-defense.md) | 防御(允许的目录限制生效) | | 2026-05-11 | `mcp-server-aidd` | [S-001 + S-002 + S-005 (static, multi-hit)](findings/2026-05-11-aidd-three-rule-multi-hit.md) | 信息(同时触发 3 条规则 — 模式压力测试) | 每个条目都包含复现命令、原始跟踪、解释、注意事项和披露建议。**目前有两个 CVE 级别的类别正在处理中:stdio fetch 服务器中的 SSRF(2 个包,已披露;1 个修复已验证,1 个维护者确认未维护)和 HTTP 传输服务器中的 DNS rebinding(4 个包,均已披露)。** ## 快速开始 — 使用单个命令审计任何可 pip 安装的 MCP 服务器 ``` pip install mcp-witness mcp-witness-audit mcp-server-fetch ``` 该单条命令会 pip 安装 `mcp-server-fetch`(或任何其他 PyPI MCP 服务器),通过 stdio 捕获其 `tools/list`,运行静态分析器规则,运行能力分类器,并打印人类可读的报告。针对官方 Anthropic 参考服务器的示例输出: ``` === mcp-server-fetch === Launched: /usr/bin/python3 -m mcp_server_fetch Tools: 1 fetch Capability tags: net_egress 2 analyzer findings: [HIGH ] MCP-S-001 fetch Tool description contains instruction-like phrasing directed at the model. [HIGH ] MCP-S-009 fetch Tool has URL parameter(s) ['url'] with no schema-level constraint and no validation keywords in the description. Capture saved to: calibration/reports/captured-mcp-server-fetch.json ``` 仅凭一条 `mcp-witness-audit` 命令,就在官方 Anthropic 参考服务器上发现了两个真实的发现 —— 一个处于描述层面(S-001 捕捉到了工具 docstring 中针对 agent 的指令),另一个处于 schema 层面(S-009 捕捉到了缺失的 SSRF allowlist)。其中 SSRF 问题此前已在 [EC2 上端到端演示](findings/2026-05-11-MCP-D-003-fetch-direct-environment-dependent-ssrf.md)(检索到了真实的 IAM 凭证),通过 #4143 披露,并由 PR #4226 修复(已验证)。 ## 包含的内容 ### CLI | Command | Purpose | |---------|---------| | `mcp-witness-audit` | **一键执行:** 针对任何可 pip 安装的 MCP 服务器进行安装 + 捕获 + 分析 + 分类 + 报告 | | `mcp-witness-capture` | 连接到任何 stdio MCP 服务器,将 `tools/list` 输出为 JSON | | `mcp-witness-scaffold-gt` | 从捕获的数据生成校准的 ground truth 骨架 | | `mcp-witness-analyze` | 静态分析 — Python 源代码或捕获的 JSON | | `mcp-witness-classify` | 在工具定义上运行能力分类器 | | `mcp-witness-eval-calibration` | 将分类器预测与手工标记的 ground truth 进行比较 | | `mcp-witness-lint-scenarios` | 针对场景文件进行 YAML lint(捕捉 null-byte smuggling、解析错误、schema 违规) | | `mcp-witness-test` | 针对真实的 MCP 服务器运行动态场景,可选择使用真实的 LLM agent | | `mcp-witness-disclose` | **协调披露助手。** 脚手架生成新的披露记录,跟踪天数里程碑(`status`),生成适应当天的跟进回复内容(`ping`) | ### 静态分析器规则(已实现 14 条 v0.1 规则中的 14 条) | ID | What it catches | Mode | |----|---|---| | `MCP-S-001` | 工具描述中的祈使语气 (you-must, now-you-can, grants-you, were-advised) | per-tool, heuristic | | `MCP-S-002` | 一个工具的描述按名称引用了另一个工具(poisoning vector) | server-level, heuristic | | `MCP-S-003` | schema 子字段中的隐藏指令(参数描述、标题、`$comment`) | per-tool, heuristic | | `MCP-S-004` | 工具标注声明为只读 / 非破坏性,但名称/描述却表明相反 | per-tool, heuristic | | `MCP-S-005` | 过宽的能力表面(例如 `fs_read` + `net_egress` = 渗出对) | server-level, classifier-driven | | `MCP-S-006` | 文件处理工具中的 path traversal | per-tool, AST + taint | | `MCP-S-007` | Shell 命令注入 (`subprocess(shell=True)`, `os.system`, `os.popen`) | per-tool, AST | | `MCP-S-008` | 没有明显输入约束的数据库查询工具(没有提及参数化查询,没有 schema 模式) | per-tool, heuristic on tools/list | | `MCP-S-009` | 没有 scheme/host allowlist 的 URL 获取工具(捕捉由 D-003 动态标记的 SSRF 类别) | per-tool, heuristic on tools/list | | `MCP-S-010` | 源代码中提交的硬编码 API keys / tokens / PEM 私钥 / JWTs / `.env` 文件 | repo-level, regex + filename | | `MCP-S-011` | Tool handler 将参数、请求数据、headers 或环境衍生的密钥记录到 stderr/stdout(调试门控调用被抑制) | per-tool, AST | | `MCP-S-012` | 引用了 `RootsCapability` 但从未调用 `list_roots()` — 声明的限制保证未被执行 | repo-level AST | | `MCP-S-013` | Prompt 模板将 handler 参数插入到 `system`/`assistant` 角色消息中而没有进行清理 | repo-level, AST + light taint | | `MCP-S-014` | HTTP transport 绑定到 loopback / `0.0.0.0` 而没有进行 Origin/Host 验证(DNS rebinding);CORS `allow_origins=['*']` + `allow_credentials=True` 反模式。v0.3 增加了 W1(host-variable 解析)、W2(基于 AST 的 Origin 验证检查)、W3(aiohttp.web 绑定形态)、W4(`os.getenv` 默认值解析)。 | repo-level, AST | 每条规则的词汇决策都附有促使产生它们的校准证据注释。所有 14 条规则的规范:[docs/static-rules.md](docs/static-rules.md)。 ### 动态场景(初始集中有 7 个) 场景 YAML 格式:[docs/scenario-schema.md](docs/scenario-schema.md)。初始集位于 [scenarios/](scenarios/) 中,并在 [scenarios/README.md](scenarios/README.md) 中进行了描述。亮点: - `MCP-D-002` — 文件系统读取工具中的 path traversal(直接探测,不需要 LLM) - `MCP-D-003` — URL 获取工具中的 SSRF(直接探测;产生 cloud-metadata 发现) - `MCP-D-001` / `MCP-D-006` — 两个短语层级的 tool-description 注入(明显与微妙) - `MCP-D-004` — 工具定义 rug pull(在首次批准后突变) - `MCP-D-005` — 通过工具输出注入不可见的 Unicode-tag-character - `MCP-D-007` — 通过 URL fetcher 进行的 cloud metadata-service 渗出(严格预言机 — 仅在实际的 metadata 响应内容上触发,专为 EC2/GCP/Azure 审计验证设计) ### 能力分类器 Layer 1 (lexical) 分类器由分析器规则和 harness 场景过滤共享。8 个能力标签,8 个参数角色,三层置信度(`high` / `medium` / `low`)。根据[规范](docs/capability-classifier.md),提升至 "stable" 需要 ≥10 个目标的语料库 + 对 `high` 置信度输出达到 ≥90% 的精度。 **当前校准语料库状态:** 11 个标记目标,87 个工具,**在所有六个执行的能力标签上达到 100% 的精度和 100% 的召回率**(`db_query`, `db_write`, `exec`, `fs_read`, `fs_write`, `net_egress`)。通过直接捕获对照 11 个目标中的 8 个进行了验证。达到了规范中 ≥10 目标 / ≥0.9 精度 / ≥0.75 召回率的 "stable" 阈值。**受 CI 保护以防止回归:** [calibration/tests/test_corpus_regression.py](calibration/tests/test_corpus_regression.py) 在每个 PR 上运行完整的评估,如果精度降至 0.90 以下、召回率降至 0.75 以下、目标计数降至 10 以下、参数角色准确率降至 0.80 以下,或者原始的四个 v0.1 标签中的任何一个从语料库中消失,就会失败。参见 [calibration/README.md](calibration/README.md)。 ### 动态测试工具 根据场景自动选择两种模式: - **Direct mode** — 无 agent,harness 作为 MCP 客户端。用于服务器端验证测试(path traversal, SSRF)。 - **Proxy mode** — harness 在被测 agent 和真实目标之间进行调解,对运行中的工具描述和输出应用突变。用于 agent 端场景(描述注入、rug pull、输出注入)。 两种 agent 驱动实现:`stub`(确定性,管道测试)和 `anthropic`(真实的 Claude tool-use 循环)。参见 [harness/README.md](harness/README.md)。 ## 架构 ``` mcp-witness/ analyzer/ Static analysis: Python AST + captured-JSON modes, 14 rules classifier/ Capability tagger: 8 tags, 8 param roles, Layer 1 (lexical) harness/ Dynamic test runner: direct + proxy modes, stub + Claude agents scenarios/ Attack-scenario YAML library (7 scenarios) calibration/ Ground-truth corpus + eval driver + capture/scaffold tools findings/ Audit-trail record (12 entries + 1 class survey; append-only) disclosures/ Append-only log of outgoing coordinated-disclosure communications docs/ Specs: rules, scenarios, classifier, threat model ``` ## 路线图和当前状态 | Phase | Planned window | Status | |---|---|---| | 1 — 静态分析器 | 第 1–6 周 | **已完成。** 14 条 v0.1 规则(S-001..S-014)已全部实现;v0.3 补丁 W1–W4 弥补了由调查暴露的 DNS-rebind 检测器漏洞。Python AST + 捕获的 JSON 模式 + repo 级别扫描;带有严重性过滤和 CI 友好退出代码的 CLI。 | | 2 — 动态 harness | 第 7–14 周 | 基本完成:direct + proxy 模式,两种 agent 驱动,7 个场景可针对真实服务器端到端运行。 | | 3 — 真实世界审计 | 第 15–20 周 | **基本完成。** 针对发布在 PyPI 上的 11 个服务器记录了 12 个发现,外加一项 [DNS-rebinding 类别调查](findings/2026-05-12-dns-rebinding-survey.md)。两个 CVE 级别类别:fetch 系列服务器中的 SSRF(2 个包已披露,**1 个修复已发布 + 独立验证**)和 HTTP 传输服务器中的 DNS rebinding(4 个包,全部处于禁运期至 2026-08-10 的协调披露中)。 | | 4 — 润色 + 发布 | 第 21–26 周 | **进行中。** 禁运日博客草稿位于 [drafts/](drafts/)(禁运前排除在 Pages 索引之外);EC2 审计手册位于 [docs/](docs/)。PyPI 发布 + 会议提交已排队。 | ## 范围和非目标 v1 的范围内: - 在 MCP 服务器实现中进行静态和动态漏洞发现 - 针对符合规范的服务器的 Agent 级别攻击场景 - Python 服务器(分析器);任何语言的服务器通过捕获的 `tools/list` JSON 支持 - stdio 传输(harness) - MCP spec 版本 `2025-06-18` 尚未实现(已计划): - TypeScript 分析器支持(tree-sitter;将解锁 5 个排队的 TS 校准目标) - harness 中的 SSE / Streamable HTTP 传输 - DNS / 文件系统 canaries(目前仅支持 HTTP) v1 范围之外(有意为之 — 这些是很好的后续工作,而不是功能): - 运行时 "agent firewall" / 行为检测 / EDR-for-agents - 非 MCP agent 框架(LangChain、AutoGPT、自定义 orchestrators) - 生产级 WAF 风格的保护 - 超出扫描器输出范围的宿主侧强化建议 ## 项目指标 | | | |---|---| | 通过的测试 | **191 / 191** | | 分析器规则 | **14 条中的 14 条** (S-001..S-014) — v0.1 规范完成 + v0.3 W1–W4 补丁 | | 动态场景 | 7(5 个来自 v0.1 初始集 + D-006 subtle-injection + D-007 cloud-metadata-exfil) | | 校准语料库 | **11 个标记目标,87 个工具,100/100 精度-召回率**(8 个通过直接捕获验证) — 达到了规范的 "stable" 阈值;通过 [test_corpus_regression.py](calibration/tests/test_corpus_regression.py) 受 CI 保护 | | 真实世界发现条目 | **12 + 1 项类别调查**(跨 2 个披露跟踪类别的 6 个漏洞 — SSRF + DNS rebinding;4 个防御;2 个信息性) | | 已提交的协调披露 | **6**(1 个修复已发布 + 独立验证;5 个正等待维护者回应,禁运期至 2026-08-10) | | 包 | 5(`analyzer`、`classifier`、`harness`、`calibration` + 作为 YAML 的 `scenarios`) | | Console scripts | 8 | ## 运行测试套件 ``` pip install -e ".[dev]" pytest ``` 包含覆盖率: ``` pytest --cov=analyzer --cov=classifier --cov=harness --cov=calibration --cov-report=term-missing ``` Lint + 格式化: ``` ruff check . ruff format --check . ``` 可选的 pre-commit hooks(一次性设置): ``` pip install pre-commit pre-commit install ``` 安装后,每次 `git commit` 都会在推送时运行 ruff + 快速测试子集。 对于测试真实 LLM 的动态场景,请安装可选的 Anthropic agent 并设置 API key: ``` pip install "mcp-witness[anthropic]" export ANTHROPIC_API_KEY=sk-ant-... mcp-witness-test scenarios/MCP-D-001-tool-desc-injection-fetch.yaml \ --server-cmd python --server-arg=-m --server-arg=mcp_server_fetch \ --agent anthropic ``` ## 方法学和威胁模型 攻击面通过 MCP primitive(tools、resources、prompts、sampling、roots)以及传输和跨方面的 agent 级别攻击进行枚举。每个检测规则和每个场景都带有一个映射回此分类法的 `category` 字段。类别以及标签/角色词汇表位于: - [docs/static-rules.md](docs/static-rules.md) — 检测规则和词汇决策 - [docs/scenario-schema.md](docs/scenario-schema.md) — 动态场景 YAML schema 和步骤类型 - [docs/capability-classifier.md](docs/capability-classifier.md) — 能力标签和参数角色词汇表,校准计划 - [docs/detector-evolution-s014.md](docs/detector-evolution-s014.md) — 关于 MCP-S-014 如何在 W1–W4 补丁系列中从五分之一变为四分之三的具体示例;静态规则演进的方法学说明 - [docs/methodology-soft-escalation.md](docs/methodology-soft-escalation.md) — LinkedIn 逃生阀模式:在 30 天电子邮件沉默后,一句礼貌的 "if this is unmaintained, that's fine, just let me know" 是如何引出在第 +30 天获得已验证维护者的回复的 ## 负责任的披露 针对第三方服务器的发现遵循协调披露:维护者在公开发布前有 90 天的时间(从通知之日起计算),如果修复处于积极开发中,则可延长。使用 mcp-witness 的报告者应遵循相同的做法。[disclosures/](disclosures/) 目录记录了每一项外发的协调披露沟通,包括渠道决策审计跟踪 — 当公开 issue 成为最后选择的渠道时([ARadRareness/mcp-registry#3](https://github.com/ARadRareness/mcp-registry/issues/3)),当准入自动化进行推诿时(HackerOne 分类 interstitial + `disclosure@anthropic.com` 自动回复,两者均作为实质性的披露结果记录在案),当带有礼貌逃生阀的 LinkedIn DM 在 30 天电子邮件沉默后引出了未维护确认时。 披露记录是该项目的重要承载产出物。有关完整的状态表和方法学说明,请参见 [disclosures/README.md](disclosures/README.md)。 政策 + 联系方式:[SECURITY.md](SECURITY.md)。 ## 贡献 欢迎参与 Issue 讨论以及规则集/场景提案。目前最具影响力的贡献领域: 1. **标记更多校准目标。** 语料库需要更多标记的服务器,以便分类器能够超越当前的 `stable` 阈值。工作流程是一键式的(`capture` → `scaffold-gt` → 手动标记 → `eval-calibration`)。参见 [calibration/README.md](calibration/README.md)。 2. **提议新的分析器规则**,请遵循 [docs/static-rules.md](docs/static-rules.md) §Rule lifecycle。真实世界的证据(来自捕获的服务器)是纳入的门槛。 3. **提议新的动态场景**,请遵循 [docs/scenario-schema.md](docs/scenario-schema.md)。同样的证据标准。 ## 许可证 Apache 2.0 — 参见 [LICENSE](LICENSE)。
标签:CISA项目, GraphQL安全矩阵, MCP, Python, 云安全监控, 安全扫描器, 无后门, 漏洞披露, 逆向工具, 静态分析