Skip to content

MCP---Agent-Starter-Kit Arbitrary File Read via MCP read_doc #64

Description

@wing3e

MCP---Agent-Starter-Kit Arbitrary File Read via MCP read_doc

1) CNA / Submission Type

  • Submission type: Report a vulnerability (CVE ID request)
  • Reporter role: Independent security researcher
  • Report date: April 13, 2026

2) Reporter Contact

  • Reporter name: Winegee
  • Reporter email: Winegee@zju.edu.cn
  • Permission to share contact with vendor: Yes

3) Vendor / Product Identification

  • Vendor: fub05
  • Product: MCP---Agent-Starter-Kit
  • Repository: https://github.com/fub05/MCP---Agent-Starter-Kit.git
  • Reviewed local source path: python-batch-04/datasets/fub05-MCP---Agent-Starter-Kit
  • Exposed interface: MCP tool read_doc (and parallel HTTP helper logic in /tool/get/{name})
  • Affected component(s):
  • mcp-docs-server/app/mcp_adapter.py
  • mcp-docs-server/app/server.py

4) Vulnerability Type

  • CWE: CWE-73 (External Control of File Name or Path)
  • Short title: the MCP docs adapter reads arbitrary local files because name is joined directly with the docs base path

5) Affected Versions

  • Confirmed affected: current scanned revision in the local dataset
  • Suspected affected range: revisions containing the same self.base_docs / name logic
  • Fixed version: Not available at time of report (April 12, 2026)

6) Vulnerability Description

The starter kit includes mcp-docs-server, which is supposed to expose only files from its local docs/ directory. In the optional MCP integration, the read_doc tool receives a caller-controlled name field and resolves it as self.base_docs / name without canonicalization or root-boundary checks.

For absolute paths, Path(base_docs) / "/etc/passwd" resolves to /etc/passwd directly. For relative traversal strings, the resulting path can also escape the docs directory. The HTTP helper route in server.py uses the same unchecked pattern, but the clearest externally reachable primitive is the MCP read_doc tool because it accepts free-form strings instead of a single path segment.

7) Technical Root Cause

  1. MCP registration
    • mcp-docs-server/app/mcp_adapter.py:25-33
    • read_doc reads name = params.get("name") and resolves p = (self.base_docs / name).
  2. Unchecked file read
    • mcp-docs-server/app/mcp_adapter.py:31-33
    • If the resolved path exists, the server returns p.read_text(encoding="utf-8").
  3. Parallel unchecked HTTP path construction
    • mcp-docs-server/app/server.py:27-32
    • target = BASE / name followed by FileResponse(str(target)).
  4. Audit conclusion
    • The advertised docs-only trust boundary is broken for MCP callers because name is treated as a filesystem path rather than a document identifier.

8) Attack Prerequisites

  • fastmcp installed and the MCP integration enabled.
  • Ability to invoke the read_doc MCP tool.
  • Read permissions for the target local file under the server's OS account.

9) Proof of Concept / Reproduction Guidance

  1. Reproduction request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_doc",
    "arguments": {
      "name": "/etc/passwd"
    }
  }
}
  1. Why this works
  • In pathlib, self.base_docs / "/etc/passwd" resolves to /etc/passwd.
  • The tool checks only exists() and then returns the file contents.
  1. Expected observable result
  • The tool responds with the contents of /etc/passwd (or another attacker-selected readable file), not a document from the local docs/ directory.
  • This PoC avoids HTTP path-segment encoding edge cases by targeting the MCP tool directly, which accepts a free-form string.

10) Security Impact

  • Confidentiality: High, because arbitrary local files can be disclosed to the MCP caller.
  • Integrity: Low, because the confirmed primitive is read-only.
  • Availability: Low, aside from incidental load or disclosure of large files.
  • Scope: Unchanged.

11) CVSS v3.1 Suggestion

  • Suggested vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L
  • Suggested base score: 8.6 (High) when the MCP server is exposed to untrusted clients.

12) Workarounds / Mitigations

  • Disable the MCP docs adapter until path validation is added.
  • Run the server only in trusted local environments.
  • Restrict the docs server process to a tightly sandboxed filesystem view if possible.

13) Recommended Fix

  • Normalize and resolve the requested path, then reject anything outside base_docs.
  • Prefer document IDs or basename-only selection instead of raw path strings.
  • Add regression tests for absolute paths and ../ traversal through the MCP interface.

14) References

15) Credits

  • Discoverer: Winegee
  • Discovery method: Static analysis (CodeQL) plus local source-code audit

16) Additional Notes

  • The README states the service should expose a local docs folder as a retrieval tool. The confirmed issue is specifically that this boundary is not enforced for MCP callers.
  • The HTTP helper path was not needed for confirmation because the MCP adapter alone provides a complete arbitrary-read primitive.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions