Skip to content

Arbitrary Local File Read Vulnerability in @promptx/mcp-office of PromptX #571

@BruceJqs

Description

@BruceJqs

Arbitrary Local File Read Vulnerability in @promptx/mcp-office of PromptX

1) CNA / Submission Type

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

2) Reporter Contact

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

3) Vendor / Product Identification

4) Vulnerability Type

  • CWE: CWE-862 (Improper Authorization)
  • Short title: Arbitrary local file read in MCP tool request handling

5) Affected Versions

  • Confirmed affected: Commit 5c0bd15a53f9152ae370205ef39cd26dc8b11d1a
  • Suspected affected range: revisions containing the same request-to-filesystem path flows listed below
  • Fixed version: Not available at time of report

6) Vulnerability Description

An arbitrary local file read vulnerability (CWE-862) has been identified in @promptx/mcp-office of PromptX, specifically within packages/mcp-office/src/index.ts. Multiple MCP tools—including read_docx, read_xlsx, read_pptx, list_xlsx_sheets, and read_pdf—accept a user-supplied path argument and use it directly in filesystem operations such as fs.readFileSync and AdmZip without workspace-boundary enforcement or allowlisting. An attacker with access to the mcp-office server can read arbitrary Office or PDF files from any location on the local filesystem by providing an absolute path outside the intended workspace. Version 2.4.0 is confirmed affected, and no fixed version is available at the time of reporting.

7) Technical Root Cause

  1. js/file-access-from-request
    • Source: packages/mcp-office/src/index.ts:154 (request)
    • Sink: packages/mcp-office/src/index.ts:164
    • Sink code: const buffer = fs.readFileSync(filePath);
  2. js/file-access-from-request
    • Source: packages/mcp-office/src/index.ts:154 (request)
    • Sink: packages/mcp-office/src/index.ts:175
    • Sink code: const buffer = fs.readFileSync(filePath);
  3. js/file-access-from-request
    • Source: packages/mcp-office/src/index.ts:154 (request)
    • Sink: packages/mcp-office/src/index.ts:204
    • Sink code: const zip = new AdmZip(filePath);
  4. js/file-access-from-request
    • Source: packages/mcp-office/src/index.ts:154 (request)
    • Sink: packages/mcp-office/src/index.ts:239
    • Sink code: const buffer = fs.readFileSync(filePath);
  5. js/file-access-from-request
    • Source: packages/mcp-office/src/index.ts:154 (request)
    • Sink: packages/mcp-office/src/index.ts:250
    • Sink code: const buffer = fs.readFileSync(filePath);

8) Attack Prerequisites

  • Attacker can invoke the built-in mcp-office MCP server or induce an agent/client to call one of its tools.
  • No effective runtime policy strips or constrains attacker-controlled arguments.path values before filesystem access.
  • The target file exists locally and is in a format accepted by the selected tool.

9) Proof of Concept / Reproduction Guidance

This proof of concept provides a concise, CVE-style reproduction example for the reported issue.

  1. Reproduction request
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_docx","arguments":{"path":"<absolute-path-outside-workspace>/promptx-poc/secret.docx"}}}
  1. Validation
  • Build and start @promptx/mcp-office, for example with MCP Inspector from the repository root:
pnpm install
pnpm --filter @promptx/mcp-office build
npx @modelcontextprotocol/inspector node packages/mcp-office/dist/index.js
  • Create a valid DOCX file outside the repository root:
mkdir -p <outside-workspace-dir>/promptx-poc
printf 'PromptX arbitrary file read PoC\n' > <outside-workspace-dir>/promptx-poc/secret.txt
textutil -convert docx <outside-workspace-dir>/promptx-poc/secret.txt -output <outside-workspace-dir>/promptx-poc/secret.docx
  • Submit the tools/call request above to the mcp-office server.
  • Confirm that the returned tool output contains the DOCX text content, proving that an attacker-controlled path outside the workspace reaches the file-read sink.

10) Security Impact

  • Confidentiality: High (arbitrary local Office/PDF documents outside the workspace can be read).
  • Integrity: None (the demonstrated issue is read-only).
  • Availability: Low (malformed or expensive parser inputs may cause tool errors or resource consumption).
  • Scope: Changed.

11) CVSS v3.1 Suggestion

  • Suggested vector: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:L
  • Suggested base score: 7.4 (High)
  • Adjust AV, PR, or UI if deployment-specific access controls significantly restrict who can invoke mcp-office.

12) Workarounds / Mitigations

  • Disable or remove the built-in mcp-office server where it is not needed.
  • Restrict tool invocation to trusted users/agents only.
  • Enforce a readable-root allowlist before any filesystem access.
  • Reject paths that resolve outside explicitly approved workspace directories.

13) Recommended Fix

  • Eliminate the request-to-filesystem-path flows documented above.
  • Normalize and resolve the requested path before use, then verify that it remains inside an approved workspace or configured allowlist root.
  • Revalidate after symlink resolution.
  • Reuse the same boundary-checking model already implemented in packages/mcp-workspace/src/service/workspace.service.ts.
  • Add regression tests proving attacker-controlled paths outside approved roots cannot reach fs.readFileSync or AdmZip.

14) References

15) Credits

  • Discoverer: BruceJin
  • Discovery method: Static analysis (CodeQL) plus repository source-code audit and local reproduction

16) Additional Notes for Form Mapping

  • Audit verdict: Confirmed exploitable: attacker-controlled arguments.path reaches local file-read sinks in a separate built-in MCP server.
  • Dynamic exploit replay status: completed locally with a stable read_docx proof of concept.
  • This is not mitigated by mcp-workspace path checks because mcp-office is launched as an independent MCP server and handles its own tool requests.

For furthermore information, please refer to BruceJqs/public_exp#8

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions