Get a Demo

Let's Patch It!

Book a short call with one our specialists, we'll walk you through how Endor Patches work, and ask you a few questions about your environment (like your primary programming languages and repository management). We'll also send you an email right after you fill out the form, feel free to reply with any questions you have in advance!

CVE

CVE-2026-77244

[mcp-atlassian] Authentication bypass in HTTP transport: AtlassianOpaqueTokenVerifier accepts any non-empty token
Back to all
CVE

CVE-2026-77244

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

Description

mcp-atlassian deploys in two common patterns:

  Pattern A (single-user, server-side credentials): operator sets

    JIRAUSERNAME + JIRAAPITOKEN (or CONFLUENCEUSERNAME + CONFLUENCEAPITOKEN)

    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/mcpatlassian/utils/tokenverifier.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

   (OAUTHPROXYENABLE_ENV defaults to false; main.py:726). When

   buildauth_provider() returns None, FastMCP HTTP transport accepts

   requests with no authentication challenge.

  1. UserTokenMiddleware.parseauth_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

   useratlassiantoken.

  1. JiraFetcher / ConfluenceFetcher fall back to JiraConfig.from_env()

   when no user-supplied token is in scope state. from_env() reads

   JIRAAPITOKEN 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 mcpatlassian.utils.tokenverifier 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 JIRAAPITOKEN configured:

       export JIRA_URL=https://researcher.atlassian.net

       export JIRA_USERNAME=researcher@example.com

       export JIRAAPITOKEN=<researcher's-real-token>

       export MCP_TRANSPORT=streamable-http

       export PORT=3000

       # Do NOT set OAUTHPROXYENABLE_ENV — leave it default (false)

       mcp-atlassian

  1. 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":"jiragetissue",

                "arguments":{"issue_key":"PROJ-1"}

              }

            }'

   Expected: returns the Jira issue payload — using the server's

   JIRAAPITOKEN to authenticate to Atlassian. No client-side token

   provided.

  1. 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.

  1. Full Jira write access. Create, edit, delete issues. Add comments

   under the operator's identity. Move issues across boards. Bulk-edit.

  1. Full Confluence read/write access. Same surface — pages, spaces,

   attachments, permissions, restricted spaces visible to the operator's

   identity.

  1. 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.

  1. Pivot. Attachments often contain credentials, infrastructure

   diagrams, customer data. Confluence pages often store secrets in

   plaintext under the assumption of access control.

  1. 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 OAUTHPROXYENABLE_ENV is not set, REFUSE to start the HTTP

     transport unless an explicit "single-user mode" flag is set:

       SINGLEUSERMODE = isenvtruthy("MCPATLASSIANSINGLE_USER")

       if MCPTRANSPORT == "streamable-http" and not authprovider and not SINGLEUSERMODE:

           raise SystemExit(

               "HTTP transport requires either OAUTHPROXYENABLE=true "

               "or MCPATLASSIANSINGLE_USER=true (acknowledges that env "

               "credentials will be used for any incoming request)."

           )

  1. Even with SINGLEUSERMODE, bind the HTTP transport to 127.0.0.1

     by default unless the operator overrides with an explicit

     MCPATLASSIANBIND_PUBLIC=true.

  1. 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).

  1. 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.

Package Versions Affected

Package Version
patch Availability
No items found.

Automatically patch vulnerabilities without upgrading

Fix Without Upgrading
Detect compatible fix
Apply safe remediation
Fix with a single pull request

CVSS Version

Severity
Base Score
CVSS Version
Score Vector
C
H
U
10
-
3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
C
H
U
0
-
3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
C
H
U
10
-
3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N

Related Resources

No items found.

References

https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-wrhw-j3f9-8vc6, https://nvd.nist.gov/vuln/detail/CVE-2026-77244, https://github.com/sooperset/mcp-atlassian/pull/1448, https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460, https://github.com/sooperset/mcp-atlassian, https://github.com/sooperset/mcp-atlassian/releases/tag/v0.22.0

Severity

10

CVSS Score
0
10

Basic Information

Base CVSS
10
EPSS Probability
0.00286%
EPSS Percentile
0.19822%
Introduced Version
0,0.11.0,0.12.0,0.21.0
Fix Available
0.22.0

Fix Critical Vulnerabilities Instantly

Secure your app without upgrading.
Fix Without Upgrading