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

# mcpdx
**针对 MCP 服务器的安全检查**






`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, 主机安全, 反取证, 安全评估, 实时处理, 密码管理, 攻击面枚举, 无后门, 逆向工具