akar5h/jakk

GitHub: akar5h/jakk

jakk 是一款不依赖 LLM 的 MCP 服务器黑盒安全扫描器,通过确定性探测在 CI 中快速发现命令注入、路径遍历、授权缺陷等漏洞。

Stars: 2 | Forks: 1

# jakk **一款针对 MCP 服务器的黑盒安全扫描器。** 将其指向一个 MCP endpoint;它将枚举服务器的工具,触发一系列精心挑选的 单次调用对抗性探测,并对返回的结果进行分类。无需在循环中使用 LLM —— 具有确定性、速度快、零 token 成本。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/akar5h/jakk/actions/workflows/ci.yml) [![Action 冒烟测试](https://static.pigsec.cn/wp-content/uploads/repos/cas/a3/a3c8f0cc19f185401b03b0e8237c7f9c4fd08b54a960f96f0d74ab7a1573b871.svg)](https://github.com/akar5h/jakk/actions/workflows/action-smoke.yml) [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) ![Python](https://img.shields.io/badge/python-3.10%2B-blue) ![Probes](https://img.shields.io/badge/probes-13-orange) ![Tests](https://img.shields.io/badge/tests-190-green) [Model Context Protocol](https://modelcontextprotocol.io) 允许 LLM 代理调用外部服务器上的工具。这些服务器是一种全新的、快速增长的攻击面 —— 包括命令注入、路径遍历、 授权失效、SSRF、工具描述投毒。`jakk` 是 MCP 服务器维护者的快速第一道防线:只需添加一个 GitHub Action 步骤, 扫描你刚构建的服务器,并且仅在出现可重现的证据时才让 CI 失败。 ``` $ jakk mcp scan --endpoint http://127.0.0.1:8008/mcp/stream --library library/mcp ──────────────── jakk scan :: http://127.0.0.1:8008/mcp/stream ──────────────── Probe results (13) ┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓ ┃ outcome ┃ severity ┃ test id ┃ tool ┃ evidence ┃ ┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩ │ vulnerable │ critical │ mcp.command.secret_… │ init_bare_repo… │ FLAG{git_co… │ │ vulnerable │ high │ mcp.command.shell_ma… │ init_bare_repo… │ …xJAKK-MARK… │ │ pass │ high │ mcp.schema.descripti… │ - │ │ │ ... │ │ │ │ │ └────────────┴──────────┴────────────────────────┴─────────────────┴──────────────┘ Tests run: 13 pass=4 skipped=5 vulnerable=4 4 vulnerability findings ``` ## GitHub Action 快速入门 `jakk` 作为 CI 冒烟测试最为安全。该 Action 默认设置为 `safe: true`, 因此除非你明确选择进行更深入的测试,否则它只会运行只读 / 无副作用的探测。 本仓库在 [`.github/workflows/action-smoke.yml`](.github/workflows/action-smoke.yml) 中自测了该 Action: CI 针对一个微型的 stdio MCP 服务器运行 `uses: ./`,验证是否写入了 JSONL, 并断言在干净的测试用例上没有触发任何发现结果。 对于已知的外部实现,请手动运行 [`真实目标冒烟测试`](.github/workflows/real-target-smoke.yml)。 它将以只读 HTTP 模式启动 GitHub 官方的 `github-mcp-server` Docker 镜像,使用本地 Action 对其进行扫描, 写入 JSONL + SARIF,使用 `github/codeql-action/upload-sarif@v3` 上传 SARIF,并保存发现结果的 artifact。 该工作流是真实目标 SARIF 摄取的冒烟测试。 公开的验证点: - **GitHub 原生摄取:** PR #4 针对官方的 `github-mcp-server` 运行了 `真实目标冒烟测试`; `upload-sarif` 验证了该 SARIF,将其上传,并且 GitHub 报告处理完成。 - **针对易受攻击目标的非空 SARIF:** 针对 `examples/external_targets/ch01-extended` 的本地运行 触发了 `mcp.authz.cross_tenant_read` 并被标记为 `critical`,生成了包含 `1` 条规则和 `1` 条结果的 SARIF 2.1.0。 这证明了相同的输出路径能够提供可操作的发现结果,而不仅仅是空的扫描元数据。 ``` name: MCP security smoke test on: pull_request: push: branches: [main] permissions: contents: read security-events: write jobs: jakk: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Start your MCP server however your project normally does it. - name: Start MCP server run: | ./scripts/start-mcp-server.sh & echo $! > /tmp/mcp-server.pid - name: Wait for MCP endpoint run: | for i in {1..30}; do curl -fsS http://127.0.0.1:8000/health && exit 0 sleep 1 done exit 1 - name: Run jakk uses: akar5h/jakk@v0.2 with: endpoint: http://127.0.0.1:8000/mcp args: "--bearer ${{ secrets.MCP_TEST_TOKEN }}" sarif: jakk-findings.sarif - name: Upload SARIF if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: jakk-findings.sarif - name: Upload findings if: always() uses: actions/upload-artifact@v4 with: name: jakk-findings path: jakk-findings.jsonl ``` 对于允许发生数据变更的已授权测试目标,可以选择启用 完整的库: ``` - uses: akar5h/jakk@v0.2 with: endpoint: http://127.0.0.1:8000/mcp safe: "false" args: "--bearer ${{ secrets.MCP_TEST_TOKEN }} --arg owner=octocat --arg repo=Hello-World" ``` ## 安装 ``` pip install jakk # 或者,从源码: git clone https://github.com/akar5h/jakk && cd jakk && pip install -e . ``` ## 快速开始 ``` # 使用只读 probe 扫描本地 MCP endpoint jakk mcp scan --endpoint http://127.0.0.1:8008/mcp/stream --safe # 一个经过身份验证的服务器,仅限安全(只读) probe,结果输出至 JSONL jakk mcp scan \ --endpoint https://api.example.com/mcp/stream \ --bearer "$ACCESS_TOKEN" \ --safe \ --jsonl findings.jsonl \ --sarif findings.sarif ``` ## 探测目录 涵盖 7 个攻击面的 13 个探测,覆盖了 OWASP-for-MCP 类别 MCP01–MCP05、MCP08 和 MCP10。 | 探测 | 类别 | 严重性 | |---|---|---| | `mcp.command.shell_marker` | 命令注入 (sink) | high | | `mcp.command.secret_file_read` | 命令注入 (影响) | critical | | `mcp.prefix_bypass` | CVE-2025-53110 startswith 绕过 | high | | `mcp.path.canary_file_read` | 路径遍历 (影响) | critical | | `mcp.response.secret_overshare` | 正常响应中的机密泄漏 | high | | `mcp.response.directive_passthrough` | 通过响应进行的间接注入 | high | | `mcp.schema.description_smuggling` | 通过描述进行的工具投毒 | high | | `mcp.auth.no_credential` | 接受匿名访问 | critical | | `mcp.auth.invalid_token` | 接受无效 token | critical | | `mcp.auth.wrong_prefix` | 在没有 scheme 的情况下接受 bearer | low | | `mcp.authz.cross_tenant_read` | 迷惑代理 / BOLA | critical | | `mcp.ssrf.cloud_metadata` | 针对云元数据 endpoint 的 SSRF | critical | | `mcp.sql.error_based` | 通过 query 型参数进行的 SQL 注入 | high | [`docs/tests/`](docs/tests/) 中有针对每个探测的详细说明。对于攻击者而言,每个 `vulnerable` 判定结果的具体含义详见:[`docs/threat-models.md`](docs/threat-models.md)。 ## 结果 每个探测都会产生六种结果中的一种 —— 只有 `vulnerable` 需要进行分类筛查: | 结果 | 含义 | |---|---| | **vulnerable** | 响应表明服务器偏离了其应保持的安全属性。 | | **echo** | 输入被反射但未被解析 —— 其本身不可被利用。 | | **suggestive** | 不同调用之间的验证结果不一致;重新运行以明确判断。 | | **pass** | 探测已运行,响应干净(包括服务器安全地拒绝了该输入)。 | | **skipped** | 没有兼容的工具,或缺少配置(`--arg`、`--bearer`)。这不算失败。 | | **error** | 无法完成调用(传输失败)。 | `jakk` 能够区分*"服务器拒绝了我们的恶意输入"* (pass) 和*"我们无法进行测试"* (error),以及*"发生了 shell 展开"* (vulnerable) 和*"输入被原样回显"* (echo)。一个误报频发的扫描器比没有扫描器更糟糕 —— 每一次 `vulnerable` 都应是真实的。 ## 它有何不同 - **无需 LLM。** 匹配器是确定性的(正则表达式 / canary 回显 / schema 扫描)。零 token 成本,完全可复现。 - **GitHub 原生。** 该 Action 默认安全,输出 JSONL + SARIF,并且可以通过 `--exit-nonzero-on-fired` 在出现 `vulnerable` 发现结果时阻止 PR 合并。 - **Schema 感知,与供应商无关。** 探测通过*语义角色*(`path`、`url`、`query` 等)来定位参数,因此无论服务器如何命名其参数,同一个库可以适用于各种服务器。([详情](docs/context-args/README.md)) - **诚实的分类。** 6 种结果的分类法,将真实的发现结果与输入反射以及“无法测试”的情况区分开来。 - **它自己也在使用自身。** `jakk` 自身的攻击面已经过审计 —— 参见 [`docs/2026-05-23_self-security-audit.md`](docs/2026-05-23_self-security-audit.md)。 `jakk` 是 MCP 安全测试的*服务器端*部分。而 *代理端*部分(多轮次、针对使用 MCP 的代理进行 LLM 自适应攻击)则是另一个不同的问题,需要使用单独的工具。 ## 在故意设计的易受攻击的实验环境中尝试 ``` # 获取 breach-to-fix 实验靶标(含 vulnerable 和 hardened 变体) ./examples/external_targets/fetch.sh docker compose -f examples/external_targets/_vendor/mcp-breach-to-fix-labs/docker-compose.yml \ up -d git-command-injection-vulnerable git-command-injection-secure jakk mcp scan --endpoint http://127.0.0.1:8008/mcp/stream --library library/mcp # fires jakk mcp scan --endpoint http://127.0.0.1:9008/mcp/stream --library library/mcp # clean ``` 为了得到简洁的阳性 SARIF 证据,请运行本地的 ch01 authz 实验: ``` CHALLENGE_HOST=127.0.0.1 CHALLENGE_PORT=18011 \ CH01_EXT_DATA_PATH=/path/to/projects.json \ python examples/external_targets/ch01-extended/server.py jakk mcp scan \ --endpoint http://127.0.0.1:18011/mcp/stream \ --library library/mcp \ --select mcp.authz.cross_tenant_read \ --cred-a alpha-api-key \ --cred-b bravo-api-key \ --foreign-id CRM-1001 \ --jsonl ch01-extended.jsonl \ --sarif ch01-extended.sarif ``` 预期数据点:`vulnerable=1`,SARIF `results.length == 1`,规则 `mcp.authz.cross_tenant_read`。 目标列表请参见 [`examples/external_targets/`](examples/external_targets/),关于 ch01 / ch02 / ch08 的实时结果 请参见 [`docs/2026-05-22_smoke-report.md`](docs/2026-05-22_smoke-report.md)。 ## 负责任地使用 `jakk` 是一款进攻性安全工具。仅在你拥有或获得明确授权进行测试的系统(你自己的基础设施、漏洞赏金计划的明确范围或付费的测试委托)上运行它。 [深度暴露方法](docs/depth-of-exposure-methodology.md) 记录了在接触任何你不拥有的目标之前应进行的授权预检。报告安全问题请遵循 [SECURITY.md](SECURITY.md)。 ## License [Apache 2.0](LICENSE)。
标签:DOE合作, MCP服务器, Python, XXE攻击, 图数据库, 无后门, 请求拦截, 逆向工具, 黑盒测试