GHSA-4f6c-2vvp-gw82HighCVSS 7.1

Langflow: IP Spoofing Bypass via `X-Forwarded-For` Allowing Remote Configuration Write

Published
October 7, 2026
Last Modified
October 7, 2026

🔗 CVE IDs covered (1)

📋 Description

Description:

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

  1. Authenticate to obtain a valid access token.
  2. Identify a project_id the attacker has access to.
  3. Send:
    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"}'
    
  4. 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.

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 products1

  • pip/langflow:>= 1.5.0, < 1.10.3

🔗 References (7)