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

CVE-2026-77244: [mcp-atlassian] Authentication bypass in HTTP transport: AtlassianOpaqueTokenVerifier accepts any non-empty token

criticalCVSS 3.1 10.0GHSA-wrhw-j3f9-8vc6

mcp-atlassian in HTTP transport mode performs no real authentication. `AtlassianOpaqueTokenVerifier` accepts any non-empty token, and with the default config a request with no token at all is also served, using the operator's own Jira and Confluence credentials. Anyone who can reach the port gets operator-level access. Versions before 0.22.0 are affected; upgrade to 0.22.0.

Published Sep 22, 2026 · Analysis Oct 22, 2026

What an attacker can do

Two defaults combine. First, verify_token() in src/mcp_atlassian/utils/token_verifier.py returns a valid AccessToken for any non-empty string, with a 30-day expiry and the required scopes attached. Second, the OAuth proxy provider is off by default (OAUTH_PROXY_ENABLE_ENV is false), so _build_auth_provider() returns None and the FastMCP HTTP transport never issues an auth challenge.

UserTokenMiddleware does not reject a request that lacks an Authorization header; it just leaves user_atlassian_token unset. JiraFetcher and ConfluenceFetcher then fall back to JiraConfig.from_env(), which reads JIRA_USERNAME and JIRA_API_TOKEN from the environment. This is the documented single-user quickstart.

So a bare POST /mcp with a tools/call for jira_get_issue, or a request with Authorization: Bearer anything-at-all, returns Jira data fetched with the operator's token. The attacker gets full read and write access to Jira and Confluence as the operator: issues, comments, attachments, pages, and the ability to create webhooks or automation rules. Atlassian's audit trail names the operator, not the attacker.

The attacker only needs network reach to the HTTP port, such as a Docker port mapping, an unauthenticated reverse proxy, a shared internal network or a forgotten tunnel. The CVSS score is 10.0 with changed scope.

Who is affected

mcp-atlassian before 0.22.0, running with the HTTP transport (MCP_TRANSPORT=streamable-http) and credentials in environment variables (JIRA_API_TOKEN, CONFLUENCE_API_TOKEN) without the OAuth proxy enabled. Servers using stdio are not reachable over the network this way.

pip show mcp-atlassian

Check for a published port in your Docker or compose file, and for a reverse proxy or tunnel in front of the server.

mcp-atlassian

Ecosystem
PyPI
Affected
< 0.22.0
Fixed in
0.22.0
Check
pip show mcp-atlassian

How to fix it

Upgrade to mcp-atlassian 0.22.0:

pip install -U mcp-atlassian==0.22.0

The fix is PR #1448. The report suggests refusing to start HTTP transport without an auth provider unless an explicit single-user flag is set, binding to 127.0.0.1 by default, and replacing the verifier with one that calls Atlassian (such as /rest/api/3/myself) to validate the token. Beyond upgrading, keep the port off untrusted networks, put authentication in front of it, and rotate the Atlassian API token if the server was reachable by anyone you do not trust.

How to tell if you were hit

Look at the MCP server's access logs for POST /mcp requests with no Authorization header or with obviously fake Bearer tokens, from IPs that are not your MCP clients. In Atlassian audit logs, review activity under the operator's identity that the operator did not perform, such as bulk issue reads, attachment downloads and new webhooks or automation rules. Rotate the API token afterwards. Beam records agent MCP tool calls, but this attack comes from direct network requests, so server and Atlassian logs matter more.

Original advisory

From the GitHub Advisory Database (CC BY 4.0).

Description

mcp-atlassian deploys in two common patterns:

Pattern A (single-user, server-side credentials): operator sets JIRA_USERNAME + JIRA_API_TOKEN (or CONFLUENCE_USERNAME + CONFLUENCE_API_TOKEN) in environment variables. Server uses these to call Jira/Confluence. This is the documented quickstart pattern.

