제출 #1052035: sooperset mcp-atlassian 0.17.0 - 0.23.1 Path Traversal정보

제목sooperset mcp-atlassian 0.17.0 - 0.23.1 Path Traversal
설명Product: mcp-atlassian (sooperset/mcp-atlassian, MCP server for Jira/Confluence; PyPI package) Affected versions: >= 0.17.0, <= 0.23.1 (latest release at report time; unfixed). SUMMARY: The upload-side path guard added in the #1448 hardening confines an attachment's source path to the process working directory (validate_safe_path() with base_dir = os.getcwd()). The project's own advisory for that batch (GHSA-wm45-qh3g-v83f) states that the vulnerable boundary is remote/multi-user transports, and the official remote-deployment documentation (docs/http-transport.mdx) tells operators to run uvx mcp-atlassian --transport streamable-http with no working-directory requirement. The working directory of that process is set by the deployment method, not by the application: a systemd system unit without WorkingDirectory= starts the process with cwd = / (systemd.exec(5) default), a systemd user unit with cwd = $HOME, and a shell launch inherits the shell's cwd. When the cwd is /, every absolute path on the host satisfies is_relative_to(cwd) and the guard passes everything. An authenticated remote MCP caller (the attacker position of the accepted upload advisories, e.g. CVE-2026-77253/77259) calls confluence_upload_attachment with file_path pointing at any file the server process can read — /etc/passwd, /proc/self/environ (containing the operator's Atlassian tokens), .env, SSH keys — and the server reads it and uploads it to the caller's own Confluence instance. Nothing in the package changes the working directory, nothing configures the guard's boundary, and nothing warns when a remote transport starts from a wide cwd. The same root cause was already accepted by the project on the download side: CVE-2026-77271 (GHSA-6vmq-24h2-pj7j, High, "Incomplete path traversal fix … (bypass of GHSA-xjgw-4wvw-rgm4)"), whose suggested fix was to *"pass a dedicated, explicitly configured directory as base_dir instead of relying on CWD"*. The Jira download path then received a second hardening that refuses writes into the cwd root (comment referencing GHSA-6vmq); the upload side received neither. This report is the same defect on the read/exfiltration side, in the latest release v0.23.1. ROOT CAUSE: src/mcp_atlassian/utils/io.py (v0.23.1): def validate_safe_path(path, base_dir=None) -> Path: if base_dir is None: base_dir = os.getcwd() # boundary = whatever the process cwd happens to be resolved_base = Path(base_dir).resolve(strict=False) ... if not resolved_path.is_relative_to(resolved_base): raise ValueError(f"Path traversal detected: {path} resolves outside {resolved_base}") return resolved_path The check itself resolves symlinks and rejects ../ and symlink escapes (control-group tests below). The defect is only the boundary choice: the process cwd, which in a remote deployment is an environment property the operator never chose deliberately. Upload chain (all vendor code, no second line of defense): - src/mcp_atlassian/confluence/attachments.py:155 — file_path = str(validate_safe_path(file_path)) (no base_dir). The comment above states the intent: "Confine the upload source to the workspace before it is read" — vacuous when cwd is /. - src/mcp_atlassian/confluence/attachments.py:639 — open(file_path, "rb"), then _upload_attachment_direct sends the bytes to Confluence. - src/mcp_atlassian/servers/confluence.py:1832-1960 — the tool forwards file_path untouched; no allowlist, rewrite, or re-check. - No chdir anywhere in the package; no configuration key for the guard's base directory; no startup warning about the cwd. Attacker position in v0.23.1: the unauthenticated chain from CVE-2026-77248 is closed by default (servers/dependencies.py:993-998 refuses to fall back to global credentials without a user identity unless the operator explicitly sets ALLOW_GLOBAL_CRED_FALLBACK=true). The attacker is the authenticated remote MCP caller of a multi-user HTTP/SSE deployment — the same position as the accepted batch (GHSA-vc25-24vv-fxxm: "In HTTP or multi-user deployments, an MCP caller who can invoke write tools …"). If an operator sets ALLOW_GLOBAL_CRED_FALLBACK=true on a wide-cwd host (the documented single-user option), the unauthenticated position is restored as well (deployment-dependent, not the default). DEPLOYMENT MATRIX: Each row verified against the project's Dockerfile, README, docs/http-transport.mdx at v0.23.1, and systemd.exec(5): The affected population is non-containerized remote deployments (systemd units, shell/uvx launches). The share of deployments per form is not measurable from outside; no share is claimed. REPRODUCTION (V0.23.1 INSTALLED FROM PYPI; INSTALLED UTILS/IO.PY BYTE-IDENTICAL TO TAG V0.23.1 = COMMIT 5BE93A041385E2BA076478787946D93AE02C663F): 1. Harness on unmodified vendor code (validate_safe_path → upload_attachment → _upload_attachment_direct → open(file_path, "rb"), only _send_attachment_request patched to capture the multipart body; loopback only): - wide cwd (home directory) → {'success': True, 'size': 54} and the multipart body contains the secret file's content — exfiltration; - control, confined cwd (fresh temp workspace) → {'success': False, 'error': 'Path traversal detected: … resolves outside …'} — refused, zero leak. - variants: cwd = C:\ (root-of-drive equivalent of /) → succeeds; symlink escape under confined cwd → refused; relative ../ traversal → refused. The resolve/is_relative_to logic is hard; the vulnerability is exclusively the boundary choice. 2. End-to-end over the real streamable-http transport (one-take screen recording, 4 min, unedited): mock Confluence on 127.0.0.1:18080 → real mcp-atlassian v0.23.1 server with --transport streamable-http --port 9000 and cwd C:\ → standard MCP initialize + tools/call confluence_upload_attachment with file_path = "C:\\Windows\\System32\\drivers\\etc\\hosts" → tool returns "Attachment uploaded successfully, size 402" → the mock Confluence prints the complete 402-byte hosts file. A real read of a server-side file by a remote caller, on the released package. IMPACT: - An authenticated remote MCP caller of an HTTP/SSE deployment reads arbitrary files readable by the server process on the server host and exfiltrates them to their own Confluence/Jira: /etc/passwd, /proc/self/environ (operator's JIRA_API_TOKEN / CONFLUENCE_API_TOKEN), .env files, SSH keys, application config. - In multi-tenant deployments this breaks tenant isolation as in the original batch. - The exfiltration channel is the tool's designed output (the attachment), indistinguishable from a legitimate upload in server logs. - Prerequisites: a remote (HTTP/SSE) deployment whose startup cwd is wide — the platform default for the standard non-containerized startup methods, never mentioned in the documentation. - Not affected: containerized deployments on the official image (WORKDIR /app); local stdio usage (defined by the project as intended behavior). SUGGESTED REMEDIATION: 1. Explicit boundary (primary). Give the upload guard a configured base directory instead of the cwd — e.g. MCP_ATLASSIAN_UPLOAD_DIR, defaulting to a dedicated directory; pass it explicitly at both call sites. This is the fix the project itself recommended in GHSA-6vmq-24h2-pj7j. 2. Startup check (defense in depth). When the remote transport is enabled and the process cwd resolves to the filesystem root or a home directory, warn prominently or refuse to start until the upload directory is configured — the same pattern already applied to the Jira download path (cwd-root write rejection referencing GHSA-6vmq). 3. Documentation. docs/http-transport.mdx should state the working-directory requirement for remote deployments and show WorkingDirectory= for systemd units (default /, per systemd.exec(5)). CVSS v3.1: 7.7 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N). CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) DISCLOSURE: Reported privately to the maintainers via GitHub PVR on 2026-10-04 (GHSA-qh7q-jmc7-rc7j, state triage, no CVE requested through GitHub). Not publicly disclosed. DEDUP: NVD/GitHub/OSV searched: the related accepted advisories are GHSA-wm45-qh3g-v83f (CVE-2026-73496, upload write side), GHSA-6vmq-24h2-pj7j (CVE-2026-77271, download side), and GHSA-6cr4-ccf3-x7h4 (CVE-2026-77259). None covers the upload-read boundary set to the process CWD in a remote HTTP/SSE deployment. FIX: Pass an explicitly configured base directory (e.g. MCP_ATLASSIAN_UPLOAD_DIR) to validate_safe_path instead of os.getcwd(); warn or refuse to start a remote transport when the resolved cwd is the filesystem root or a home directory; document WorkingDirectory= for systemd units.
사용자
 NeoWSY (UID 82976)
제출2026. 10. 22. PM 01:28 (19 날 ago)
모더레이션2026. 11. 10. AM 10:30 (19 days later)
상태수락
VulDB 항목433614 [sooperset mcp-atlassian 까지 0.24.0 io.py validate_safe_path 디렉토리 순회]
포인트들17

Interested in the pricing of exploits?

See the underground prices here!