79%対51%:独立系ネオバンクはアプリセキュリティで後れ。 レポートを読む

ディープダイブ

ディープダイブ

MongoDB レプリカセットの use-after-free で未認証クラッシュ(CVE-2026-118383)

未認証のリクエストにより、4.4.32、5.0.35、6.0.30、7.0.48、8.0.37、8.3.16、9.0.7 より前の MongoDB レプリカセットの遅れたプライマリがクラッシュします。

要点

  • MongoDB のレプリケーション復旧処理にある use-after-free により、未認証のクライアントが waiter を仕込み、メンバーがプライマリになった際にクラッシュさせることができます。MongoDB は 2026 年 11 月 10 日に CVE-2026-118383 を公開しました。
  • 影響を受けるブランチのレプリカセットメンバーはすべて対象で、シャードと設定サーバーのメンバーも含みます。単独の mongod は復旧処理を行わないため影響を受けません。
  • MongoDB は CVSS v4.0 9.5 の Critical と評価しています。Datadog はクラッシュを再現し、MongoDB は特定の条件でコード実行が可能になり得ると述べています。攻撃の報告はなく、2026 年 11 月 13 日時点で CISA の KEV には未登録です。
  • 各メンバーを所属ブランチの修正版に更新してください。修正版は 4.4.32、5.0.35、6.0.30、7.0.48、8.0.37、8.3.16、9.0.7 です。Atlas とコンテナは「修正方法」を参照してください。

影響を受けますか?

次の条件がすべて当てはまる場合に影響を受けます。

  • サーバーが MongoDB のレプリカセットメンバーである。シャードクラスタのシャードメンバーや設定サーバーメンバーも含みます。
  • バージョンが表の影響範囲のいずれかに入っている。
  • 攻撃者がそのメンバーの MongoDB ポートに到達でき、readConcern.afterOpTime を含む ping コマンドを送信できる。脆弱なバージョンでは認証情報を確認する前にこれらのリクエストを受け付けるため、認証を有効にしても準備は防げません。

単独の mongod は影響を受けません。プライマリの復旧状態を持たず、クラッシュに必要な選挙に参加できないためです。

クラッシュには、メンバーが遅れを取り、その後に選挙に勝つことも必要です。攻撃者はどちらも引き起こせないため、仕込まれた waiter は実際のメンテナンスやフェイルオーバーの際に作動する罠です。

ブランチ 影響を受けるバージョン 修正バージョン
9.0 9.0.0 から 9.0.6 9.0.7
8.3 8.3.0 から 8.3.15 8.3.16
8.0 8.0.0 から 8.0.36 8.0.37
7.0 7.0.0 から 7.0.47 7.0.48
6.0 6.0.0 から 6.0.29 6.0.30
5.0 5.0.0 から 5.0.34 5.0.35
4.4 4.4.0 から 4.4.31 4.4.32

MongoDB のアドバイザリには、2026 年 7 月 31 日にサポート終了となった 8.2 ブランチは記載されていません。8.2 は修正を受け取りません。公開された実証コードでは、テスト専用の設定を使ったラボで 8.2.12 にも不具合が残っていたと報告されています。8.2 はサポート対象外として扱い、修正済みのブランチへ移行してください。

プライマリだけでなく、すべてのメンバーのバージョンを確認してください。

# On each host, including members in containers and members that were offline
mongosh --quiet --eval 'db.version()'

# In a replica set, list every member and its state
mongosh --quiet --eval 'rs.status().members.map(m => m.name + " " + m.stateStr)'

# Docker
docker exec <container> mongosh --quiet --eval 'db.version()'

# Kubernetes, for each member pod
kubectl exec <pod> -- mongosh --quiet --eval 'db.version()'

rs.status() は単独サーバーではエラーを返すため、そのホストがレプリカセットメンバーでないことを確認できます。Atlas ではクラスタのメンテナンス設定を開いてください。MongoDB はクラスタがメンテナンス中に自動で修正版へ更新されると述べています。

修正方法

