
مجموعة أدوات للكشف والاستغلال لثغرة CVE-2026-39987، وهي ثغرة تنفيذ تعليمات برمجية عن بُعد (RCE) قبل المصادقة في دفاتر Marimo. تتضمن ماسحًا بلغة Python وسكربت Nmap NSE لتحديد الحالات المعرضة للخطر عبر فحوصات نقطة نهاية WebSocket.
ثغرة تنفيذ تعليمات برمجية عن بُعد قبل المصادقة في Marimo، وهو دفتر ملاحظات Python مفتوح المصدر لعلوم البيانات والذكاء الاصطناعي/تعلّم الآلة. نقطة نهاية WebSocket الطرفية (/terminal/ws) تتجاوز التحقق من المصادقة بالكامل، بينما نقطة نهاية دفتر الملاحظات المجاورة (/ws) تفرضها بشكل صحيح. يمكن لمهاجم غير مصادق الاتصال بـ /terminal/ws والحصول على صدفة PTY تفاعلية كاملة على النظام المضيف بدون أي بيانات اعتماد.
تم استغلالها في البرية خلال 10 ساعات من الإفصاح عنها. سرق المهاجمون بيانات اعتماد AWS في أقل من 3 دقائق.
تؤثر على Marimo <= 0.20.4. تم إصلاحها في Marimo 0.23.0.
| الحقل | التفاصيل |
|---|---|
| معرف CVE | CVE-2026-39987 |
| البائع | مشروع Marimo |
| المنتج | Marimo (دفتر ملاحظات Python) |
| الإصدارات المتأثرة | <= 0.20.4 |
| CVSS v3.1 | 9.3 (حرجة) |
| CWE | CWE-306 — غياب المصادقة لوظيفة حرجة |
| متجه الهجوم | الشبكة |
| المصادقة | غير مطلوبة |
| تفاعل المستخدم | لا شيء |
| نضج الاستغلال | مستغلة بنشاط في البرية |
| الوقت حتى الاستغلال | ~10 ساعات بعد الإفصاح |
| تم إصلاحها في | Marimo 0.23.0 |
Marimo هو دفتر ملاحظات Python تفاعلي مفتوح المصدر مصمم كبديل حديث لـ Jupyter. وهو مبني لعلوم البيانات، وتجارب الذكاء الاصطناعي/تعلّم الآلة، وتحليل البيانات التفاعلي. تشمل ميزاته الرئيسية تتبع التبعيات تلقائيًا، والتنفيذ القابل للتكرار، وتجربة مطور أنظف مقارنة بدفاتر الملاحظات التقليدية.
يكتسب Marimo زخمًا سريعًا في مجتمع Python والذكاء الاصطناعي/تعلّم الآلة، خاصة بين الممارسين الذين يريدون سير عمل دفاتر ملاحظات أكثر تنظيماً مما يوفره Jupyter.
مثل جميع بيئات دفاتر الملاحظات، عادةً ما يكون لدى مثيلات Marimo وصول إلى موارد حساسة: بيانات اعتماد السحابة (AWS، GCP، Azure)، وسلاسل اتصال قواعد البيانات، ومفاتيح API لخدمات الذكاء الاصطناعي (OpenAI، Anthropic، وغيرها)، والوصول إلى الشبكة الداخلية. على عكس تطبيقات الويب التقليدية، تم تصميم دفاتر الملاحظات لتنفيذ تعليمات برمجية عشوائية. هذا هو غرضها الأساسي.
هذا المزيج يجعل أي تجاوز للمصادقة في بيئة دفتر ملاحظات مدمرًا بشكل خاص.``` Typical Marimo Deployment:
┌──────────────┐ ┌────────────────────────────────┐ │ │ HTTP │ Marimo Server │ │ Browser │────────>│ │ │ (User) │ │ ┌──────────────────────────┐ │ │ │<────────│ │ /ws (Notebook) │ │ └──────────────┘ WS │ │ ✅ validate_auth() │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ /terminal/ws │ │ │ │ ❌ NO AUTH CHECK │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ Python Environment │ │ │ │ .env files │ │ │ │ AWS credentials │ │ │ │ API keys │ │ │ └──────────────────────────┘ │ └────────────────────────────────┘
---
## الغوص العميق في الثغرة
### نقطتا نهاية WebSocket
ينفّذ خادم Marimo نقاط نهاية WebSocket متعددة لميزات مختلفة. الفرق الحاسم بين نقطتي النهاية الرئيسيتين هو وجود (أو غياب) فحص المصادقة:```
Authentication Flow Comparison:
/ws (Notebook WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ validate_auth │───>│ Accept │───>│ Notebook │
└─────────┘ └───────┬───────┘ └──────────┘ └───────────┘
│
❌ Reject if
not authenticated
/terminal/ws (Terminal WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ Check mode & │───>│ Accept │───>│ PTY Shell │
└─────────┘ │ platform only │ └──────────┘ └───────────┘
└───────────────┘
⚠️ No auth check!
Anyone gets a shell!
نقطة نهاية دفتر الملاحظات (/ws) تستدعي بشكل صحيح validate_auth() للتحقق من هوية المستخدم قبل قبول اتصالات WebSocket. هذا هو السلوك الأمني المتوقع.
نقطة نهاية الطرفية (/terminal/ws) تتحقق فقط مما إذا كان الخادم في وضع التشغيل وما إذا كانت المنصة تدعم وظيفة الطرفية. إنها لا تستدعي أبدًا validate_auth(). بعد اجتياز هذه الفحوصات الأساسية، تقبل الاتصال وتنشئ جلسة PTY (طرفية زائفة) كاملة.```python
async def websocket_connect(self, message): await self.validate_auth() # ✅ Checks authentication await self.accept() # ... notebook communication
async def websocket_connect(self, message): if not self.is_running_mode(): # Only checks mode await self.close() return if not self.is_platform_supported(): # Only checks platform await self.close() return await self.accept() # ❌ No auth! Anyone gets a shell # ... PTY shell creation
هذا هو **CWE-306: Missing Authentication for Critical Function** (غياب المصادقة للوظائف الحرجة). أخطر نقطة نهاية على الخادم (تلك التي توفر قشرة تفاعلية) لا تحتوي على أي مصادقة على الإطلاق.
### الهجوم: من الاستشارة إلى مفاتيح AWS في 3 دقائق