~/f4n6 $ grep -r "Official MCP Python SDK Flaw Can Let Malicious Servers Steal OAuth Credentials" ./investigations/ --include="*.md"

Official MCP Python SDK Flaw Can Let Malicious Servers Steal OAuth Credentials

Jeff Davies 29 Sep 2026 6 min read

1. Executive summary

The official Model Context Protocol (MCP) Python SDK, used to build MCP clients and servers, failed to validate the authorization server named by the MCP server it was connecting to, so a malicious server could redirect the OAuth token exchange to an endpoint it controlled. Affected clients sent their client secret, authorization code, and PKCE proof key to that attacker endpoint, and the attacker could then redeem them for a valid access token carrying whatever permissions the application held. The flaw is rated high (7.5) for the two machine-to-machine providers and 6.5 for the interactive provider; no CVE had been assigned as of September 29. Any EMEA financial services firm running an MCP client over HTTP against a server it does not fully control, with credentials for a real login service, is exposed, and the client secret is long-lived, so it keeps working until rotated. The fix is in SDK versions 1.30.0 and 2.2.0, but for two providers the upgrade alone is not sufficient.

2. Regulatory framing

Article Trigger (the fact in this item) Practical impact
DORA Art. 28: ICT third-party risk — general principles The exposure arises from an open-source SDK dependency (the official MCP Python SDK) that the firm does not control, and the fix requires a version upgrade plus configuration change. The SDK is an ICT third-party dependency; firms must track the version in use, assess the risk of connecting to MCP servers outside their control, and record the remediation.
DORA Art. 30: key contractual provisions with ICT third-party providers Where an MCP server is operated by a third party, the client's OAuth credentials are sent to that server's nominated authorization endpoint. Contracts covering MCP server provision should address the authorization endpoint the server may name and the firm's right to verify it.
NIS2 Art. 21(2)(d): supply chain security measures The vulnerability is in a software component in the supply chain (the MCP Python SDK), and exploitation depends on connecting to an untrusted MCP server. Supply chain security measures must cover the SDK dependency and the trust boundary applied to MCP servers the firm connects to.

3. Technical analysis & attack chain

The flaw is in the MCP Python SDK's OAuth client flow. When an MCP client needs to log in, it asks the MCP server it is connecting to where its authorization server can be found. On affected versions the SDK did not always check that answer before acting on it.

Confirmed attack chain:

  1. An application built on the affected SDK acts as an MCP client over HTTP and connects to an MCP server the operator does not fully control, while holding credentials for a real login service.
  2. The client asks the MCP server where its authorization server is.
  3. The malicious server answers with an authorization server of the attacker's choosing. It can either name the attacker's own server outright, or serve login details that name the user's real service while directing the credential exchange elsewhere.
  4. The SDK, without validating the answer, sends the client secret, the authorization code, and the PKCE proof key to the attacker-controlled token endpoint.
  5. The attacker redeems the authorization code and proof key at the real login service and requests a valid access token. Cycode demonstrated the full exchange in a test.
  6. The resulting token carries whatever permissions the application was granted. The client secret is long-lived and keeps working until it is changed.

The PKCE proof key is a one-time value designed to stop a stolen authorization code from being reused. Handing it over alongside the code defeats that protection, so the attacker does not need to replay a captured code; the exchange completes with the stolen material.

Affected providers are OAuthClientProvider, ClientCredentialsOAuthProvider, PrivateKeyJWTOAuthProvider, and the deprecated 1.x RFC7523OAuthClientProvider. The two machine-to-machine providers (ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider) need no sign-in and no person present, which is why they carry the higher 7.5 rating. With the interactive provider, a person still has to approve a sign-in, but Cycode says the page they approve is the genuine login page, so nothing looks wrong; that path is rated 6.5.

Not affected: MCP servers built with the SDK, local (stdio) clients, and clients that attach their own tokens.

The fix is in versions 1.30.0 (1.x line) and 2.2.0 (2.x line). In the fixed versions the client works out which login service it expects before fetching any details and refuses any that name a different one. For ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider, upgrading changes nothing until issuer= is also passed to name the login service those credentials belong to; without it they still follow whichever server the MCP server points them at. On 1.30.0 the warning about the missing issuer= is a standard deprecation warning, which Python hides by default, so it is easy to miss. The deprecated RFC7523OAuthClientProvider has no issuer= option at all, so clients on it must move to one of the other two providers.

Disclosure timeline: the issuer checks shipped in the 1.30.0 and 2.2.0 release notes on September 7, listed under behavior changes rather than as a security fix. The advisory followed on September 28, the same day Cycode published its write-up.

