joshuaruppe/mcpdx

GitHub: joshuaruppe/mcpdx

mcpdx 是一个零依赖的 MCP 服务器安全评估工具包,支持被动静态审计、主动模糊测试与能力漂移检测,可输出多种格式的报告。

Stars: 1 | Forks: 0

mcpdx # mcpdx **针对 MCP 服务器的安全检查** ![Python](https://img.shields.io/badge/python-3.8+-blue?logo=python&logoColor=white) ![Dependencies](https://img.shields.io/badge/dependencies-zero-brightgreen) ![MCP](https://img.shields.io/badge/MCP-2025--11--25-8A2BE2) ![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey) ![License](https://img.shields.io/badge/license-MIT-green) ![Use](https://img.shields.io/badge/use-authorized_testing_only-red)
`mcpdx` 是一个零依赖的工具包,用于对 [Model Context Protocol](https://modelcontextprotocol.io) 服务器进行**已授权**的安全评估。它通过 **stdio** 或 **Streamable HTTP** 建立连接,枚举暴露的攻击面(工具、资源、提示词),执行**被动静态审计**,并且仅在您明确选择加入时,**主动模糊测试**工具输入。检查结果可以输出到控制台,或导出为 JSON、Markdown 或 SARIF 格式,并附带对 CI 友好的退出代码。 ## 目录 - [mcpdx](#mcpdx) - [目录](#contents) - [功能](#features) - [安装](#install) - [快速开始](#quick-start) - [子命令](#subcommands) - [标志](#flags) - [示例](#examples) - [检测项](#detections) - [被动检测(静态,不调用任何工具)](#passive-static-no-tool-is-invoked) - [主动检测(仅在指定 `--active` / `fuzz` 时调用工具)](#active-only-with---active--fuzz-invokes-tools) - [访问控制与会话探测(HTTP,只读,由 `audit`/`scan` 自动运行)](#access-control--session-probes-http-read-only-run-automatically-by-auditscan) - [能力漂移 / 恶意替换检测(`audit`/`scan`,只读)](#capability-drift--rug-pull-detection-auditscan-read-only) - [退出代码](#exit-codes) - [项目结构](#project-structure) - [License](#license) ## 功能 - 🔍 **枚举** 完整的声明面(工具、资源、资源模板和提示词),支持通过 stdio 或 Streamable HTTP。 - 🛡️ **被动静态审计**:检测工具/提示词投毒、隐藏的 Unicode、工具名称冲突、敏感能力与输入验证缺失、危险的 `icons[]` 来源以及已知 CVE 标记。绝不调用任何工具。 - 💥 **主动模糊测试** *(可选启用)*:测试路径遍历、命令/SQL/模板注入、SSRF、NoSQL 和原型链污染,以及对工具响应的实时输出投毒和机密泄露扫描。 - 🔄 **漂移 / 恶意替换检测**:对攻击面进行快照并在稍后进行差异对比,或者在会话内重新枚举,以捕获使用后的定义替换行为。 - 🌐 **传输和访问控制探测**(HTTP):认证边界差异测试、伪造会话接受检测、TLS 状态/降级测试以及速率限制检测。 - 📄 **报告**:支持彩色的控制台输出,以及 JSON、Markdown 和 SARIF 2.1.0(GitHub Code Scanning)格式导出。 - 🧰 **零依赖**:仅使用 Python 3.8+ 的标准库。直接将其放入测试设备中即可运行。 ## 安装 ``` git clone mcpdx && cd mcpdx python mcpdx.py --help # nothing to install; stdlib only ``` ## 快速开始 `mcpdx` 通过两种方式连接服务器:通过 **stdio** 启动本地服务器(`--stdio`),或通过 **HTTP(S)** 连接到远程服务器(`--http`)。每个命令都适用于这两种传输方式。 ``` # 对本地 stdio 服务器的被动审计(只读) python mcpdx.py audit --stdio "python my_server.py" # ...或者通过 HTTP(S) 连接的远程服务器,附带 auth header python mcpdx.py audit --http https://mcp.example.com/mcp -H "Authorization: Bearer $TOKEN" # 完整评估,包括主动 fuzzing,生成 JSON + Markdown 报告(远程或本地) python mcpdx.py scan --http https://mcp.example.com/mcp --active --json out.json --md out.md # 针对内置的 vulnerable fixture 进行尝试 python mcpdx.py scan --stdio "python examples/vulnerable_server.py" --active --yes ``` ## 子命令 | 命令 | 功能描述 | 是否触碰目标? | |---------|--------------|---------------------| | `enum` | 连接并列出工具、资源、模板、提示词 | 只读握手 | | `snapshot` | 捕获声明面的清单,以便后续进行漂移比较 | 只读 | | `audit` | `enum` + **被动**静态安全审计(+ 漂移检测、HTTP 探测) | 只读 | | `scan` | `audit` + **可选的**主动模糊测试(`--active`) | 只读,或带标志时主动 | | `fuzz` | 仅对工具输入进行**主动**模糊测试 | **是,会调用工具** | | `call` | 手动调用一个工具 / 读取资源 / 获取提示词 | 是(调用一次) | | `report` | 将已保存的扫描结果(通过 `--name` 或路径)**离线**重新渲染为 SARIF / Markdown;使用 `--list` 列出已保存的扫描 | 否(仅读取文件) | ## 标志
传输(仅选择一项) | 标志 | 含义 | |------|---------| | `--stdio "CMD …"` | 启动本地 MCP 服务器;将完整命令行作为单个字符串传入 | | `--http URL` | 连接到 Streamable HTTP MCP endpoint | | `-H, --header "K: V"` | 添加 HTTP header(可重复),例如 `-H "Authorization: Bearer …"` | | `-e, --env "K=V"` | 为启动的 stdio 服务器设置环境变量(可重复) | | `--cwd DIR` | stdio 服务器的工作目录 | | `--insecure` | 禁用 TLS 证书验证(HTTP) | | `--timeout SEC` | 单次请求超时时间(默认 30) |
输出 / 详细级别 | 标志 | 含义 | |------|---------| | `-v` | 调试日志记录(协议级别的通信内容) | | `-vv` | **Trace**:双向转储原始 JSON-RPC 数据帧 | | `-q, --quiet` | 抑制日志 chatter(仍会打印结果) | | `--no-color` | 禁用 ANSI 颜色 | | `--no-banner` | 不打印 ASCII 徽标 | | `--json FILE` | 写入机器可读的 JSON 报告 | | `--md FILE` | 写入 Markdown 报告 | | `--sarif FILE` | 写入 SARIF 2.1.0 报告(适用于 GitHub Code Scanning) | | `--name NAME` | 将扫描结果保存到本地存储(`.mcpdx/scans/`),以便后续 `report NAME` 命令能重新渲染 |
主动测试标志(`scan` / `fuzz`) | 标志 | 含义 | |------|---------| | `--active` | (`scan`)同时运行主动模糊测试阶段 | | `--yes` | 跳过交互式授权提示(适用于自动化/CI) | | `--max-payloads N` | 限制每个参数族和每个参数的 payload 数量 | | `--no-robustness` | (`fuzz`)跳过超长 / 类型混淆探测 |
漂移 / 恶意替换标志(`audit` / `scan`) | 标志 | 含义 | |------|---------| | `--baseline FILE` | 将当前攻击面与已保存的 `snapshot` 清单进行对比,并报告能力漂移 | | `--watch N` | 在会话内额外重新枚举 N 次,并报告漂移情况(捕获会话中途 / 使用后的替换) | | `--watch-interval SEC` | `--watch` 重新枚举之间的间隔秒数(默认为 2) | | `--out FILE` | (`snapshot`)清单输出路径(默认为 `mcpdx-manifest.json`) |
## 示例 ``` # 枚举本地 stdio 服务器 python mcpdx.py enum --stdio "npx -y @modelcontextprotocol/server-filesystem ./data" # 对远程 HTTP 服务器进行被动审计,详细模式,导出 Markdown python mcpdx.py audit --http https://mcp.example.com/mcp -v --md report.md \ -H "Authorization: Bearer $TOKEN" # 包含主动 fuzzing 的完整评估,生成 JSON + Markdown 报告 python mcpdx.py scan --stdio "python my_server.py" --active --json out.json --md out.md # 执行审计并输出 SARIF,以上传至 GitHub Code Scanning python mcpdx.py audit --stdio "python my_server.py" --sarif mcpdx.sarif # 为扫描命名,随后按名称重新渲染(离线,无需重新扫描) python mcpdx.py scan --stdio "python my_server.py" --name prod-api python mcpdx.py report prod-api --sarif mcpdx.sarif --md report.md python mcpdx.py report --list # show saved scans # (也可以使用已保存的 JSON 报告路径来代替名称) python mcpdx.py report report.json --sarif mcpdx.sarif # 追踪每个 JSON-RPC frame python mcpdx.py audit --stdio "python my_server.py" -vv # 手动调用单个 tool python mcpdx.py call --stdio "python my_server.py" \ --tool read_file --args '{"path":"README.md"}' # 立即创建快照,然后在重新测试时进行 diff,以检测 capability drift / rug pulls python mcpdx.py snapshot --stdio "python my_server.py" --out baseline.json python mcpdx.py audit --stdio "python my_server.py" --baseline baseline.json # 监控服务器是否存在使用后定义替换 python mcpdx.py audit --stdio "python my_server.py" --watch 3 --watch-interval 5 ``` 在 [`examples/`](examples/) 目录中提供了一个故意设计为存在漏洞的演示服务器,这样您无需将工具指向真实目标即可查看检测结果: ``` # stdio fixture:投毒、冲突、命令注入、输出投毒等 python mcpdx.py scan --stdio "python examples/vulnerable_server.py" --active --yes # HTTP fixture:CVE 标记、auth-boundary、forged-session 检测结果 python examples/insecure_http_server.py 8765 & python mcpdx.py audit --http http://127.0.0.1:8765/mcp -H "Authorization: Bearer x" ``` ## 检测项 ### 被动检测(静态,不调用任何工具) - 工具/提示词**投毒**:公开文本中包含注入式的表达方式。除了顶层描述外,它还会扫描工具的 **`title`** 和每一个**参数 `description`**(递归扫描,包含嵌套 schema),这是隐藏容易被审查者忽略的指令的常见位置,此外还会检测伪造的聊天模板 / 角色分隔符(`<|im_start|>`、`<>`、`### system` 等)。 - **隐藏/不可见的 Unicode**(零宽字符、RTL 覆盖、Unicode 标签块),用于将指令瞒过人工审查。 - **工具名称冲突 / 遮蔽**:重复或外观相似的标识符(调用路径混淆 / namespace 抢注)。 - 公开描述中的 **Markdown 链接 / URL** 和**强制批准措辞**(例如“始终允许”、“不要询问”)。 - **敏感能力**暴露(代码执行、文件系统、网络、数据库、机密信息、消息传递),通过名称/描述推断。 - **输入验证**缺失:不受限制的自由文本参数直接传递给敏感的 sink。 - **破坏性**工具(通过 `annotations.destructiveHint` 检测)。 - 看起来容易受到路径遍历攻击的**模板化资源**。 - **传输层**弱点:明文 HTTP、未授权的枚举。 - **危险的图标来源**:`icons[]` 元数据(MCP 2025-11-25)携带 SVG `data:`/`file:` URI(可能包含脚本)或源外的 `http(s)` 来源(当客户端 UI 渲染它们时,可能会成为数据外发/追踪信标)。 - 根据 `serverInfo` 进行**已知 CVE / 易受攻击版本**标记(例如 MCP-Inspector < 0.14.1,CVE-2025-49596)。 - 服务器身份 / 能力侦察。 ### 主动检测(仅在指定 `--active` / `fuzz` 时调用工具) - **路径遍历**(`../../etc/passwd`、`etc/shadow`、编码格式、SMB/UNC、`file://`)。 - **命令注入**(带有无害算术/回显测试信号的 shell 元字符)。 - **SQL 注入**(基于错误)和 **SSTI**(`{{7*7}}`/`${7*7}` 解析为 `49`)。 - **SSRF**(云元数据、回环地址、`file://`、自定义 URI scheme)。 - **NoSQL / 原型链污染**:将操作符对象(`{"$ne":null}`、`__proto__`)注入对象类型的参数中(尽力而为,基于错误检测;需手动确认)。 - **输出投毒**:扫描实时工具*输出*中是否包含注入的指令和隐藏的 Unicode(将每个输出视为下一个 agent 的不受信任输入)。 - **机密信息暴露**:工具输出中泄露的凭据,包括**AI 提供商密钥**(Anthropic `sk-ant-`、OpenAI 项目/旧版密钥)、AWS/GCP/Slack/GitHub(经典版 + 细粒度)、GitLab、HuggingFace、npm 和 Telegram token、JWT 以及私钥。 - **输出 schema 不匹配**:声明的 `outputSchema` 与实际返回字段不一致。 - **健壮性 / 信息泄露**(超长输入、导致泄露堆栈跟踪和内部路径的类型混淆)。 ### 访问控制与会话探测(HTTP,只读,由 `audit`/`scan` 自动运行) - **认证边界差异测试**:如果提供了凭据,会在剥离凭据的情况下重新测试枚举操作,标记出在没有身份验证的情况下依然可以被枚举的服务器。 - **会话验证**:在未进行握手的情况下发送一个从未颁发过的 `Mcp-Session-Id`,标记出接受伪造/无绑定会话的服务器。 - **TLS 状态**:检查协商的 TLS 版本/加密套件(标记 1.0/1.1 版本和弱加密算法),并测试 `https` endpoint 是否会降级为明文 `http://`。 - **速率限制**(主动阶段):短暂的并发突发请求会标记出在创建会话/工具时没有进行限流的服务器(提示词风暴 / 资源耗尽风险)。 ### 能力漂移 / 恶意替换检测(`audit`/`scan`,只读) - **`snapshot` + `--baseline`**:将每个工具/提示词/资源定义进行哈希处理生成清单;后续运行时会与其进行对比,并标记出新增 / 删除 / 的能力。被修改的文本会重新经过投毒 + 隐藏 Unicode 扫描器的检测,因此如果某个描述*突变为*注入 payload,则会被标记为 HIGH。 - **`--watch N`**:在会话内重新枚举,以捕获服务器在正常使用几次后将良性定义替换为恶意定义的行为(即已记录在案的 WhatsApp 模式)。 - **使用后检查**:`scan --active` 会在模糊测试后自动重新枚举,并与测试前的攻击面进行差异对比。 这些检测项对应了 NSA CSI *“Model Context Protocol (MCP): Security Design Considerations for AI-Driven Automation”*(U/OO/6030316-26,2026 年 5 月)中提到的安全问题:访问控制、工具投毒、参数注入/ACE、输出投毒、工具名称冲突、token/session 安全、传输安全、能力漂移 / 恶意替换以及已知漏洞追踪。 ## 退出代码 `mcpdx` 对 CI 友好。进程退出代码反映了最严重的检测结果: | 代码 | 含义 | |:----:|---------| | `0` | 干净,无任何发现 | | `1` | 中等程度的发现 | | `3` | 高 / 严重级别的发现 | | `2` | 授权被拒绝 | | `4` | 连接 / 协议错误 | ## 项目结构 ``` mcpdx/ ├── mcpdx.py CLI entry point (launch options, subcommands, banner) ├── requirements.txt (empty) zero runtime deps; stdlib only ├── mcpcore/ │ ├── log.py verbosity-aware colour logger (-v / -vv) │ ├── transport.py stdio + Streamable HTTP JSON-RPC transports │ ├── client.py MCP client (handshake, enumeration, invocation) │ ├── payloads.py detection signatures + active payloads │ ├── audit.py passive static auditor (poisoning, collision, CVE, …) │ ├── fuzz.py active fuzzer (+ live output-poisoning scan) │ ├── probes.py HTTP auth-boundary + session-validation probes │ ├── netprobe.py TLS posture / downgrade + rate-limit probes │ ├── drift.py capability-drift / rug-pull detection │ ├── report.py Finding model + console / JSON / Markdown renderers │ └── sarif.py SARIF 2.1.0 renderer (offline; GitHub Code Scanning) └── examples/ ├── vulnerable_server.py stdio test fixture └── insecure_http_server.py HTTP test fixture (CVE / auth / session) ``` ## License 采用 [MIT License](LICENSE) 发布。© 2026 Joshua Ruppe。
标签:MCP, Python, 主机安全, 反取证, 安全评估, 实时处理, 密码管理, 攻击面枚举, 无后门, 逆向工具