
# مختبر Docker محلي يعيد إنتاج CVE-2026-39987، ثغرة تنفيذ التعليمات البرمجية عن بُعد (RCE) قبل المصادقة في WebSocket الطرفية الخاص بـ marimo. يقارن بين الإصدارات المعرضة للخطر والإصدارات المُصححة مع نصوص PoC الأقل ضررًا لأغراض البحث الأمني التعليمي.
مختبر Docker محلي فقط لإعادة إنتاج وفهم CVE-2026-39987 في marimo. يقارن هذا المشروع خدمة marimo الثغرة مع خدمة marimo المُصححة جنبًا إلى جنب، ثم يوضح الفرق باستخدام سكربت إثبات بأقل ضرر.
CVE-2026-39987 هي ثغرة حرجة لتنفيذ التعليمات البرمجية عن بُعد قبل المصادقة في marimo، وهو إطار عمل دفاتر ملاحظات Python تفاعلي.
السلوك الثغري موجود في نقطة نهاية WebSocket الطرفية:
/terminal/ws
في الإصدارات المتأثرة، يمكن الوصول إلى هذه النقطة دون مصادقة صالحة ويمكنها إنشاء جلسة طرفية تفاعلية. يمكن لمهاجم غير مصادق يتمكن من الوصول إلى خادم تحرير marimo الثغري تنفيذ أوامر بصلاحيات عملية marimo.
يعيد هذا المختبر إنتاج الثغرة في بيئة Docker محلية خاضعة للتحكم:
| الخدمة | الإصدار | الرابط | السلوك المتوقع |
|---|---|---|---|
vuln | marimo 0.20.4 | http://127.0.0.1:8081 | /terminal/ws يقبل اتصال WebSocket غير مصادق |
patched | marimo 0.23.0 | http://127.0.0.1:8082 | /terminal/ws يرفض اتصال WebSocket غير مصادق مع 403 Forbidden |
الهدف ليس توفير استغلال مُسلح. الهدف هو إظهار الفرق الأمني الملحوظ بين الإصدار الثغري والإصدار المُصحح باستخدام أدلة محلية قابلة لإعادة الإنتاج.
المكوّن المتأثر هو نقطة نهاية WebSocket الطرفية في marimo:
/terminal/ws
تُستخدم هذه النقطة بواسطة بيئة تحرير marimo لتوفير وظائف طرفية عبر جلسة WebSocket متصلة بالمتصفح.
كانت النقطة الثغرية تقبل اتصالات WebSocket دون فرض نفس فحوصات المصادقة المتوقعة لوظائف تحرير marimo المحمية.
الفرق المهم هو:
السلوك الثغري:
يمكن للعميل غير المصادق الاتصال بـ /terminal/ws
يتم إنشاء جلسة طرفية
يمكن إرسال الأوامر عبر WebSocket
السلوك المُصحح:
يتم رفض العميل غير المصادق
يفشل مصافحة WebSocket مع 403 Forbidden
لا يتم إنشاء جلسة طرفية
يضيف التصحيح التحقق من المصادقة إلى تدفق WebSocket الطرفي قبل السماح بإنشاء جلسة طرفية.
إذا تم تعريض خادم تحرير marimo الثغري لشبكة يمكن الوصول إليها، فقد يتمكن مهاجم غير مصادق من تنفيذ أوامر كمستخدم يقوم بتشغيل عملية marimo.
في هذا المختبر، يتم تشغيل عملية marimo عمدًا كمستخدم غير جذر منخفض الصلاحيات:
uid=10001(marimo) gid=10001(marimo) groups=10001(marimo)
هذا يحافظ على أمان العرض التوضيحي مع إثبات الثغرة.
صُمم هذا المختبر لإثبات ثلاثة أشياء:
/terminal/ws.403 Forbidden.يتجنب المختبر عمدًا الأوامر التدميرية، والاستمرارية، والصدور العكسي، وسرقة بيانات الاعتماد، أو أي هدف مواجه للإنترنت.
.
├── docker-compose.yml
├── vuln/
│ ├── Dockerfile
│ └── notebook.py
├── patched/
│ ├── Dockerfile
│ └── notebook.py
├── poc/
│ ├── poc.py
│ ├── rce_poc.py
│ └── requirements.txt
├── SAFETY.md
├── README.md
└── .gitignore
الجهاز المضيف
127.0.0.1:8081 ─────► حاوية vuln
marimo 0.20.4
/terminal/ws يقبل WebSocket غير مصادق
127.0.0.1:8082 ─────► حاوية patched
marimo 0.23.0
/terminal/ws يرفض WebSocket غير مصادق
تكشف كلتا الخدمتين عن المنفذ الداخلي 2718 الخاص بـ marimo، لكن منافذ المضيف مختلفة:
vuln -> 127.0.0.1:8081
patched -> 127.0.0.1:8082
الخدمات مرتبطة بـ 127.0.0.1 فقط. ليس المقصود تعريضها لشبكة LAN أو الإنترنت.
تم تكوين الحاويات مع عدة حواجز حماية حيثما أمكن:
- ربط المنافذ بـ 127.0.0.1 فقط
- التشغيل كمستخدم غير جذر
- إسقاط قدرات Linux
- تمكين no-new-privileges
- استخدام نظام ملفات جذر للقراءة فقط
- توفير مواقع كتابة tmpfs محدودة فقط
- عزل الخدمات داخل شبكة جسر Docker مخصصة
لا تزيل هذه الضوابط الثغرة من الخدمة الثغرية. إنها تقلل من نطاق الانفجار للعرض التوضيحي المحلي.
الافتراضات المختبرة:
- Linux x86_64 أو macOS مع Docker Desktop
- Docker Compose v2
- Python 3.9+
- اختبار محلي فقط
الأدوات المطلوبة:
docker --version
docker compose version
python3 --version
انتقل إلى دليل المشروع أو ادخل إليه:
cd cve-2026-39987
قم ببناء وتشغيل كلتا الخدمتين:
docker compose up -d --build
تحقق من أن كلتا الحاويتين تعملان:
docker compose ps
الخدمات المتوقعة:
cve-2026-39987-vuln
cve-2026-39987-patched
تحقق من إصدار الخدمة الثغرية:
docker compose exec vuln marimo --version
المتوقع:
0.20.4
تحقق من إصدار الخدمة المُصححة:
docker compose exec patched marimo --version
المتوقع:
0.23.0
أنشئ بيئة Python افتراضية:
python3 -m venv .venv
source .venv/bin/activate
قم بتثبيت التبعيات:
python -m pip install -r poc/requirements.txt
سكربت الإثبات الأساسي هو:
poc/poc.py
يحاول الاتصال بـ:
/terminal/ws
ثم يرسل أوامر إثبات غير ضارة فقط:
id
whoami
hostname
لا يتم استخدام صدور عكسي، أو كتابة ملفات، أو استمرارية، أو وصول لبيانات الاعتماد، أو أوامر تدميرية.
قم بالتشغيل:
python poc/poc.py --base-url http://127.0.0.1:8081
النتيجة المتوقعة:
[*] Target WebSocket: ws://127.0.0.1:8081/terminal/ws
[*] Sending benign proof command only: id; whoami; hostname
[+] websocket connected without credentials
[result] VULNERABLE: unauthenticated command execution observed
[proof]
uid=10001(marimo) gid=10001(marimo) groups=10001(marimo)
marimo
<container-hostname>
إذا طبع السكربت مخرجات الطرفية الخام لكنه تضمن هذه الكتلة، فإن الثغرة ما زالت مؤكدة:
CVE39987_PROOF_START
uid=10001(marimo) gid=10001(marimo) groups=10001(marimo)
marimo
<container-hostname>
CVE39987_PROOF_END
هذا يعني أن اتصال WebSocket نجح وأن مخرجات الأوامر عادت من الجلسة الطرفية.
قم بالتشغيل:
python poc/poc.py --base-url http://127.0.0.1:8082
النتيجة المتوقعة:
[*] Target WebSocket: ws://127.0.0.1:8082/terminal/ws
[*] Sending benign proof command only: id; whoami; hostname
[-] websocket connection rejected/failed: Handshake status 403 Forbidden
[result] not exploitable by this unauthenticated check
هذا يُظهر أن الخدمة المُصححة ترفض الوصول غير المصادق قبل إنشاء جلسة طرفية.
الملف أدناه مخصص للتعلم المحلي فقط:
poc/rce_poc.py
يوضح نفس المشكلة بطريقة أكثر واقعية في الوقت الفعلي من خلال الاتصال بـ WebSocket الطرفي الثغري والسماح بالتفاعل الطرفي.
استخدمه فقط ضد مختبر Docker المحلي.
python poc/rce_poc.py --base-url http://127.0.0.1:8081
السلوك المتوقع:
[+] Connected successfully (Pre-Auth RCE)
✓ Interactive shell is ready to use!
أوامر آمنة للتجربة:
id
whoami
hostname
pwd
python -c 'import marimo; print(marimo.__version__)'
exit
python poc/rce_poc.py --base-url http://127.0.0.1:8082
السلوك المتوقع:
[-] Connection Failed: 403 Forbidden
[!] This is likely the PATCHED version.
The WebSocket endpoint is now protected.
العميل في الوقت الفعلي موجود للمساعدة في فهم كيفية تصرف WebSocket الثغري. لا ينبغي استخدامه ضد أي نظام خارج هذا المختبر المحلي.
لنشر المحفظة، الدليل الأساسي الموصى به هو poc/poc.py، لأنه محدود وقابل للتكرار وبأقل ضرر.
يجب تلخيص النتيجة الرئيسية للمختبر على النحو التالي:
vuln / marimo 0.20.4:
مصافحة WebSocket غير المصادقة إلى /terminal/ws تنجح
مخرجات الأوامر غير الضارة قابلة للملاحظة
patched / marimo 0.23.0:
مصافحة WebSocket غير المصادقة إلى /terminal/ws تفشل
يعيد الخادم 403 Forbidden
لا يتم إنشاء جلسة طرفية
هذا هو الإثبات الأساسي لإعادة إنتاج CVE.
إذا كنت تريد فحص السلوك يدويًا، استخدم عميلًا يدعم WebSocket واتصل بـ:
ws://127.0.0.1:8081/terminal/ws
ws://127.0.0.1:8082/terminal/ws
المتوقع:
8081 -> يتم قبول الاتصال
8082 -> 403 Forbidden
سكربتات PoC هي المفضلة لأنها تنتج أدلة أوضح.
في بيئة حقيقية، تشمل المؤشرات المفيدة:
- طلبات WebSocket إلى /terminal/ws
- محاولات وصول غير مصادقة إلى خوادم تحرير marimo
- جلسات طرفية غير متوقعة تم إنشاؤها بواسطة marimo
- أوامر تم تشغيلها بواسطة عملية marimo
- الوصول إلى .env أو مفاتيح SSH أو بيانات اعتماد السحابة أو أسرار دفاتر الملاحظات
- حركة مرور شبكة صادرة بعد وقت قصير من اتصالات /terminal/ws
أمثلة على القطع الأثرية المحلية للفحص:
docker compose logs vuln
docker compose logs patched
docker compose ps
docker compose exec vuln ps aux
إيقاف وإزالة الحاويات:
docker compose down
إزالة وحدات التخزين أيضًا:
docker compose down -v
إزالة بيئة Python الافتراضية المحلية إذا رغبت:
rm -rf .venv
هذا المستودع مخصص للبحث الأمني المحلي وعرض المحفظة فقط.
مسموح:
- اختبار localhost
- إعادة إنتاج Docker فقط
- أوامر إثبات غير ضارة مثل id و whoami و hostname
- مقارنة السلوك الثغري والمُصحح
- توثيق السبب الجذري وأفكار الكشف
غير مسموح:
- الاختبار ضد خوادم marimo العامة
- اختبار أنظمة لا تملكها أو لا تديرها
- الصدور العكسي
- الاستمرارية
- سرقة بيانات الاعتماد
- الأوامر التدميرية
- الحركة الجانبية
- سلوك الروبوتات أو البرامج الضارة
هذا المشروع مخصص فقط للاختبار المحلي المصرح به والتعليم الأمني الدفاعي. لا تستخدم هذه السكربتات ضد أنظمة لا تملكها أو ليس لديك إذن صريح لاختبارها.
GitHub Security Advisory — GHSA-2679-6mx9-h9xc:
https://github.com/advisories/GHSA-2679-6mx9-h9xc
NVD — CVE-2026-39987:
https://nvd.nist.gov/vuln/detail/CVE-2026-39987
مستودع marimo الرئيسي:
https://github.com/marimo-team/marimo
Commit التصحيح — إضافة التحقق من المصادقة إلى WebSocket الطرفي:
https://github.com/marimo-team/marimo/commit/c24d4806398f30be6b12acd6c60d1d7c68cfd12a
PR التصحيح — marimo PR #9098:
https://github.com/marimo-team/marimo/pull/9098
| الملف | الغرض |
|---|
docker-compose.yml | يحدد خدمات marimo الثغرية والمُصححة |
vuln/Dockerfile | يبني خدمة marimo الثغرية |
patched/Dockerfile | يبني خدمة marimo المُصححة |
vuln/notebook.py | ملف دفتر ملاحظات marimo الأدنى المستخدم بواسطة الخدمة الثغرية |
patched/notebook.py | ملف دفتر ملاحظات marimo الأدنى المستخدم بواسطة الخدمة المُصححة |
poc/poc.py | سكربت إثبات بأقل ضرر يشغل أوامر غير ضارة فقط |
poc/rce_poc.py | عميل تعلم في الوقت الفعلي لمراقبة السلوك الطرفي في المختبر المحلي |
poc/requirements.txt | تبعيات Python لسكربتات PoC |
SAFETY.md | قواعد السلامة وحدود النطاق |