GHSA-33f5-2c5q-wgwj
RMCP: Missing Resource Field Validation in OAuth Protected Resource Metadata Discovery
Details
### Summary The `rmcp` library does not validate the `resource` parameter in OAuth Protected Resource metadata (RFC 9728), allowing a malicious MCP server to redirect OAuth flows to a legitimate authorization server and steal the resulting access tokens.
### Details RFC 9728 specifies two MUST requirements for resource parameter validation: - Section 7.3: the client MUST ensure that the resource identifier URL it is using as the prefix for the metadata request exactly matches the `resource` value in the returned metadata document. - Section 3.3: if the `resource` value returned is not identical to the URL the client used, the data MUST NOT be used.
In the current implementation (`crates/rmcp/src/transport/auth.rs`), the `ResourceServerMetadata` struct (lines 390–394) does not include a resource field: ```rust struct ResourceServerMetadata { authorization_server: Option<String>, authorization_servers: Option<Vec<String>>, scopes_supported: Option<Vec<String>>, } ``` And discover_oauth_server_via_resource_metadata() (lines 1446–1465) proceeds without any resource URL validation.
#### Recommended fix 1. Add the `resource` field to the struct: ```rust struct ResourceServerMetadata { resource: Option<String>, // RFC 9728 REQUIRED field authorization_server: Option<String>, authorization_servers: Option<Vec<String>>, scopes_supported: Option<Vec<String>>, } ``` 2. Add validation logic after fetching metadata: ```rust let Some(resource_metadata) = self .fetch_resource_metadata_from_url(&resource_metadata_url) .await? else { return Ok(None); }; // RFC 9728: validate that the resource identifier matches our target server if let Some(resource) = &resource_metadata.resource { if resource.trim_end_matches('/') != self.base_url.as_str().trim_end_matches('/') { return Err(AuthError::MetadataError(format!( "Resource metadata mismatch: expected '{}', got '{}'", self.base_url, resource ))); } } ```
### PoC
1. Attacker sets up a malicious MCP server at `fake-mcp.com/mcp`. 2. At `fake-mcp.com/mcp/.well-known/oauth-protected-resource`, the attacker serves metadata declaring: - resource: `real-mcp.com/mcp` (the legitimate server) - authorization_servers: the legitimate authorization server(s) of `real-mcp.com/mcp`
3. Victim configures any MCP client using `rmcp` to connect to `fake-mcp.com/mcp`.
4. `rmcp` fetches the protected resource metadata and, without validating that the `resource` field (`real-mcp.com/mcp`) differs from the configured server (`fake-mcp.com/mcp`), initiates an OAuth flow with the legitimate authorization server. 5. The victim sees a legitimate authorization prompt and completes the flow. 6. The resulting access token — valid for `real-mcp.com/mcp` — is sent to `fake-mcp.com/mcp` in subsequent requests. 7. The attacker captures the token and can impersonate the victim on `real-mcp.com/mcp`.
### Impact This is an access token theft vulnerability via OAuth resource metadata spoofing. All MCP clients built on `rmcp` that rely on OAuth-protected MCP servers are affected. An attacker who tricks a user into connecting to a malicious MCP server can steal valid access tokens for any legitimate MCP server, enabling full impersonation of the victim.
#### Credit Jian Cui, Minsun Shim, Zhou Li, Xiaojing Liao University of Illinois Urbana-Champaign (UIUC) University of California, Irvine (UCI)
Are you affected?
Enter the version of the package you're using.
Affected packages
References
- https://github.com/modelcontextprotocol/rust-sdk/security/advisories/GHSA-33f5-2c5q-wgwj[WEB]
- https://nvd.nist.gov/vuln/detail/CVE-2026-63127[ADVISORY]
- https://github.com/modelcontextprotocol/rust-sdk/pull/937[WEB]
- https://github.com/modelcontextprotocol/rust-sdk/commit/c1a8b29ff2cc45e7820b900dae42cbb4958089ec[WEB]
- https://github.com/modelcontextprotocol/rust-sdk[PACKAGE]
- https://github.com/modelcontextprotocol/rust-sdk/releases/tag/rmcp-v2.0.0[WEB]