Confidence caveats. The technical mechanism and the affected/not-affected lists come from the SDK maintainers' advisory as reported. The demonstration of the full credential exchange and the claim that the resulting token carries the application's granted permissions rest on Cycode's testing, a single source; treat the token-permission scope as reported rather than independently corroborated. No CVE identifier was assigned as of September 29, so there is no CVSS vector or CWE classification to cite; the 7.5 and 6.5 ratings are the maintainers' own, not NVD-assigned. No exploitation in the wild is reported in the source material.

4. Mitigation & containment

P1 (within 24h)

  • Identify every application that uses the MCP Python SDK as an MCP client over HTTP with one of OAuthClientProvider, ClientCredentialsOAuthProvider, PrivateKeyJWTOAuthProvider, or the deprecated 1.x RFC7523OAuthClientProvider. Check the installed version (pip show mcp or the lockfile) and flag anything below 1.30.0 on the 1.x line or below 2.2.0 on the 2.x line.
  • For any client that may already have connected to an untrusted MCP server, rotate its client secret and revoke its tokens at the login service. The client secret is long-lived, so rotation is the only way to cut off a stolen one.
  • Until the upgrade is in place, restrict affected clients to MCP servers the firm fully controls. On older versions there is no other workaround.

P2 (within 72h)

  • Upgrade to 1.30.0 on the 1.x line or 2.2.0 on the 2.x line.
  • For ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider, pass issuer= to name the login service those credentials belong to. The upgrade alone does not fix these two providers.
  • Move any client off the deprecated RFC7523OAuthClientProvider to one of the other two providers; it has no issuer= option.
  • After upgrading, clear any stored OAuth client registrations once. Older registrations are not tied to a login service and stay that way.

P3 (within 7 days)

  • Confirm the issuer= setting is present in configuration for the two machine-to-machine providers, since the deprecation warning that flags its absence is hidden by default on 1.30.0.
  • Add MCP server endpoints to the inventory of third-party dependencies and record the SDK version and provider in use for each client.

5. Indicators of compromise

No indicators of compromise available in the source material. The source names no hashes, domains, IP addresses, file paths, or other atomic artefacts. The observable behaviour is the credential flow itself.

Behavioural indicators:

behaviour where to observe confidence
MCP client sends client secret, authorization code, and PKCE proof key to a token endpoint that differs from the expected authorization server OAuth client logs, outbound HTTP/TLS metadata from the MCP client host high (mechanism confirmed by maintainers' advisory)
MCP server response names an authorization server different from the one the client expects MCP client request/response logs high
Interactive sign-in approval page is the genuine login page while credentials go elsewhere Browser and OAuth flow logs medium (single-sourced to Cycode)
Access token issued to the application is redeemed from an unexpected source Login service token issuance and redemption logs medium (single-sourced to Cycode)

6. Detection

The source material contains no distinctive strings, command-line flags, mutex names, file paths, registry keys, or hard-coded values belonging to the threat itself. It names SDK providers and a configuration parameter, which are product and configuration identifiers, not artefacts of a malicious file or traffic. A YARA or Sigma rule built from those would detect reporting about the flaw, not the flaw.

Insufficient indicators to author detection rules.

7. Sources

  • The Hacker News, "Official MCP Python SDK Flaw Can Let Malicious Servers Steal OAuth Credentials", https://thehackernews.com/2026/09/official-mcp-python-sdk-flaw-can-let.html, 2026-09-29.
  • MCP Python SDK maintainers' security advisory, referenced in the above; no direct URL provided in the source material.
  • Cycode, vulnerability report and demonstration, referenced in the above; no direct URL provided in the source material.

8. Adverse Trace position

Adverse Trace assesses this as a high-severity credential-theft flaw in a widely used SDK, with the practical risk concentrated in the two machine-to-machine OAuth providers where no human is present to notice anything wrong. The impact is bounded by what the affected application was granted, but the stolen client secret is long-lived, so the window stays open until rotation. We have no CISA KEV entry or NVD record to cite because no CVE was assigned as of September 29, and the 7.5 and 6.5 ratings are the maintainers' own. The demonstration of the full token exchange is single-sourced to Cycode; verify the token-permission scope against your own login service logs before treating it as confirmed. We will track CVE assignment and any exploitation reporting, and we will re-issue this advisory if either appears. Clients should treat the P1 rotation and the issuer= configuration change as the two actions that close the exposure, and confirm both are in place rather than relying on the version upgrade alone.


Read the original source →

Published via PulseTrace — Adverse Trace threat intelligence.

Post this to LinkedIn
Formatting is converted automatically — headings, bullets, a link back & hashtags. Paste straight in.
J
Jeff Davies