mail-mcp-bridge Path Traversal / Arbitrary Directory Deletion Vulnerability Report
1) CNA / Submission Type
- Submission type: Report a vulnerability (CVE ID request)
- Reporter role: Independent security researcher
- Report date: April 10, 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: fatbobman
- Product: mail-mcp-bridge
- Repository: https://github.com/fatbobman/mail-mcp-bridge.git
- Reviewed local source path:
python-batch-04/datasets/fatbobman-mail-mcp-bridge
- Affected component(s):
src/mail_mcp_server.py
src/cleanup_attachments.py
src/extract_attachments.py
4) Vulnerability Type
- CWE: CWE-22 / CWE-73 (Path Traversal / External Control of File Name or Path)
- Short title: path traversal in
cleanup_attachments allows deletion of directories outside the attachment cache root
5) Affected Versions
- Confirmed affected: d9e7d9acc2abcf9da8252d76506fc5afbc08d08e
- Suspected affected range: revisions containing the same
message_ids path-join behavior documented below
- Fixed version: Not available at time of report (April 10, 2026)
6) Vulnerability Description
The cleanup_attachments MCP tool accepts a list of RFC message_ids and is intended to remove temporary extraction directories beneath the attachment cache root. However, the implementation only strips angle brackets from each message_id before joining it under the base directory. Traversal sequences such as ../mail-mcp-bridge-poc survive unchanged, so the server resolves a path outside mail-mcp-attachments and recursively deletes it with shutil.rmtree(). This provides a real arbitrary-directory deletion primitive relative to the temp root used by the service. The related extraction flow in extract_attachments.py repeats the same unsafe message_id-to-directory join when creating directories.
7) Technical Root Cause
- The server exposes a cleanup tool that forwards caller-controlled
message_ids into the cleanup helper:
src/mail_mcp_server.py:151-165
src/mail_mcp_server.py:430-441
- The attachment base path defaults to
/tmp/mail-mcp-attachments (or ${MAIL_ATTACHMENT_PATH}/mail-mcp-attachments):
src/cleanup_attachments.py:13-24
- Each attacker-controlled
message_id is only normalized with strip('<>'), which does not remove ../ traversal tokens:
src/cleanup_attachments.py:67-70
- Code:
clean_message_id = message_id.strip('<>')
- The code then enumerates and recursively deletes the resulting path without a containment check:
src/cleanup_attachments.py:72-84
- Code:
shutil.rmtree(message_dir)
- The extraction helper contains the same root cause on directory creation:
src/extract_attachments.py:131-135
- Code:
message_dir = Path(base_dir) / clean_message_id
- In this repository, the cleanup path is the strongest exploit primitive because it does not depend on a valid Mail message or attachment payload.
8) Attack Prerequisites
- Ability to invoke the
cleanup_attachments MCP tool.
- The server must run with filesystem permissions to access and remove the targeted directory.
- A deployment where untrusted or partially trusted callers can submit arbitrary
message_ids.
- For the demonstrated PoC, the chosen victim directory must exist as a sibling reachable through
../ from the configured attachment base path.
9) Proof of Concept / Reproduction Guidance
This PoC exercises the real vulnerable deletion path without depending on a valid macOS Mail message.
- Ensure the server uses the default temp base or set:
export MAIL_ATTACHMENT_PATH=/tmp
- Create a harmless sibling directory outside the intended cache root:
mkdir -p /tmp/mail-mcp-bridge-poc
printf 'sentinel\n' > /tmp/mail-mcp-bridge-poc/evidence.txt
- Send the following MCP tool call:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "cleanup_attachments",
"arguments": {
"message_ids": [
"../mail-mcp-bridge-poc"
]
}
}
}
- Expected result:
- The helper computes
message_dir = Path("/tmp/mail-mcp-attachments") / "../mail-mcp-bridge-poc".
message_dir.exists() succeeds because the path resolves to /tmp/mail-mcp-bridge-poc.
shutil.rmtree(message_dir) deletes the sibling directory outside mail-mcp-attachments.
- The JSON response reports the cleaned path as something like
/tmp/mail-mcp-attachments/../mail-mcp-bridge-poc, and /tmp/mail-mcp-bridge-poc/evidence.txt is gone afterward.
10) Security Impact
- Confidentiality: Low. The cleanup code enumerates files and sizes inside attacker-selected directories before deletion.
- Integrity: High. Attackers can delete directories outside the intended attachment cache root.
- Availability: High. Deleting application data or operator-owned temp directories can disrupt the host or nearby services.
- 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:H
- Suggested base score: 8.8 (High)
- If only trusted authenticated operators can call the MCP tool,
PR should be adjusted upward.
12) Workarounds / Mitigations
- Reject
message_id values containing path separators, traversal tokens, or absolute-path syntax before using them as directory names.
- Resolve the candidate directory and verify that it remains under the canonical attachment base directory before any
exists(), rglob(), or rmtree() call.
- Consider using a server-generated opaque directory name instead of mapping raw RFC
Message-ID values directly onto the filesystem.
- Restrict access to attachment-management tools until a fix is deployed.
13) Recommended Fix
- Apply canonicalization plus root-boundary enforcement in both
cleanup_attachments.py and extract_attachments.py.
- Treat
message_id as untrusted input even if it resembles a mail header value; remove path separators rather than only stripping angle brackets.
- Add regression tests for payloads such as
../mail-mcp-bridge-poc, ..\\mail-mcp-bridge-poc, absolute paths, and symlink-based escapes.
- Return an explicit error when the resolved path is outside the attachment cache root.
14) References
15) Credits
- Discoverer:
Winegee
- Discovery method: Static analysis (CodeQL) plus repository source-code audit
16) Additional Notes for Form Mapping
- Audit verdict: Confirmed vulnerability.
- Total reviewed SARIF results for this rule in the repository: 5
- Concrete PoC status: source-confirmed and mapped to the real
cleanup_attachments tool and deletion sink.
- Dynamic exploit replay status: not executed end-to-end in this batch update.
- Version-range accuracy should be finalized by the maintainer against release history before public disclosure.
mail-mcp-bridge Path Traversal / Arbitrary Directory Deletion Vulnerability Report
1) CNA / Submission Type
2) Reporter Contact (fill before submit)
WinegeeWinegee@zju.edu.cnYes3) Vendor / Product Identification
python-batch-04/datasets/fatbobman-mail-mcp-bridgesrc/mail_mcp_server.pysrc/cleanup_attachments.pysrc/extract_attachments.py4) Vulnerability Type
cleanup_attachmentsallows deletion of directories outside the attachment cache root5) Affected Versions
message_idspath-join behavior documented below6) Vulnerability Description
The
cleanup_attachmentsMCP tool accepts a list of RFCmessage_idsand is intended to remove temporary extraction directories beneath the attachment cache root. However, the implementation only strips angle brackets from eachmessage_idbefore joining it under the base directory. Traversal sequences such as../mail-mcp-bridge-pocsurvive unchanged, so the server resolves a path outsidemail-mcp-attachmentsand recursively deletes it withshutil.rmtree(). This provides a real arbitrary-directory deletion primitive relative to the temp root used by the service. The related extraction flow inextract_attachments.pyrepeats the same unsafemessage_id-to-directory join when creating directories.7) Technical Root Cause
message_idsinto the cleanup helper:src/mail_mcp_server.py:151-165src/mail_mcp_server.py:430-441/tmp/mail-mcp-attachments(or${MAIL_ATTACHMENT_PATH}/mail-mcp-attachments):src/cleanup_attachments.py:13-24message_idis only normalized withstrip('<>'), which does not remove../traversal tokens:src/cleanup_attachments.py:67-70clean_message_id = message_id.strip('<>')src/cleanup_attachments.py:72-84shutil.rmtree(message_dir)src/extract_attachments.py:131-135message_dir = Path(base_dir) / clean_message_id8) Attack Prerequisites
cleanup_attachmentsMCP tool.message_ids.../from the configured attachment base path.9) Proof of Concept / Reproduction Guidance
This PoC exercises the real vulnerable deletion path without depending on a valid macOS Mail message.
export MAIL_ATTACHMENT_PATH=/tmp{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "cleanup_attachments", "arguments": { "message_ids": [ "../mail-mcp-bridge-poc" ] } } }message_dir = Path("/tmp/mail-mcp-attachments") / "../mail-mcp-bridge-poc".message_dir.exists()succeeds because the path resolves to/tmp/mail-mcp-bridge-poc.shutil.rmtree(message_dir)deletes the sibling directory outsidemail-mcp-attachments./tmp/mail-mcp-attachments/../mail-mcp-bridge-poc, and/tmp/mail-mcp-bridge-poc/evidence.txtis gone afterward.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:HPRshould be adjusted upward.12) Workarounds / Mitigations
message_idvalues containing path separators, traversal tokens, or absolute-path syntax before using them as directory names.exists(),rglob(), orrmtree()call.Message-IDvalues directly onto the filesystem.13) Recommended Fix
cleanup_attachments.pyandextract_attachments.py.message_idas untrusted input even if it resembles a mail header value; remove path separators rather than only stripping angle brackets.../mail-mcp-bridge-poc,..\\mail-mcp-bridge-poc, absolute paths, and symlink-based escapes.14) References
src/mail_mcp_server.pysrc/cleanup_attachments.pysrc/extract_attachments.py15) Credits
Winegee16) Additional Notes for Form Mapping
cleanup_attachmentstool and deletion sink.