Skip to content

BioinfoMCP Arbitrary File Write via POST /upload #61

Description

@wing3e

BioinfoMCP Arbitrary File Write via POST /upload

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: florensiawidjaja
  • Product: BioinfoMCP
  • Repository: https://github.com/florensiawidjaja/BioinfoMCP.git
  • Reviewed local source path: python-batch-04/datasets/florensiawidjaja-BioinfoMCP
  • Exposed interface: Flask web route POST /upload
  • Affected component(s):
  • bioinfo_mcp_platform/app.py

4) Vulnerability Type

  • CWE: CWE-73 (External Control of File Name or Path)
  • Short title: arbitrary file write by trusting the uploaded multipart filename

5) Affected Versions

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

6) Vulnerability Description

The web-facing BioinfoMCP platform exposes POST /upload for converting uploaded manuals into generated MCP server artifacts. The route reads the uploaded file object from request.files, takes its client-supplied filename verbatim, and writes it using f.save(os.path.join("uploads", f.filename)).

Because the multipart filename field is attacker-controlled, an absolute path such as /tmp/bioinfomcp_poc.pdf overrides the intended uploads/ directory entirely, and traversal sequences can also escape it. The route then passes that attacker-chosen saved path into scripts/do_sth.py, so the unsafe path is not only written but also treated as the input artifact for the rest of the conversion workflow.

7) Technical Root Cause

  1. HTTP entry point
    • bioinfo_mcp_platform/app.py:70-79
    • upload() accepts form field name and uploaded file file.
  2. Unsafe destination path construction
    • bioinfo_mcp_platform/app.py:80
    • in_path = os.path.join(UPLOAD, f.filename)
  3. Unsafe file write
    • bioinfo_mcp_platform/app.py:81
    • f.save(in_path)
  4. Follow-on use of the attacker-selected path
    • bioinfo_mcp_platform/app.py:82-89
    • The application launches scripts/do_sth.py with in_path as the uploaded manual path.
  5. Audit conclusion
    • The browser-facing upload route provides an arbitrary local file write primitive through the multipart filename field.

8) Attack Prerequisites

  • Network access to the BioinfoMCP web platform.
  • Ability to submit a multipart upload to /upload.
  • Write permissions for the chosen target path under the server's OS account.

9) Proof of Concept / Reproduction Guidance

  1. Reproduction request
curl -X POST http://target/upload \
  -F 'name=fastqc' \
  -F 'file=@./dummy.pdf;filename=/tmp/bioinfomcp_poc.pdf;type=application/pdf'
  1. Why this works
  • Flask exposes the raw multipart filename value through f.filename.
  • os.path.join("uploads", "/tmp/bioinfomcp_poc.pdf") resolves to /tmp/bioinfomcp_poc.pdf on POSIX systems.
  • f.save(...) writes the uploaded content there immediately.
  • The file write happens before scripts/do_sth.py runs, so the primitive succeeds even if later conversion logic errors out.
  1. Expected observable result
  • The uploaded file is written to /tmp/bioinfomcp_poc.pdf instead of uploads/.
  • The server log shows the subsequent scripts/do_sth.py execution using that attacker-selected path.

10) Security Impact

  • Confidentiality: Low, because this specific primitive is a write rather than a direct read.
  • Integrity: High, because arbitrary writable files can be created or overwritten outside the intended upload directory.
  • Availability: Medium, because malicious uploads can clobber operational files or poison later conversion runs.
  • Scope: Unchanged.

11) CVSS v3.1 Suggestion

  • Suggested vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L
  • Suggested base score: 8.1 (High) when the upload route is exposed to untrusted users.

12) Workarounds / Mitigations

  • Restrict the web platform to trusted users until upload handling is fixed.
  • Strip directory separators and absolute-path markers from multipart filenames.
  • Store uploads under server-generated names rather than caller-controlled names.

13) Recommended Fix

  • Replace direct use of f.filename with werkzeug.utils.secure_filename() or an equivalent allowlist.
  • Generate a server-side random filename and keep the original name only as metadata.
  • Add regression tests for absolute filenames and traversal payloads in multipart uploads.

14) References

15) Credits

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

16) Additional Notes

  • This report intentionally focuses on the web upload route because it is the clearest externally reachable arbitrary-write primitive in the repository.
  • Other reviewed MCP wrappers often accept file paths as part of their advertised bioinformatics-tool workflow and were not relied on for this confirmation.

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