Skip to content

filesystem-mcp-server ALLOWED_PATHS Prefix-Check Bypass via read_file_tool / write_file_tool #65

Description

@wing3e

filesystem-mcp-server ALLOWED_PATHS Prefix-Check Bypass via read_file_tool / write_file_tool

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 (fill before submit)

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

3) Vendor / Product Identification

  • Vendor: geekgod382
  • Product: filesystem-mcp-server
  • Repository: https://github.com/geekgod382/filesystem-mcp-server.git
  • Reviewed local source path: python-batch-04/datasets/geekgod382-filesystem-mcp-server
  • Confirmed affected function: is_path_allowed (reached by read_file_tool, write_file_tool, and the other file-management tools)
  • Reviewed source file: server.py

4) Vulnerability Type

  • CWE: CWE-73 (External Control of File Name or Path)
  • Short title: the ALLOWED_PATHS guard can be bypassed with sibling-prefix paths, exposing file operations outside the intended root

5) Affected Versions

  • Confirmed affected: current scanned revision in the local dataset
  • Suspected affected range: revisions containing the same request-to-sink flow documented below
  • Fixed version: Not available at time of report (April 10, 2026)

6) Vulnerability Description

The repository README says access is limited to configured ALLOWED_PATHS, with the default root set to the operator's home directory. In server.py, that policy is implemented by is_path_allowed(path), which computes os.path.abspath(path) and then checks abs_path.startswith(allowed) for each configured root.
That string-prefix check is bypassable. If /home/alice is allowlisted, a sibling path such as /home/alice_backup/loot.txt still starts with /home/alice and is therefore treated as allowed even though it is outside the intended root.
Because the same helper guards read_file_tool, write_file_tool, delete, move, copy, and directory-listing operations, an untrusted MCP caller can use prefix-sharing paths to read, write, or delete files outside the configured ALLOWED_PATHS boundary.

7) Technical Root Cause

  1. Documented trust boundary
    • The README says access should stay inside configured ALLOWED_PATHS and specifically claims "No access to system paths."
    • Relevant docs: README.md:29-37
  2. Vulnerable allowlist check
    • server.py:29-32 implements the guard as abs_path = os.path.abspath(path) followed by abs_path.startswith(allowed).
    • This compares raw string prefixes instead of canonical path ancestry.
  3. Reachable sinks after the bypass
    • read_file_tool uses the attacker-controlled path in open(path, 'r', ...) at server.py:278.
    • write_file_tool creates directories and writes to the attacker-controlled path at server.py:313-315.
    • Additional tools reuse the same guard before delete, copy, move, list, and stat operations elsewhere in server.py.
  4. Audit conclusion
    • The real issue is allowlist-bypass by prefix confusion, not complete absence of a path check.

8) Attack Prerequisites

  • The attacker can invoke any exposed file-management MCP tool that relies on is_path_allowed().
  • The server accepts attacker-controlled path without a stricter validation layer in front of the confirmed sink.
  • The executing process has the network or filesystem privileges needed to make the sink security-relevant.

9) Proof of Concept / Reproduction Guidance

The most direct reproduction is to choose a path that shares the allowlisted prefix but is not actually inside the allowlisted directory.

  1. Example environment assumption
  • Assume the operator leaves the default policy in place and ALLOWED_PATHS = ["/home/alice"].
  • Prepare a sibling directory such as /home/alice_backup/.
  1. Reproduction request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_file_tool",
    "arguments": {
      "path": "/home/alice_backup/codex-poc.txt",
      "content": "prefix bypass proof",
      "encoding": "utf-8"
    }
  }
}
  1. Why this works
  • os.path.abspath("/home/alice_backup/codex-poc.txt") still starts with the string /home/alice, so is_path_allowed() returns true even though the path is outside the real allowlisted directory.
  • The same bug also accepts sibling-prefix paths such as /Users/alice_evil/... on macOS when /Users/alice is allowlisted.
  1. Expected result
  • is_path_allowed("/home/alice_backup/codex-poc.txt") returns true because the absolute path still starts with the string /home/alice.
  • The server reaches the write sinks in server.py:313-315 and creates /home/alice_backup/codex-poc.txt, even though that file is outside the intended allowlisted root.
  • The same prefix-confusion pattern can then be reused against read, delete, copy, move, and directory-listing tools.

10) Security Impact

  • Confidentiality: High when attacker-controlled paths can expose sensitive local files.
  • Integrity: High when attacker-controlled paths can overwrite, create, or delete files.
  • Availability: Medium to High depending on whether critical files or working directories are affected.
  • 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:H/A:H
  • Suggested base score: 9.8 (Critical) when arbitrary file read/write/delete is reachable via the exposed tool.

12) Workarounds / Mitigations

  • Restrict filesystem operations to a fixed allowlisted root directory.
  • Resolve and canonicalize attacker-controlled paths before use, then enforce root-boundary checks.
  • Reject traversal tokens, absolute paths, and symlink escapes where not explicitly required.
  • Restrict sensitive file tools to trusted authenticated callers only.

13) Recommended Fix

  • Add canonicalization plus root-boundary enforcement before each filesystem sink.
  • Replace free-form path inputs with structured identifiers or allowlisted relative paths.
  • Add regression tests covering .., absolute paths, symlink escapes, and mixed-encoding traversal payloads.
  • Document the intended trust boundary for all file-oriented MCP/HTTP tools.

14) References

15) Credits

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

16) Additional Notes for Form Mapping

  • Audit verdict: Confirmed vulnerability.
  • Total reviewed SARIF results for this repository/rule group: 34
  • Dynamic exploit replay status: not completed in this batch update.
  • Version-range accuracy should still be finalized by the maintainer against release history before public disclosure.

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