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
- HTTP entry point
bioinfo_mcp_platform/app.py:70-79
upload() accepts form field name and uploaded file file.
- Unsafe destination path construction
bioinfo_mcp_platform/app.py:80
in_path = os.path.join(UPLOAD, f.filename)
- Unsafe file write
bioinfo_mcp_platform/app.py:81
f.save(in_path)
- 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.
- 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
- Reproduction request
curl -X POST http://target/upload \
-F 'name=fastqc' \
-F 'file=@./dummy.pdf;filename=/tmp/bioinfomcp_poc.pdf;type=application/pdf'
- 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.
- 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.
BioinfoMCP Arbitrary File Write via
POST /upload1) CNA / Submission Type
2) Reporter Contact
WinegeeWinegee@zju.edu.cnYes3) Vendor / Product Identification
python-batch-04/datasets/florensiawidjaja-BioinfoMCPPOST /uploadbioinfo_mcp_platform/app.py4) Vulnerability Type
filename5) Affected Versions
/uploadfile-save logic6) Vulnerability Description
The web-facing BioinfoMCP platform exposes
POST /uploadfor converting uploaded manuals into generated MCP server artifacts. The route reads the uploaded file object fromrequest.files, takes its client-suppliedfilenameverbatim, and writes it usingf.save(os.path.join("uploads", f.filename)).Because the multipart
filenamefield is attacker-controlled, an absolute path such as/tmp/bioinfomcp_poc.pdfoverrides the intendeduploads/directory entirely, and traversal sequences can also escape it. The route then passes that attacker-chosen saved path intoscripts/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
bioinfo_mcp_platform/app.py:70-79upload()accepts form fieldnameand uploaded filefile.bioinfo_mcp_platform/app.py:80in_path = os.path.join(UPLOAD, f.filename)bioinfo_mcp_platform/app.py:81f.save(in_path)bioinfo_mcp_platform/app.py:82-89scripts/do_sth.pywithin_pathas the uploaded manual path.filenamefield.8) Attack Prerequisites
/upload.9) Proof of Concept / Reproduction Guidance
filenamevalue throughf.filename.os.path.join("uploads", "/tmp/bioinfomcp_poc.pdf")resolves to/tmp/bioinfomcp_poc.pdfon POSIX systems.f.save(...)writes the uploaded content there immediately.scripts/do_sth.pyruns, so the primitive succeeds even if later conversion logic errors out./tmp/bioinfomcp_poc.pdfinstead ofuploads/.scripts/do_sth.pyexecution using that attacker-selected path.10) Security Impact
11) CVSS v3.1 Suggestion
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L8.1 (High)when the upload route is exposed to untrusted users.12) Workarounds / Mitigations
13) Recommended Fix
f.filenamewithwerkzeug.utils.secure_filename()or an equivalent allowlist.14) References
bioinfo_mcp_platform/app.py15) Credits
Winegee16) Additional Notes