GHSA-4596-2p6p-28cv: Medium severity pip/mcp-atlassian vulnerability

Published Sep 22, 2026
·
Updated

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)

Affected Software

1 affected componentFixes available
pip/mcp-atlassian<0.22.0
0.22.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/mcp-atlassian to a version that resolves this vulnerability.

    Fixed in 0.22.0
  2. Configuration

    Create the token directory with mode 0o700 and write the token file with owner-only read/write permissions (0o600); also change existing token directories to 0o700.

    mcp-atlassian OAuth fallback file storage filesystem permissions = OAuth token directory 0o700; OAuth token file 0o600

Event History

Sep 22, 2026
Advisory Published
via GitHub·08:35 PM
Data Sourced
via GitHub·08:35 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Who is realistically exposed to credential theft?

Users who use the OAuth token fallback file storage on a shared system are exposed, because other local users may be able to read token files under the user's .mcp-atlassian directory. The files can contain access tokens, refresh tokens, and cloud IDs for Atlassian services.

2

What does an attacker need to exploit this issue?

An attacker needs local access to the same system and permission to read the victim's token files. No user interaction is required once the files are readable; the disclosed tokens can provide Atlassian API credentials for the victim's Jira and Confluence data.

3

Are default filesystem permissions affected?

Yes. The token directory and files are created without explicit restrictive permissions, so they use the system umask; the advisory notes typical Linux modes of 0755 for the directory and 0644 for the token file, making the file world-readable.

4

How can I check whether existing tokens may be exposed?

Inspect the .mcp-atlassian directory in each affected user's home directory and check permissions on OAuth token files. Files created with permissions that allow other local users to read them may expose the stored access token, refresh token, and cloud ID.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203