Pattern B (multi-user, OAuth or per-request PAT): operator sets up OAuth proxy or accepts per-user tokens via Authorization or service headers.

The authentication mechanism in HTTP transport has two issues that combine to permit unauthenticated access to Pattern A deployments:

  1. AtlassianOpaqueTokenVerifier.verify_token() at src/mcp_atlassian/utils/token_verifier.py accepts any non-empty string as a valid token:

async def verify_token(self, token: str) -> AccessToken | None: if not token: return None scopes = self.required_scopes or [] return AccessToken( token=token, client_id="atlassian", scopes=scopes, expires_at=int(time.time()) + 86400 * 30, )

The docstring documents this: "we accept non-empty tokens and attach the required scopes."

  1. The default deployment does NOT enable the OAuth proxy auth provider (OAUTH_PROXY_ENABLE_ENV defaults to false; main.py:726). When _build_auth_provider() returns None, FastMCP HTTP transport accepts requests with no authentication challenge.
  1. UserTokenMiddleware._parse_auth_header (main.py:601-664) extracts tokens from Authorization headers and stores them in scope state. If NO Authorization header is present (main.py:584-595), the middleware does not reject the request — it simply does not populate user_atlassian_token.
  1. JiraFetcher / ConfluenceFetcher fall back to JiraConfig.from_env() when no user-supplied token is in scope state. from_env() reads JIRA_API_TOKEN and JIRA_USERNAME from environment and uses them as the API credentials.

Composition: an attacker who reaches the HTTP transport (e.g., server exposed on a port reachable from attacker — direct bind, Docker port mapping, reverse proxy without auth, container in a network the attacker joined) can:

  • Send no Authorization header at all, OR
  • Send any garbage Bearer token

Either request reaches tool handlers. The tool handlers, finding no user-supplied token, use the server's env-var credentials to call Jira / Confluence. The attacker has full operator-level access to the operator's Atlassian instance.

This is the same vulnerability class as CVE-2026-27825 (Arctic Wolf, unauthenticated RCE+SSRF in Atlassian MCP). The previous CVE was for a different code path; this report concerns the auth verifier and middleware behavior present in the current main branch.

**Steps to Reproduce**

Source-level demonstration:

1. Verify the verifier accepts arbitrary tokens:

       cd src/
       python -c "
       import asyncio
       from mcp_atlassian.utils.token_verifier import AtlassianOpaqueTokenVerifier
       v = AtlassianOpaqueTokenVerifier(required_scopes=['read:jira-work'])
       result = asyncio.run(v.verify_token('anything-at-all'))
       print('Accepted:', result is not None)
       print('Token stored:', result.token if result else None)
       print('Scopes granted:', result.scopes if result else None)
       "

   Expected:
       Accepted: True
       Token stored: anything-at-all
       Scopes granted: ['read:jira-work']

End-to-end (researcher's own Atlassian sandbox):

1. Start mcp-atlassian in HTTP mode against a researcher-owned Atlassian
   Cloud instance with JIRA_API_TOKEN configured:

       export JIRA_URL=https://researcher.atlassian.net
       export [email protected]
       export JIRA_API_TOKEN=<researcher's-real-token>
       export MCP_TRANSPORT=streamable-http
       export PORT=3000
       # Do NOT set OAUTH_PROXY_ENABLE_ENV — leave it default (false)
       mcp-atlassian

2. From another machine (or curl on localhost), with no auth:

       curl -X POST http://localhost:3000/mcp \
            -H "content-type: application/json" \
            -H "accept: application/json, text/event-stream" \
            -d '{
              "jsonrpc":"2.0", "id":1, "method":"tools/call",
              "params":{
                "name":"jira_get_issue",
                "arguments":{"issue_key":"PROJ-1"}
              }
            }'

   Expected: returns the Jira issue payload — using the server's
   JIRA_API_TOKEN to authenticate to Atlassian. No client-side token
   provided.

