Marimo CVE-2026-39987 — 認証なしPTYシェル奪取のPre-Auth RCE、アドバイザリ公開から9時間41分で悪用観測

この記事は約15分で読めます。

Pythonのリアクティブノートブック「Marimo」に、認証なしでホスト上のPTYシェルを奪取できる事前認証リモートコード実行(Pre-Auth RCE)の脆弱性CVE-2026-39987が公開されました。GitHub Security Advisory(GHSA-2679-6mx9-h9xc)は2026年4月8日に公開され、その直後からSysdig Threat Research Teamがハニーポットで悪用試行を観測しています。Endor Labsの集約によれば、最初の悪用試行はアドバイザリ公開から9時間41分後、認証情報窃取の完了までは3分未満という短さでした。本稿では、AI/MLデータサイエンス基盤でノートブックツールが持つ攻撃面と、アドバイザリ公開から悪用までの時間が数時間スケールまで圧縮されつつある現実を、実務者視点で整理します。

脆弱性の概要

項目 内容
CVE ID CVE-2026-39987
GHSA ID GHSA-2679-6mx9-h9xc
CVSSスコア NVDは2026年4月9日時点で「Received」状態であり、NVD自身のCVSS評価は現時点で未提供(Awaiting Analysis)です。代わりにGitHub CNA(marimo-team)がCVSS v3.1で9.8 Critical(ベクター:AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)を付与しており、GitLab Advisory Database経由で確認できます。加えてEndor LabsおよびThe Hacker NewsはCVSS v4.0で9.3 Criticalと記載していますが、本稿執筆時点ではNVDにv4.0スコアの登録はなく、その出所は確定できていません
CWE分類 CWE-306(Missing Authentication for Critical Function、重要機能に対する認証の欠如)
影響対象 GitHub Security Advisory(GHSA)本文には「Affected versions: <= 0.20.4」と記載されている一方、NVD公式Descriptionには「Prior to 0.23.0, Marimo has a Pre-Auth RCE vulnerability」と記載されており、影響範囲の表記が一次ソース間で異なります。実務上は0.23.0未満のすべてのバージョンを影響対象として扱うのが安全です
修正バージョン Marimo 0.23.0
公開日 GitHub Security Advisory:2026年4月8日(marimo-teamが公開)。NVD Published Date:2026年4月9日
発見者/報告者 q1uf3ng氏(@q1uf3ng、GitHubクレジット記載)。パッチ担当はmarimo-teamメンテナーのmscolnick氏
修正コミット c24d4806398f30be6b12acd6c60d1d7c68cfd12a(PR #9098)
悪用観測 Sysdig Threat Research Teamが複数クラウドプロバイダー上に展開したハニーポットで、アドバイザリ公開から9時間41分後に最初の悪用試行を観測(Endor Labs経由で引用)。2026年4月11日時点でCISA KEVへの登録は確認されていません

Marimoとはどのようなツールか

Marimoは、Python用のリアクティブノートブック環境で、Jupyterの現代的な代替として位置づけられています。GitHubスター数は約19,600〜20,300に達し、データサイエンス、機械学習実験、内部分析といった用途で採用が広がっています。ノートブック内のセル間の依存関係を自動追跡し、ひとつのセルを変更すると関連するセルが自動的に再計算される「リアクティブ実行」が特徴です。

運用形態として注目すべき点は、Marimoがリモート共有とインタラクティブ編集を設計上の強みとして位置づけていることです。このため、実際のデプロイメントではインターネットから到達可能な編集サーバーとして配置されるケースが多くなります。開発チームはしばしば、ネットワークアクセスを持つコンテナ内でMarimoを動かし、協業のためにポートを公開する構成を採っています。

脆弱性の技術的な核心 — 同じサーバーの2つのWebSocketエンドポイントで認証ロジックが食い違っていた

本脆弱性は、Marimoの統合ターミナル機能がWebSocket経由で到達可能でありながら、同じサーバー上の他のWebSocketエンドポイントが認証を強制していたのに対し、ターミナル用エンドポイントだけが認証チェックを完全に欠いていたという構造的な不整合に起因します。

Marimo公式のGitHub Security Advisoryは、脆弱コードと正しく実装された比較対象コードを並べて公開しています。脆弱なコードはmarimo/_server/api/endpoints/terminal.pyに存在し、以下のような処理フローでした。

@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
    # 認証チェックなし!
    await websocket.accept()  # 接続を直接許可
    # ...
    child_pid, fd = pty.fork()  # PTYシェル生成

ターミナルWebSocketエンドポイントが確認していたのは「実行モードがEDITかどうか」「プラットフォームがターミナルをサポートしているか」のみで、認証検証は一切行われていませんでした。そのうえでpty.fork()を呼び出し、攻撃者はPTY子プロセス経由で完全なインタラクティブシェルを獲得できました。

一方、同じサーバー上の通常WebSocketエンドポイント(/ws)は正しく認証を実装しており、以下のようにWebSocketConnectionValidatorを通じて認証を強制していました。

@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の公式アドバイザリは、この認証の非対称性を本脆弱性の根本原因として明記しています。さらに、Marimoが利用しているStarlette のAuthenticationMiddlewareは、認証に失敗した接続をUnauthenticatedUserとしてマークするだけで能動的にWebSocket接続を拒否しないため、実際の認証強制は各エンドポイントの@requires()デコレーターまたはvalidate_auth()呼び出しに依存します。ターミナルエンドポイントにはどちらも存在しなかったため、認証ミドルウェアが有効であっても認証なしの接続が通ってしまう構造でした。

攻撃チェーン — WebSocket接続1回でPTYシェル、デフォルトDockerではroot

Marimo公式アドバイザリが示している攻撃チェーンは、驚くほど単純です。

  1. 攻撃者がws://TARGET:2718/terminal/wsにWebSocket接続(認証不要)
  2. websocket.accept()が接続を直接許可
  3. pty.fork()がPTY子プロセスを生成
  4. 完全なインタラクティブシェルが開通し、任意コマンドが実行可能になる
  5. デフォルトのDockerデプロイメントでは、コマンドはroot権限で実行される

1回のWebSocket接続だけで、完全なインタラクティブシェルが得られます。フィッシング、セッション窃取、サプライチェーン汚染といった回り道は一切不要で、「接続してコマンドを打つ」だけで完結します。

GHSA本文に動作する完全なPoCコードが掲載されている

本件で武器化の障壁が極めて低い理由のひとつは、現在公開されているMarimo公式のGitHub Security Advisory本文に、動作する完全なPythonコードのProof of Concept(PoC)が掲載されていることです。アドバイザリにはWebSocket接続からidコマンド実行までの具体的なコードと、uid=0(root) gid=0(root)という実行結果、再現環境のDockerfileまで掲載されています。

透明性と迅速な対処を促す観点ではこの公開判断は正しい判断ですが、同時に攻撃者は追加の研究なしに現在のアドバイザリ内容を即座に武器化できる状態にあります。アドバイザリのどの時点からPoCが含まれていたかについては、公開初期の版と現在の版を厳密に比較できないため本稿では断定しませんが、後述するSysdigの観測データが示す「アドバイザリ公開から10時間足らずで悪用試行」という事実は、攻撃者がアドバイザリ記載の情報をきわめて速やかに武器化したことを示唆しています。

アドバイザリには「認証トークン(access_token)が発行されている通常ユーザー向けのサーバー構成でも、ターミナルWebSocketパスはその認証モデルを完全に迂回する」という再現結果も記されています。つまり、本脆弱性は「認証を有効にしているから安全」という前提を成り立たなくするものでした。

Sysdigによる悪用観測 — 9時間41分後に最初の試行、3分未満で認証情報窃取完了

Sysdig Threat Research Teamは、複数のクラウドプロバイダー上に脆弱なMarimoインスタンスを動かすハニーポットノードを展開し、本脆弱性の野生での悪用状況を観測しました。観測結果はEndor Labsの解析ブログで引用されており、次のような数値が示されています。

  • 最初の悪用試行:アドバイザリ公開から9時間41分後
  • 認証情報窃取までの所要時間:3分未満
  • 攻撃者の行動パターン:90分間にわたって4回接続、セッション間に休止を挟む

Endor Labsは「攻撃者はアドバイザリの記述から直接動作するエクスプロイトを組み立て、認証なしのターミナルエンドポイントに接続し、侵害環境を手動で探索していた」と分析しています。セッション間の休止があることから、単発の自動スキャンではなく、実行している人間またはエージェントが結果を見ながら判断していた可能性が示唆されています。

露出インスタンスの推定規模 — デフォルトポートだけでは見えない

Endor LabsはMarimoインスタンスの公開状況についても調査しています。MarimoのデフォルトポートであるTCP/2718を対象とする単純なスキャンでは、過去の大規模クロールデータセット上で少数のリスナーしか見つかりません。しかし、実際のデプロイメントの多くは、80番または443番ポートでTLS終端され、リバースプロキシ経由でMarimoにアクセスできる構成になっているため、ポート2718への検索結果だけでは全貌が見えません。

Endor Labsは独自の偵察に基づいて、「数十から数百のインスタンスがパッチ未適用で露出したままの可能性がある」と推定しています。これはあくまで推定値で、確定的な数字ではない点を明記しておきます。

ここで重要なのは、MarimoのようなAI/MLデータサイエンスツールは、しばしば.envファイル、クラウドプロバイダー資格情報、データベース接続、内部APIなど、高価値な資産と同じホストに同居していることです。Endor Labsは「クラウドアカウント乗っ取りまで、1回のWebSocket接続しか離れていない」と表現しており、Marimoホストの侵害が周辺サービスへの横展開につながりやすい構造を指摘しています。

時間軸の圧縮 — Langflow(20時間)からMarimo(10時間)へ

本件が示す最大の教訓は、アドバイザリ公開から悪用までの時間が数時間スケールまで圧縮されている現実です。Sysdig Threat Research Teamは、2026年3月17日に公開されたLangflowのCVE-2026-33017(AIエージェントビルダー向けの事前認証RCE)でも類似の観測を行っており、当時は公開から20時間以内に最初の悪用試行を確認しています。Marimoでは、これが10時間以下(正確には9時間41分)まで縮みました。

Sysdigは以前のLangflow記事で「この時間軸の圧縮は、中央集権的にパッチが当てられるフロントラインのWebアプリと比べて、内部のデータサイエンスインフラのパッチ適用速度は遅いため、攻撃者の時間的優位が大きくなる」と指摘しています。Marimoの事例は、そのパターンが恒常化しつつあることを示しています。

本件は、Claudeが以前取り上げた記事#30 Flowise CVE-2025-59528(AIエージェントビルダーのRCE)や、4/7セッションで話題に挙がったLangflow案件と並列する「AI/MLツールエコシステムの急所となる設計上の欠陥」シリーズの新しい事例と位置づけられます。共通して観察される構造は、「信頼されたネットワーク向けに設計されたノートブック・オーケストレーションツールが、一部または全体としてインターネットに露出し、ひとつの欠陥で全体が崩れる」というパターンです。

実務への含意

本脆弱性から、インフラ・セキュリティ担当者が引き出せる実務的な含意を4点整理します。

第一に、即時パッチ適用です。Marimoを使っているチームは、バージョン0.23.0以降にアップグレードする必要があります。特にインターネットからアクセス可能な配置となっている場合、時間の猶予はありません。Sysdigの観測データが示すように、攻撃者はアドバイザリ公開から10時間以内に動き出します。公開から数日経過した現在、パッチ未適用の露出インスタンスは継続的に探索されていると想定するのが安全です。

第二に、AI/MLツールへの認証とネットワーク境界の再確認です。Endor Labsが「非対称信頼の教科書的事例」と表現したように、本件は「サーバー全体が認証を要求しているはず」というユーザーの前提が、ひとつのWebSocketエンドポイントの実装不備で崩れる典型例です。同種のPythonノートブック系ツール、AIエージェントビルダー、内部分析プラットフォームが社内で使われている場合、公開範囲とエンドポイントごとの認証ロジックを改めて棚卸しする価値があります。

第三に、開発系ツールのネットワーク到達性の絞り込みです。Marimoの強みであるリモート共有・インタラクティブ編集機能は、裏を返せば攻撃面を拡大する機能でもあります。本番環境に近い位置で動かす必要があるMarimoインスタンスには、VPN経由・ジャンプホスト経由・IPアクセスリスト経由でのみ到達できるように絞り込むことが、パッチ適用と並行して考慮すべき防御層になります。

第四に、パッチ適用のタイムラインを短縮する検討です。記事#32(FortiGate IRの2週間潜伏)や記事#30(Flowise長期流通)で見てきた「検出困難性を武器にした長期運用型」とは対照的に、本件は「公開から10時間の勝負」という真逆のパターンです。中央集権的に管理されていない内部データサイエンスインフラは、標準的な脆弱性対応プロセスの外側にあることが多く、インシデント対応チームが「そもそもどこに何がデプロイされているか」を把握していない場合もあります。CVE開示直後に緊急対応のトリガーを引ける体制かどうかを、改めて確認する契機となる事案です。

検出・緩和ガイダンス

即時の対処

  • Marimo 0.23.0以降へのアップグレード:これが最優先かつ最も確実な対処です
  • 露出インスタンスの棚卸し:デフォルトポート2718だけでなく、80/443番ポートでリバースプロキシ経由でMarimoに到達できる経路がないかを確認します。社内のCMDB、Ingressルール、ロードバランサー設定の横串調査が出発点です
  • 管理インターフェースのネットワーク境界強化:Marimoエディットサーバーへのアクセスを、VPNや信頼できるIP範囲に制限します

侵害疑い時の確認

  • Marimoアクセスログの遡及調査:2026年4月8日のアドバイザリ公開以降、/terminal/wsエンドポイントへの不審なWebSocket接続ログを確認します
  • 同居ホストの資格情報ローテーション:Marimoホスト上に.envファイル、クラウドSDK認証情報、SSH鍵、kubeconfigなどが存在している場合、侵害があったと想定してこれらを全てローテーションする判断が必要になります
  • 横展開先の調査:MarimoホストからIAMで到達可能なクラウドリソース、Kubernetesクラスタ、データベース接続の監査ログを遡って、不審なアクセスがないかを確認します

ソース

一次ソース

参考情報

コメント

タイトルとURLをコピーしました