Skip to content

[Vulnerability] modelcontextprotocol/python-sdk: OAuth Issuer Validation Bypass / Man-in-the-Middle Attack #8

Open

Description

@github-actions

Potential Security Vulnerability Detected

Repository: modelcontextprotocol/python-sdk
Commit: d060b36
Author: Max
Date: 2026-09-02T15:51:22Z

Commit Message

Validate the authorization server metadata issuer on every discovery path (#3398)

Pull Request

PR: #3398 - Validate the authorization server metadata issuer on every discovery path
Labels: None

Description:
The OAuth client now decides which issuer it expects before it fetches any authorization server metadata, and checks what follows against that one value: the metadata's issuer (RFC 8414 section 3.3) on the legacy no-PRM path as well as the PRM path, the SEP-2352 binding of stored credentials, and the issuer recorded on a new registration. A 403 scope step-up goes through the same discovery when it holds no metadata yet, and the two machine-to-machine providers gain an optional issuer= so pre...

Analysis

Vulnerability Type: OAuth Issuer Validation Bypass / Man-in-the-Middle Attack
Severity: High

Description

The OAuth client was not validating the authorization server metadata's issuer field on the legacy discovery path (when no Protected Resource Metadata is available). An attacker could perform a man-in-the-middle attack by serving malicious authorization server metadata from a different issuer, allowing credential binding to be re-evaluated against an attacker-controlled issuer and potentially intercepting tokens or performing authentication bypass. The patch enforces RFC 8414 section 3.3 compliance by validating the issuer matches the expected value on all discovery paths.

Affected Code

// Before patch: issuer was not validated on legacy path
1. **Discovery.** It reads the `WWW-Authenticate` header, fetches the server's Protected Resource Metadata from `/.well-known/oauth-protected-resource`, learns which authorization server protects this resource, and fetches *that* server's metadata.

Proof of Concept

1. Client requests MCP server resource, gets 401 Unauthorized
2. MCP server has no Protected Resource Metadata (legacy path)
3. Client fetches authorization server metadata from the MCP server's origin: http://mcp-server.example.com/.well-known/oauth-authorization-server
4. Attacker intercepts and returns: {"issuer": "http://attacker.com", "token_endpoint": "http://attacker.com/token", ...}
5. Before patch: Client accepts this metadata without checking if issuer matches the expected value
6. Client's credential binding gets re-evaluated against attacker's issuer, tokens sent to attacker
7. After patch: Client validates issuer field and rejects metadata where issuer != MCP server's origin, preventing the attack

This issue was automatically created by Vulnerability Spoiler Alert.
Detected at: 2026-10-06T07:14:23.409Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions