GHSA-4F6C-2VVP-GW82
Vulnerability from github – Published: 2026-10-07 16:18 – Updated: 2026-10-07 16:18Description:
Summary
An IP spoofing vulnerability in the Model Context Protocol (MCP) configuration installation endpoint (POST /api/v1/mcp/project/{project_id}/install) allowed authenticated remote attackers to bypass the "local-only" access restriction. By sending a spoofed X-Forwarded-For: 127.0.0.1 header, an attacker could make the server treat the request as originating from localhost, letting them write/overwrite an MCP client configuration file on the server's filesystem.
Details
The vulnerability lived in the get_client_ip helper, used to enforce the local-only gate for install_mcp_config. It trusted the leftmost (fully client-controlled) entry of X-Forwarded-For unconditionally, with no check for whether the request had actually passed through a trusted proxy.
Vulnerable code (introduced by commit d3d06be8e5, first released in v1.5.0): src/backend/base/langflow/api/v1/mcp_projects.py
def get_client_ip(request: Request) -> str:
# Check for X-Forwarded-For header (common when behind proxies)
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
# The client IP is the first one in the list
return forwarded_for.split(",")[0].strip()
if request.client:
return request.client.host
return "255.255.255.255"
@router.post("/{project_id}/install")
async def install_mcp_config(
project_id: UUID,
body: MCPInstallRequest, # {client: str, transport: "sse" | "streamablehttp" | None}
request: Request,
current_user: CurrentActiveMCPUser,
):
client_ip = get_client_ip(request)
if not is_local_ip(client_ip):
raise HTTPException(status_code=500, detail="MCP configuration can only be installed from a local connection")
...
Correction vs. the original report: the request body accepted by this endpoint is MCPInstallRequest {client: str, transport: str | None} (src/backend/base/langflow/api/v1/schemas/__init__.py). There is no mcp_path field, and the destination path is never attacker-supplied. install_mcp_config resolves the write target itself, via get_config_path(body.client), to one of a fixed, small set of well-known per-OS developer-tool config paths under the server process's home directory: ~/.cursor/mcp.json (Cursor), ~/.codeium/windsurf/mcp_config.json (Windsurf), or the Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows/WSL). The impact is therefore "attacker-influenced content written into one of these fixed files," not an arbitrary-path write.
PoC
- Authenticate to obtain a valid access token.
- Identify a
project_idthe attacker has access to. - Send:
bash curl -X POST "http://<server-ip>:7860/api/v1/mcp/project/<project_id>/install" \ -H "Authorization: Bearer <token>" \ -H "X-Forwarded-For: 127.0.0.1" \ -H "Content-Type: application/json" \ -d '{"client": "cursor"}' - The server returns
200 OKand writes/overwrites~/.cursor/mcp.jsonon the host with an attacker-influenced MCP server entry, despite the request originating from a remote, non-local address.
Impact
Authenticated Remote Configuration Write to one of a fixed set of IDE/MCP client config files on the host. Could be leveraged to: - Inject a malicious MCP server definition into Cursor/Windsurf/Claude Desktop config, so a local developer who later opens that IDE on the host connects to an attacker-controlled MCP server. - Disrupt or corrupt the existing MCP configuration for those tools. - Bypass an intended network-boundary control ("local-only").
Status: already fixed
This exact bypass (single-line, comma-separated X-Forwarded-For spoofing, default configuration) is fixed as of:
- Fix PR: langflow-ai/langflow#13915 — "fix(security): stop trusting X-Forwarded-For for the MCP install locality check", landed as part of the broader hardening effort in langflow-ai/langflow#13530.
- Fix: get_client_ip now uses the real TCP peer (request.client.host) by default and ignores X-Forwarded-For entirely unless the operator has explicitly opted in via the rate_limit_trust_proxy setting (default False); when opted in, it takes the rightmost entry, mirroring langflow.services.rate_limit.service.get_client_ip.
- Released in: v1.11.0, and backported to v1.10.3 (langflow-ai/langflow#14071).
- Related follow-up: a narrower, related bypass — reachable only when an operator has explicitly set rate_limit_trust_proxy=true behind a proxy that emits X-Forwarded-For as repeated header lines rather than a single comma-separated line (e.g. HAProxy's option forwardfor) — was separately closed by langflow-ai/langflow#14425, released in v1.11.3. This does not affect default deployments (rate_limit_trust_proxy defaults to False).
- This report is a near-duplicate of GHSA-qvvj-g573-9638, which describes the same root cause and is fixed by the same PR.
Affected versions
- The vulnerable endpoint/helper was introduced in v1.5.0 (langflow-ai/langflow#8271, "add one click install to mcp servers on specific clients", 2025-07-08). Versions prior to v1.5.0 do not contain this endpoint and are not affected by this issue.
- Vulnerable: >= 1.5.0, < 1.10.3 (and < 1.11.0 on mainline).
- Fixed: v1.10.3 (backport) and v1.11.0 onward.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "langflow"
},
"ranges": [
{
"events": [
{
"introduced": "1.5.0"
},
{
"fixed": "1.10.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-105741"
],
"database_specific": {
"cwe_ids": [
"CWE-290",
"CWE-345"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-07T16:18:23Z",
"nvd_published_at": "2026-10-05T21:16:35Z",
"severity": "HIGH"
},
"details": "**Description**:\n### Summary\nAn IP spoofing vulnerability in the Model Context Protocol (MCP) configuration installation endpoint (`POST /api/v1/mcp/project/{project_id}/install`) allowed authenticated remote attackers to bypass the \"local-only\" access restriction. By sending a spoofed `X-Forwarded-For: 127.0.0.1` header, an attacker could make the server treat the request as originating from localhost, letting them write/overwrite an MCP client configuration file on the server\u0027s filesystem.\n\n### Details\nThe vulnerability lived in the `get_client_ip` helper, used to enforce the local-only gate for `install_mcp_config`. It trusted the leftmost (fully client-controlled) entry of `X-Forwarded-For` unconditionally, with no check for whether the request had actually passed through a trusted proxy.\n\n**Vulnerable code (introduced by commit `d3d06be8e5`, first released in v1.5.0):** `src/backend/base/langflow/api/v1/mcp_projects.py`\n\n```python\ndef get_client_ip(request: Request) -\u003e str:\n # Check for X-Forwarded-For header (common when behind proxies)\n forwarded_for = request.headers.get(\"X-Forwarded-For\")\n if forwarded_for:\n # The client IP is the first one in the list\n return forwarded_for.split(\",\")[0].strip()\n if request.client:\n return request.client.host\n return \"255.255.255.255\"\n\n\n@router.post(\"/{project_id}/install\")\nasync def install_mcp_config(\n project_id: UUID,\n body: MCPInstallRequest, # {client: str, transport: \"sse\" | \"streamablehttp\" | None}\n request: Request,\n current_user: CurrentActiveMCPUser,\n):\n client_ip = get_client_ip(request)\n if not is_local_ip(client_ip):\n raise HTTPException(status_code=500, detail=\"MCP configuration can only be installed from a local connection\")\n ...\n```\n\n**Correction vs. the original report:** the request body accepted by this endpoint is `MCPInstallRequest {client: str, transport: str | None}` (`src/backend/base/langflow/api/v1/schemas/__init__.py`). There is no `mcp_path` field, and the destination path is never attacker-supplied. `install_mcp_config` resolves the write target itself, via `get_config_path(body.client)`, to one of a fixed, small set of well-known per-OS developer-tool config paths under the server process\u0027s home directory: `~/.cursor/mcp.json` (Cursor), `~/.codeium/windsurf/mcp_config.json` (Windsurf), or the Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\\Claude\\claude_desktop_config.json` on Windows/WSL). The impact is therefore \"attacker-influenced content written into one of these fixed files,\" not an arbitrary-path write.\n\n### PoC\n1. Authenticate to obtain a valid access token.\n2. Identify a `project_id` the attacker has access to.\n3. Send:\n ```bash\n curl -X POST \"http://\u003cserver-ip\u003e:7860/api/v1/mcp/project/\u003cproject_id\u003e/install\" \\\n -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n -H \"X-Forwarded-For: 127.0.0.1\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\"client\": \"cursor\"}\u0027\n ```\n4. The server returns `200 OK` and writes/overwrites `~/.cursor/mcp.json` on the host with an attacker-influenced MCP server entry, despite the request originating from a remote, non-local address.\n\n### Impact\nAuthenticated Remote Configuration Write to one of a fixed set of IDE/MCP client config files on the host. Could be leveraged to:\n- Inject a malicious MCP server definition into Cursor/Windsurf/Claude Desktop config, so a local developer who later opens that IDE on the host connects to an attacker-controlled MCP server.\n- Disrupt or corrupt the existing MCP configuration for those tools.\n- Bypass an intended network-boundary control (\"local-only\").\n\n### Status: already fixed\nThis exact bypass (single-line, comma-separated `X-Forwarded-For` spoofing, default configuration) is fixed as of:\n- **Fix PR:** langflow-ai/langflow#13915 \u2014 \"fix(security): stop trusting X-Forwarded-For for the MCP install locality check\", landed as part of the broader hardening effort in langflow-ai/langflow#13530.\n- **Fix:** `get_client_ip` now uses the real TCP peer (`request.client.host`) by default and ignores `X-Forwarded-For` entirely unless the operator has explicitly opted in via the `rate_limit_trust_proxy` setting (default `False`); when opted in, it takes the **rightmost** entry, mirroring `langflow.services.rate_limit.service.get_client_ip`.\n- **Released in:** v1.11.0, and backported to v1.10.3 (langflow-ai/langflow#14071).\n- **Related follow-up:** a narrower, related bypass \u2014 reachable only when an operator has explicitly set `rate_limit_trust_proxy=true` behind a proxy that emits `X-Forwarded-For` as repeated header lines rather than a single comma-separated line (e.g. HAProxy\u0027s `option forwardfor`) \u2014 was separately closed by langflow-ai/langflow#14425, released in v1.11.3. This does not affect default deployments (`rate_limit_trust_proxy` defaults to `False`).\n- This report is a near-duplicate of GHSA-qvvj-g573-9638, which describes the same root cause and is fixed by the same PR.\n\n### Affected versions\n- The vulnerable endpoint/helper was introduced in **v1.5.0** (langflow-ai/langflow#8271, \"add one click install to mcp servers on specific clients\", 2025-07-08). Versions prior to v1.5.0 do not contain this endpoint and are not affected by this issue.\n- **Vulnerable:** \u003e= 1.5.0, \u003c 1.10.3 (and \u003c 1.11.0 on mainline).\n- **Fixed:** v1.10.3 (backport) and v1.11.0 onward.",
"id": "GHSA-4f6c-2vvp-gw82",
"modified": "2026-10-07T16:18:23Z",
"published": "2026-10-07T16:18:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langflow-ai/langflow/security/advisories/GHSA-4f6c-2vvp-gw82"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-105741"
},
{
"type": "WEB",
"url": "https://github.com/langflow-ai/langflow/pull/13915"
},
{
"type": "WEB",
"url": "https://github.com/langflow-ai/langflow/commit/1f39a4b9d62c9dfa1b21fa7f85e23a180c351b72"
},
{
"type": "WEB",
"url": "https://github.com/langflow-ai/langflow/commit/94859df33acd70b2a1f816e26d68f5e89a7e5639"
},
{
"type": "PACKAGE",
"url": "https://github.com/langflow-ai/langflow"
},
{
"type": "WEB",
"url": "https://github.com/langflow-ai/langflow/releases/tag/v1.10.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "Langflow: IP Spoofing Bypass via `X-Forwarded-For` Allowing Remote Configuration Write"
}
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.
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.