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:
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
- Add the
resource field to the struct:
struct ResourceServerMetadata {
resource: Option<String>, // RFC 9728 REQUIRED field
authorization_server: Option<String>,
authorization_servers: Option<Vec<String>>,
scopes_supported: Option<Vec<String>>,
}
- Add validation logic after fetching metadata:
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
-
Attacker sets up a malicious MCP server at fake-mcp.com/mcp.
-
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
-
Victim configures any MCP client using rmcp to connect to fake-mcp.com/mcp.
-
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.
-
The victim sees a legitimate authorization prompt and completes the flow.
-
The resulting access token — valid for real-mcp.com/mcp — is sent to fake-mcp.com/mcp in subsequent requests.
-
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.
References:
[1] https://datatracker.ietf.org/doc/rfc9728/
[2] https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization
Credit
Jian Cui, Minsun Shim, Zhou Li, Xiaojing Liao
University of Illinois Urbana-Champaign (UIUC)
University of California, Irvine (UCI)
Summary
The
rmcplibrary does not validate theresourceparameter 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:
resourcevalue in the returned metadata document.resourcevalue 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), theResourceServerMetadatastruct (lines 390–394) does not include a resource field:And discover_oauth_server_via_resource_metadata() (lines 1446–1465) proceeds without any resource URL validation.
Recommended fix
resourcefield to the struct:PoC
Attacker sets up a malicious MCP server at
fake-mcp.com/mcp.At
fake-mcp.com/mcp/.well-known/oauth-protected-resource, the attacker serves metadata declaring:real-mcp.com/mcp(the legitimate server)real-mcp.com/mcpVictim configures any MCP client using
rmcpto connect tofake-mcp.com/mcp.rmcpfetches the protected resource metadata and, without validating that theresourcefield (real-mcp.com/mcp) differs from the configured server (fake-mcp.com/mcp), initiates an OAuth flow with the legitimate authorization server.The victim sees a legitimate authorization prompt and completes the flow.
The resulting access token — valid for
real-mcp.com/mcp— is sent tofake-mcp.com/mcpin subsequent requests.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
rmcpthat 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.References:
[1] https://datatracker.ietf.org/doc/rfc9728/
[2] https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization
Credit
Jian Cui, Minsun Shim, Zhou Li, Xiaojing Liao
University of Illinois Urbana-Champaign (UIUC)
University of California, Irvine (UCI)