レプリカセットの各メンバーを、所属ブランチの修正版へ更新してください。一部だけを更新すると残りのメンバーが影響を受けたままになり、その後の選挙でプライマリの役割がそのうちの 1 つに移る可能性があります。

  1. デプロイをバックアップし、ベンダーのダウンロードページで各ブランチの修正版を確認します。
  2. 一度に 1 メンバーずつ更新し、再参加してから次へ進みます。レプリカセットでは、先にプライマリを降格するかセカンダリを更新し、作業中もプライマリを維持します。
  3. 各更新後にそのメンバーで db.version() を実行し、修正版であることを確認します。メンテナンス中にオフラインだったメンバーも確認してください。
  4. Docker では修正済みイメージを取得し、コンテナを再作成します。イメージだけを差し替えても古いプロセスが動き続けます。公開された実証コードは修正版の対照実行に mongodb/mongodb-community-server:8.0.37-ubi9 を使っています。
  5. Atlas では保留中のメンテナンスを適用します。プロジェクトがメンテナンスを延期している場合、MongoDB はできるだけ早く更新を適用することを勧めています。

脆弱な復旧経路を無効にするサーバー設定はありません。メンバーへのネットワークアクセスをアプリケーションホストや他のメンバーに限定すると、waiter を仕込める相手は減ります。すでに仕込まれた waiter は消えず、競合も修正されません。すべてのメンバーを更新するまでの一時的な措置としてください。

修正版は 2 点を変更します。復旧のコールバックは、生きていて現在の復旧状態だけを終了させるようになり、readConcern.afterOpTime を送信できるのは内部ユーザーだけになりました。両方とも必要です。前者は二重のクリーンアップを止め、後者はクライアントによる waiter の仕込みを防ぎます。

根本原因は何ですか?

プライマリ復旧の完了コールバックは、実行中に自身を解放し得る復旧状態への生ポインタを保持していました。そのため中断された復旧は解放済みメモリに対して abort_inlock() を再実行します。

ReplicationCoordinatorImpl は、新しく選出されたプライマリが取りこぼした操作を適用する間、1 つの CatchupState を保持します。この状態は OpTime をキーとする multimap である _lastAppliedOpTimeWaiterList に waiter を登録します。適用済みの OpTime が目標に達すると waiter が完了し、コールバックが abort_inlock() を呼んで復旧モードを終了します。abort_inlock() は _catchupState をリセットし、状態オブジェクトを破棄します。

脆弱なコールバックは完了ステータスを無視し、自身の状態がまだ現在のものかを確認していませんでした。MongoDB 8.0.34 の src/mongo/db/repl/replication_coordinator_impl.cpp 5267 行目では、コールバックは次のとおりです。

auto targetOpTimeCB = [this](Status status) {
    // Double check the target time since stepdown may signal us too.
    const auto myLastApplied = _repl->_getMyLastAppliedOpTime_inlock();
    if (_targetOpTime <= myLastApplied) {
        ...
        abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
    }
};

状態は std::unique_ptr で保持されていたため、オブジェクトの寿命は共有されていませんでした。ヘッダーの 1972 行目は std::unique_ptr<CatchupState> _catchupState; を宣言しています。

公開された実証コードは不足している手順を補います。敵対的な OpTime を持つ 2 つの readConcern.afterOpTime waiter が multimap に順序の循環を作るため、実際の復旧 waiter は setValueIf_inlock() に到達しません。次のハートビートはメンバーがすでに追いついたと判断して復旧を中断します。この中断が未完了の waiter をリセットし、コールバックを同期的に実行します。コールバックは再び abort_inlock() を呼び、外側のフレームは内側の呼び出しが解放した状態を読み続けます。

どのように悪用されますか?

  1. 攻撃者は到達可能なメンバーに、readConcern.afterOpTime を含む未認証の ping コマンドを 2 つ送ります。選んだ OpTime の項が multimap 内の waiter を誤った順序に並べるため、実際の復旧 waiter は完了できません。
  2. waiter は残ります。期限切れにならず、メンバーの再起動、ロールバック、プライマリからの降格まで存続します。
  3. その後メンバーが別のメンバーより遅れ、選挙に勝ちます。攻撃者は遅れも選挙も引き起こせないため、この手順は実際のメンテナンスや障害に依存します。
  4. 復旧が終わるとクリーンアップが 2 回実行されます。新しいプライマリは解放済みメモリを読み書きし、書き込みを受け付ける前にクラッシュします。
  5. Datadog は 8.0.34 で CatchupState::abort_inlock() のセグメンテーション違反としてクラッシュを再現しました。ラボでは管理者権限でメンバーを一時停止し、フェイルオーバーを強制しました。未認証だったのは 2 つの ping リクエストだけです。