3. Optional: same call with a garbage Bearer for completeness:

       curl ... -H "Authorization: Bearer anything-at-all" ...

   Same result.
   
   
   **Impact**:
   
   Attacker profile: any party with network reach to the HTTP transport.
No credentials, no prior account, no privileged position required.

Typical deployment patterns at risk:

  - Docker compose with port exposed (very common in mcp-atlassian's
    docs and community deployments)
  - Cloud-deployed MCP server behind a load balancer where the LB
    doesn't enforce auth (delegates to the application)
  - Internal corporate network where any employee can reach the server
  - Misconfigured Kubernetes ingress
  - Tunneled MCP server via ngrok / Cloudflare Tunnel for development
    that gets left exposed

Security impact after exploitation:

1. Full Jira read access. Every project, every issue, every comment,
   every attachment, every user — using the operator's API token.

2. Full Jira write access. Create, edit, delete issues. Add comments
   under the operator's identity. Move issues across boards. Bulk-edit.

3. Full Confluence read/write access. Same surface — pages, spaces,
   attachments, permissions, restricted spaces visible to the operator's
   identity.

4. Audit trail names the operator. Every API call is signed with the
   operator's token. From Atlassian's logging side, the operator is the
   actor — covering the attacker's tracks and shifting blame.

5. Pivot. Attachments often contain credentials, infrastructure
   diagrams, customer data. Confluence pages often store secrets in
   plaintext under the assumption of access control.

6. Persistence. Attacker can create new Jira webhooks, automation rules,
   or Confluence integrations that survive beyond the MCP session.

CVE-2026-27825 (Arctic Wolf, May 2026) was scored CVSS 9.8 Critical for
unauth RCE+SSRF in this same code surface. This report is the auth-bypass
component of the same class against the current main branch.


**Suggested Fix**

The most direct fix is the standard MCP-server-with-env-creds pattern:

  1. When OAUTH_PROXY_ENABLE_ENV is not set, REFUSE to start the HTTP
     transport unless an explicit "single-user mode" flag is set:

       SINGLE_USER_MODE = is_env_truthy("MCP_ATLASSIAN_SINGLE_USER")
       if MCP_TRANSPORT == "streamable-http" and not auth_provider and not SINGLE_USER_MODE:
           raise SystemExit(
               "HTTP transport requires either OAUTH_PROXY_ENABLE=true "
               "or MCP_ATLASSIAN_SINGLE_USER=true (acknowledges that env "
               "credentials will be used for any incoming request)."
           )

  2. Even with SINGLE_USER_MODE, bind the HTTP transport to 127.0.0.1
     by default unless the operator overrides with an explicit
     MCP_ATLASSIAN_BIND_PUBLIC=true.

  3. Document the multi-tenant pattern as requiring OAuth proxy or
     per-request user-token middleware with a verifier that actually
     verifies (not the opaque-accept-anything stub).

  4. Replace AtlassianOpaqueTokenVerifier with a verifier that performs
     a token-info or whoami call to Atlassian. The fact that Atlassian
     tokens are opaque does not preclude verification — a
     /rest/api/3/myself call validates the token and returns the
     associated user, which the verifier can attach to the AccessToken's
     scopes and user_id fields.

Defense in depth: the README quickstart should not encourage exposing
the HTTP transport without auth. The docker-compose.yml in the repo
should bind to 127.0.0.1 only by default.

Credited to crazydude123.

Questions

Do I have to send a token to exploit this?

No. A request with no Authorization header works on a default deployment, and so does any non-empty Bearer string, because the verifier accepts it.

Is the stdio transport affected?

The reported path is in the HTTP transport. A stdio server is not exposed on a network port, so the unauthenticated remote access does not apply.

What should I do after patching?

Rotate the Jira and Confluence API tokens the server used if it was reachable by untrusted parties, and review Atlassian audit logs for actions made under the operator's name.

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