# FastMCP OAuth Proxy token reuse across MCP servers

FastMCP OAuth Proxy ignores the client's `resource` parameter and issues tokens bound to its own base_url, letting a malicious MCP server harvest tokens and replay them against other MCP servers sharing the same AS.

- Payout: Research
- Severity: high
- Program: FastMCP
- Found by: an7y
- Disclosed: 2026-03-16
- CVE: CVE-2025-69196
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-5h2m-4q8j-pqpj
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-5h2m-4q8j-pqpj
- Tags: area:web domain:api domain:web impact:ato impact:internal-access kind:research platform:ghsa sev:high tech:jwt tech:oauth tech:python technique:source-review vuln:access-control vuln:auth-bypass

**Target:** FastMCP OAuth Proxy (JWT issuer/audience configuration)
**Root cause:** JWTIssuer is initialized with a static audience derived from the proxy's own base_url instead of the client-supplied `resource` parameter, so tokens carry no information about which MCP server they were issued for and receivers cannot verify audience binding.
**Steps:**
1. Set up PoC environment from the advisory attachment.
2. Put a controlled GitHub App client_id/client_secret into `mcp-server-proxy.py`.
3. Run benign MCP server with OAuth Proxy (GitHubProvider): `python3 mcp-server-proxy.py`.
4. Run malicious AS: `python3 mal_auth_server.py`.
5. Run malicious MCP server: `python3 attacker_server.py`.
6. Connect client to malicious MCP server: `python3 client.py` and complete OAuth flow.
7. Observe malicious server logs: request to benign MCP server with stolen token returns 200.
**How found:** While testing the OAuth Proxy implementation, the researcher noticed the server did not honor the `resource` parameter in authorization/token requests, then read oauth_proxy.py and found JWTIssuer audience hardcoded to base_url.
**Exploitation:** Attacker stands up a malicious MCP server that advertises the benign OAuth Proxy AS as its own authorization server. Victim completes OAuth against the AS; the token issued is bound to the proxy base_url, not the malicious server's resource. Attacker extracts the token at the malicious server and replays it against other benign MCP servers using the same AS, gaining access to their tools and resources.
**Escalation:** Single stolen token grants access to every MCP server sharing the same OAuth Proxy AS, not just the one the victim intended.
**Insight:** In OAuth-fronted MCP servers, check whether the JWT `aud` is derived from the request's `resource` parameter or from static config; a static audience means any server sharing the AS can replay tokens. Test by pointing a rogue MCP server at the victim's AS and replaying the harvested token.