MongoDB のアドバイザリは、特定の条件でサーバープロセス内の任意コード実行が可能になり得ると述べています。Datadog はラボでコード実行を達成しておらず、すべてのデプロイでクラッシュが確実に起きるとは主張していません。

攻撃の流れは?

未認証のクライアントが 2 つの afterOpTime waiter を仕込み、その後のフェイルオーバーでそのメンバーが遅れたままプライマリになり、復旧のクリーンアップが解放済みメモリ上で 2 回実行されます。

flowchart LR A["readConcern.afterOpTime 付きの
未認証 ping 2 件"] --> B["lastAppliedOpTime の
multimap に waiter を保存"] B --> C["OpTime の順序で実際の
復旧 waiter が未完了のまま"] C --> D["メンバーが遅れた後、
選挙に勝つ"] D --> E["復旧が終了し、
abort_inlock() が waiter をリセット"] E --> F["コールバックが解放済み状態で
abort_inlock() を再実行"] F --> G["新しいプライマリが
書き込み前にクラッシュ"]

攻撃者に何ができますか?

未認証の攻撃者は、レプリカセットのメンバーがプライマリになった瞬間にクラッシュさせ、プライマリを集合から外して、別のメンバーが引き継ぐまで書き込みを中断させることができます。

影響はクラッシュしたメンバーの役割によって変わります。

  • 通常のレプリカセットのプライマリがクラッシュすると、選挙と短い書き込み停止が発生します。利用可能なメンバーがいれば引き継げます。
  • シャードのプライマリがクラッシュすると、新しいプライマリが選出されるまでそのシャード経由の操作が止まることがあります。
  • 設定サーバーのプライマリがクラッシュすると、設定サーバー集合が新しいプライマリを選出するまでクラスタのメタデータ変更が止まることがあります。

条件は影響の一部です。

  • 攻撃者はメンバーの MongoDB ポートに到達できる必要があります。
  • リクエストにはアカウントもユーザー操作も不要です。
  • メンバーはその後、別のメンバーより遅れた状態で選挙に勝つ必要があります。攻撃者は強制できないため、クラッシュは即時の攻撃ではなく遅延する罠です。
  • 単独の mongod は影響を受けません。

MongoDB は、特定の条件で任意コード実行が可能になり得ると述べています。コード実行を実証した情報源はなく、Datadog の再現はクラッシュで終わりました。コード実行はベンダーの評価として扱い、実証された能力とはみなさないでください。

修正が機能する理由

修正は復旧状態に共有された寿命を与え、コールバックが waiter のステータスと、自身の状態がまだ現在のものかを確認するようにします。コールバックが abort_inlock() を 2 回実行することはなくなりました。

MongoDB 8.0.34 と 8.0.37 の diff は所有権とコールバックを変更します。

-    _catchupState = std::make_unique<CatchupState>(this);
+    _catchupState = std::make_shared<CatchupState>(this);
...
-    auto targetOpTimeCB = [this](Status status) {
+    auto targetOpTimeCB = [weakSelf = weak_from_this()](Status status) {
+        if (!status.isOK()) {
+            return;
+        }
+        auto self = weakSelf.lock();
+        if (!self || self->_repl->_catchupState != self) {
+            return;
+        }
         const auto myLastApplied = _repl->_getMyLastAppliedOpTime_inlock();
         if (_targetOpTime <= myLastApplied) {
             ...
-            abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
+            self->abort_inlock(PrimaryCatchUpConclusionReason::kSucceeded);
         }
     };

ヘッダーは CatchupState を std::enable_shared_from_this<CatchupState> から継承させ、std::shared_ptr で保持します。コールバックは weak_ptr を保つため、生きていて _catchupState にまだ設定されている状態だけを終了できます。ステータスが OK でない場合は、別の経路がすでに復旧を終了したという意味であり、コールバックは状態に触れずに戻ります。

2 つ目の修正であるコミット 6111165c32 は、クライアントが送る readConcern.afterOpTime に内部ユーザー権限を要求します。これにより通常のクライアントが waiter を仕込む前提がなくなります。2 つのコミットが 8.0.37 の修正であり、ベンダーは影響を受ける各ブランチの対応する修正版を一覧にしています。

侵害されたかどうかを確認するには?

攻撃の報告はなく、侵害の指標も公開されていません。確認はサーバーログでクラッシュの痕跡と waiter のリクエストを探します。

  • 未認証の ping が readConcern.afterOpTime を含み、MaxTimeMSExpired で終わった、ログ ID 51803 の Slow query エントリを探します。
  • メンバーがプライマリ復旧に入り(ログ ID 21359)、同じ遷移で 2 回終了した(ログ ID 21363)形跡を探します。
  • その 2 回の終了後に CatchupState::abort_inlock() を名指す致命的シグナルやバックトレースを探します。
  • 同じメンバーの以前のログも確認します。2 つのリクエストはフェイルオーバー前に届くことがあり、後でプライマリになるメンバーはそれを保持します。

通常の選挙や 1 回だけの復旧終了はこのパターンに該当しません。リクエストのフィールドがログに残るかはログ設定に依存し、attr.remote には実際のクライアントアドレスではなくループバックアドレスが表示されることがあります。失敗した試行は痕跡を残さないことがあります。

年表

  • 2026-10-30: MongoDB が 8.0.37 ホットフィックスの 2 つの修正を作成する。1 つは復旧状態、もう 1 つは readConcern.afterOpTime 用。
  • 2026-11-10: MongoDB が 20:23 UTC に CVE-2026-118383 を公開し、修正版をリリースする。2 つの修正コミットは 23:07 と 23:09 UTC に反映される。
  • 2026-11-11: Datadog Security Labs が分析と再現を公開し、公開実証コードが作成される。
  • 2026-11-12: The Hacker News が不具合と、悪用を把握していないという MongoDB の声明を報じる。
  • 2026-11-13: CISA の KEV カタログに CVE は登録されていない。

修正から公開実証コードまで 12 日
図 1: 修正は 2026 年 10 月 30 日に作成され、CVE と修正版は 11 月 10 日、公開分析と実証コードは 11 月 11 日に続きました。出典: MongoDB、Datadog Security Labs、公開実証コード、2026 年 11 月 13 日確認

よくある質問

CVE-2026-118383 は実際に悪用されていますか?

いいえ。MongoDB は社内で発見したと述べ、悪用の試みを把握していないとしています。2026 年 11 月 13 日時点で CISA の KEV カタログには登録されていません。

認証で攻撃を防げますか?

影響を受けるバージョンでは防げません。2 つの ping コマンドはサーバーが認証情報を確認する前に受け付けられるため、Datadog は未認証のクライアントで waiter を仕込めました。8.0.37 の修正は、内部ユーザー以外のクライアントが送る readConcern.afterOpTime を拒否します。

単独の mongod もこの方法でクラッシュしますか?

いいえ。単独サーバーにはレプリカセットもプライマリ復旧状態もないため、use-after-free を引き起こす二重クリーンアップは実行されません。アドバイザリは単独インスタンスを除外しています。

攻撃者はフェイルオーバーを強制できますか?

いいえ。攻撃者は waiter を仕込めますが、メンバーが自力で遅れを取り、選挙に勝つ必要があります。Datadog のラボでは、研究者が管理者権限でメンバーを一時停止し、選挙を引き起こしました。未認証だったのは 2 つのリクエストだけです。

Atlas クラスタはどうなりますか?

MongoDB は、Atlas クラスタがクラスタのメンテナンス中に修正版へ自動的に更新されると述べています。プロジェクトがメンテナンスを延期している場合、MongoDB はできるだけ早く更新を適用することを勧めています。

8.2 ブランチを使っています。修正されていますか?

アドバイザリには、2026 年 7 月 31 日にサポート終了した 8.2 は記載されていません。公開実証コードでは、テスト専用の設定で 8.2.12 にも不具合が残っていたと報告されています。サポート対象ブランチの修正版へ移行してください。

Ostorlab で公開されている MongoDB メンバーを把握する

Ostorlab の攻撃対象領域の検出は、組織内のインターネットに公開されたホストとサービスを一覧化します。公開している MongoDB ポートを把握し、最初に更新または制限すべきメンバーを判断できます。これは公開ホストの一覧であり、この CVE の検出ではありません。

参考資料

  • CVE-2026-118383 レコード、MongoDB CNA、2026-11-10 公開、CVSS v4.0 9.5 および CVSS v3.1 9.0: https://www.cve.org/CVERecord?id=CVE-2026-118383 (2026 年 11 月 13 日確認)
  • MongoDB、「Important MongoDB security update available (Action Required) - November 10, 2026」、ベンダー声明と Atlas のメンテナンス指針: https://www.mongodb.com/community/forums/t/important-mongodb-security-update-available-action-required-november-10-2026/344843 (2026 年 11 月 13 日確認)
  • Datadog Security Labs、「CVE-2026-118383: Use-after-free vulnerability in MongoDB」、2026 年 11 月 11 日、再現、クラッシュログ、悪用条件: https://securitylabs.datadoghq.com/articles/cve-2026-118383-mongodb-server-use-after-free/ (2026 年 11 月 13 日確認)
  • 公開実証コードと分析、2 リクエストのトリガー、waiter の順序循環、修正コミット: https://github.com/googlesky/CVE-2026-118383 (2026 年 11 月 13 日確認)
  • 修正コミット 23dfc542199ab3b88d6a9420b6cd3f125a897620、「SERVER-138175 [v8.0.37-hotfix] Conclude primary catch-up only from a live, current catch-up state」、2026-11-10 マージ: https://github.com/mongodb/mongo/commit/23dfc542199ab3b88d6a9420b6cd3f125a897620 (2026 年 11 月 13 日確認)
  • 修正コミット 6111165c32bfc74f197e2dfeb9480d0b52154d68、「SERVER-138119 [v8.0.37-hotfix] Allow only internal users to use readConcern.afterOpTime」、2026-11-10 マージ: https://github.com/mongodb/mongo/commit/6111165c32bfc74f197e2dfeb9480d0b52154d68 (2026 年 11 月 13 日確認)
  • MongoDB 8.0.34 のソース、src/mongo/db/repl/replication_coordinator_impl.cpp 5267 行目の脆弱なコールバックとヘッダー 1972 行目の unique_ptr: https://github.com/mongodb/mongo/blob/r8.0.34/src/mongo/db/repl/replication_coordinator_impl.cpp (2026 年 11 月 13 日確認)
  • MongoDB 8.0.37 のソース、修正されたコールバックと共有所有権: https://github.com/mongodb/mongo/blob/r8.0.37/src/mongo/db/repl/replication_coordinator_impl.cpp (2026 年 11 月 13 日確認)
  • MongoDB ダウンロードページ、修正済み Community Server ビルド: https://www.mongodb.com/try/download/community (2026 年 11 月 13 日確認)
  • MongoDB のライフサイクル日程、8.2 の 2026 年 7 月 31 日サポート終了: https://www.mongodb.com/legal/support-policy/lifecycles (2026 年 11 月 13 日確認)
  • Docker Hub、公式 mongo イメージとプル数: https://hub.docker.com/_/mongo (2026 年 11 月 13 日確認)
  • CISA の悪用済み脆弱性カタログ JSON フィード、2026 年 11 月 13 日に CVE-2026-118383 を確認(未登録): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026 年 11 月 13 日確認)
  • The Hacker News、「Critical Pre-Auth MongoDB Flaw (CVE-2026-118383) Can Crash Lagging Servers on Failover」、2026 年 11 月 12 日、Datadog の再現とベンダー声明: https://thehackernews.com/2026/11/critical-pre-auth-mongodb-flaw-cve-2026.html (2026 年 11 月 13 日確認)