Skip to content

gel-mcp Arbitrary File Read via fetch_rule Path Traversal #66

Description

@wing3e

gel-mcp Arbitrary File Read via fetch_rule Path Traversal

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: geldata
  • Product: gel-mcp
  • Repository: https://github.com/geldata/gel-mcp.git
  • Reviewed local source path: python-batch-04/datasets/geldata-gel-mcp
  • Confirmed affected tool: fetch_rule
  • Reviewed source file: src/gel_mcp/server.py

4) Vulnerability Type

  • CWE: CWE-73 (External Control of File Name or Path)
  • Short title: path traversal in fetch_rule lets callers read files outside the bundled Gel rule set

5) Affected Versions

  • Confirmed affected: 0.1.0
  • 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

gel-mcp exposes two rule-related tools: list_rules(), which enumerates the bundled Markdown rule files under RULES_DIR, and fetch_rule(rule_name), which is supposed to return one of those bundled files by name. The implementation of fetch_rule() joins attacker-controlled rule_name directly under RULES_DIR and then calls exists() / read_text() on the resulting path.

Because the code does not reject path separators, traversal tokens, or absolute paths, a caller can request files outside the bundled rule directory. For example, ../../../../../pyproject.toml resolves from src/gel_mcp/static/gel-ai-rules/src/ back to the repository root and returns the project's pyproject.toml instead of a packaged rule file.

7) Technical Root Cause

  1. Intended trust boundary
    • src/gel_mcp/server.py:18-20
    • RULES_DIR is a fixed directory containing bundled Gel AI rule files.
    • list_rules() restricts visible files to RULES_DIR.glob("*.md"), which establishes the expected "rule name only" input model.
  2. Vulnerable tool entry point
    • src/gel_mcp/server.py:143-152
    • fetch_rule(rule_name: str) accepts an arbitrary string rather than a member of the list_rules() result set.
  3. Unsafe path resolution
    • src/gel_mcp/server.py:149-152
    • rule_path = RULES_DIR / rule_name
    • if not rule_path.exists(): raise FileNotFoundError(...)
    • return rule_path.read_text()
  4. Audit conclusion
    • The server intends to expose only packaged rule documents, but the unchecked join allows traversal to any readable local file reachable from the server process.

8) Attack Prerequisites

  • The attacker can invoke the fetch_rule MCP tool.
  • The target file is readable by the account running gel-mcp.
  • No upstream proxy or wrapper constrains rule_name to the filenames returned by list_rules().

9) Proof of Concept / Reproduction Guidance

  1. Reproduction input
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fetch_rule",
    "arguments": {
      "rule_name": "../../../../../pyproject.toml"
    }
  }
}
  1. Why this works
  • RULES_DIR points to src/gel_mcp/static/gel-ai-rules/src/.
  • Appending ../../../../../pyproject.toml escapes that directory back to the repository root.
  • The server checks only whether the resolved path exists and then returns its contents.
  1. Expected observable result
  • The tool returns the text of pyproject.toml instead of a bundled Gel rule such as gel.md.
  • Any other readable file reachable by traversal from RULES_DIR can be exfiltrated the same way.

10) Security Impact

  • Confidentiality: High. Attackers can read arbitrary local files that the service account can access.
  • Integrity: None directly from the reviewed tool, which only reads files.
  • Availability: Low. Repeated reads could expose operational metadata, but no direct write or delete primitive was confirmed here.
  • 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:N/A:N
  • Suggested base score: 7.5 (High) when untrusted callers can reach the MCP tool.

12) Workarounds / Mitigations

  • Do not expose fetch_rule to untrusted callers until path validation is fixed.
  • Prefer serving only names returned by list_rules() and reject any input containing /, \\, or ...
  • Run the service with minimal filesystem privileges so non-rule files are not broadly readable.

13) Recommended Fix

  • Canonicalize the candidate path with resolve() and verify that it remains under RULES_DIR.
  • Restrict rule_name to a validated basename or allowlisted filename set.
  • Add regression tests for traversal payloads such as ../, mixed separators, and absolute paths.

14) References

15) Credits

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

16) Additional Notes for Form Mapping

  • Audit verdict: Confirmed vulnerability.
  • Total reviewed SARIF results for this repository/rule group: 2
  • Realistic PoC payload verified against the repository layout in the local dataset.
  • 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