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

mcp-atlassian's HTTP transport accepts any or no token, so unauthenticated network attackers reach Jira/Confluence tools running with the operator's env-var API credentials.

- Payout: Research
- Severity: critical
- Program: mcp-atlassian
- Found by: crazydude123
- Disclosed: 2026-09-22
- CVE: CVE-2026-77244
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-wrhw-j3f9-8vc6
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-wrhw-j3f9-8vc6
- Tags: area:ai domain:ai domain:api impact:data-leak impact:integrity kind:research platform:ghsa sev:critical tech:python technique:source-review vuln:access-control vuln:auth-bypass vuln:misconfiguration

**Target:** MCP server HTTP transport (streamable-http) with env-var Atlassian credentials
**Root cause:** AtlassianOpaqueTokenVerifier.verify_token() returns an AccessToken for any non-empty string, and with the OAuth proxy disabled no auth provider is built, so FastMCP accepts unauthenticated requests; missing Authorization headers are not rejected and fetchers fall back to JiraConfig.from_env() credentials.
**Steps:**
1. Start server with JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN, MCP_TRANSPORT=streamable-http, PORT=3000, leaving OAUTH_PROXY_ENABLE_ENV default false.
2. Confirm verifier accepts anything: `AtlassianOpaqueTokenVerifier(required_scopes=['read:jira-work'])` then `asyncio.run(v.verify_token('anything-at-all'))` returns non-None with token stored.
3. Send unauthenticated JSON-RPC: `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"}}}'`
4. Observe the Jira issue returned using the server's env-var token; repeat with `-H "Authorization: Bearer anything-at-all"` for the same result.
**How found:** Code review of the auth path: noticed the verifier docstring admits accepting non-empty tokens, traced that _build_auth_provider() returns None by default, that UserTokenMiddleware doesn't reject missing Authorization headers, and that fetchers fall back to from_env() credentials — composing into unauthenticated access.
**Exploitation:** Any party with network reach to the HTTP transport (Docker port mapping, LB without auth, internal network, exposed tunnel) sends a tools/call JSON-RPC request with no or garbage Authorization header; tool handlers find no user token in scope state and use the operator's env credentials, granting full Jira/Confluence read-write under the operator's identity, with audit logs attributing actions to the operator.
**Escalation:** Persistence via creating Jira webhooks, automation rules or Confluence integrations; pivot to secrets in attachments and Confluence pages.
**Insight:** In MCP/agent servers, trace the full credential-resolution chain: a permissive token verifier is only exploitable because the middleware tolerates absent headers and the tool layer silently falls back to server-side env credentials — check that fallback path, not just the verifier.

