flawopen.com/Teardowns/cve-2026-105697-langflow-mcp-stdio-command-injection
CVE-2026-105697: Langflow MCP Command Injection
Before 1.10.3, Langflow passed MCP stdio configuration to a shell-backed process. On an exposed instance with LANGFLOW_AUTO_LOGIN=true, an attacker could obtain a token without credentials; with auto-login disabled, an authenticated non-admin could still reach host command execution. Version 1.10.3 validates every path, including immediately before spawn, and launches without a shell.[1], [2], [3]
Imagine a clerk given a sentence where punctuation can change the job: one mark can turn a delivery instruction into a second order. Langflow sent the configured command through a shell that interprets such syntax as control, instead of treating the executable and arguments as separate data. The fix validates the permitted command and launches it directly without a shell.[1], [2]
Core Concepts & Subsystem Terms
MCP stdio transport- Starts an MCP server as a local child process and exchanges protocol messages over standard input and output.[1], [2]
Command injection (CWE-78)- Externally influenced text alters the operating-system command sent to a shell or process launcher.[1], [5]
Framework mappings
- CWE reference for the weakness described in this guide: CWE-78
Root Cause Analysis
Before 1.10.3, MCP stdio setup built a command string from configured command and arguments, then launched bash -c with that string. The settings API gained allowlist and argument/environment checks in 1.9.0, but embedded MCP Tools flow or tweak configurations could bypass that model validation because the execution sink had no shared policy. Langflow 1.10.3 centralizes validation in lfx.base.mcp.security.validate_mcp_stdio_config, applies it to API, flow, tweak, legacy component, and pre-spawn paths, and removes the shell wrapper. CVE-2026-105697 is CWE-78, CVSS 3.1 9.9. With auto-login disabled, exploitation still requires an authenticated non-admin user able to configure or execute the relevant MCP path; default auto-login can make exposed development instances reachable without credentials.[1], [2], [4], [5]
Step-by-Step Attack Flow
User reaches an MCP configuration path
The advisory describes access through MCP server settings or a flow using the MCP Tools component. On an exposed instance with the development-only default LANGFLOW_AUTO_LOGIN=true, an attacker could obtain a token without credentials. With auto-login disabled, an authenticated non-admin can still be sufficient.[1]
Configuration reaches process setup
The command and arguments become command_str while Langflow prepares an MCP stdio connection.[1]
Shell wrapper interprets the string
Before 1.10.3, the launch path used bash -c; execution occurred when Langflow tried to connect, before a successful MCP handshake.[1]
Entry-point checks are incomplete
Version 1.9.0 validates REST settings, but flow and tweak configurations can bypass that model-level validation.[1]
Host process runs with Langflow privileges
The command executes as the Langflow service user. The UI may still report that the MCP server failed to start.[1]
Source Code: Flaw vs. Secure Implementation
The vulnerable excerpt shows the shell-backed launch shape documented by the advisory, not a runnable exploit. The hardened excerpt is a source-shaped reduction of the 1.10.3 design: a shared policy validates the configuration at every entry point and again immediately before spawning, while the process API receives executable and arguments separately. Direct spawning removes shell interpretation, but does not alone make every allowed executable or package safe; operators must also apply package-source and isolation controls appropriate to their deployment.[1], [2]
Source excerpt: lfx/base/mcp/util.py — focused source-shaped excerpts
# Affected launch shape from the advisory (focused excerpt):
server_params = StdioServerParameters(
command="bash",
args=["-c", f"exec {command_str}"],
env=env_data,
)
# 1.10.3 source-shaped reduction: validate the policy, then spawn directly.
command_parts = shlex.split(command_str)
if not command_parts:
raise ValueError("MCP stdio command is required")
command, args = command_parts[0], command_parts[1:]
validate_mcp_stdio_config(command, args, env_data) # Repeat at the execution boundary.
server_params = StdioServerParameters(
command=command,
args=args,
env=env_data,
) # No shell wrapper. The shared validator rejects disallowed configurations.
Engineering & System Hardening Checklist
- ✓Upgrade
langflowto 1.10.3+,langflow-baseto 0.10.3+, andlfxto 1.10.3+; verify deployed package versions.[1], [3] - ✓Do not treat the 1.9.0 REST-model checks as a complete fix; review embedded flow and tweak MCP configurations on older releases.[1]
- ✓Validate command, arguments, environment, package source, and isolation policy at every entry point and immediately before process creation.[1], [2]
- ✓Pass executable and argument vector separately to the process API; avoid shell command-string construction.[2]
- ✓For multi-tenant deployments, pin allowed
npx/uvxpackages and enable the documented interpreter and Docker hardening settings.[1] - ✓FlawOpen incident-response recommendation: Review who could configure or execute MCP stdio connections while vulnerable versions were exposed; investigate host telemetry for unexpected Langflow child processes.[1]
- ✓Add regression coverage for REST, embedded-flow, tweak, legacy-component, and pre-spawn paths; assert rejected configurations never create a process.[2]