Mongoose は MongoDB 用のオブジェクトデータモデリング (ODM) ライブラリで、Node.js アプリケーションでのデータベースのやりとりを簡単にします。スキーマベースのソリューションを提供することで、Mongoose は JavaScript オブジェクトを MongoDB ドキュメントにマッピングできるようにし、データを構造化して管理や検証を簡単にする抽象化レイヤーとして機能します。カスタムロジック実行用のミドルウェアや直感的なクエリ構築システムなどの機能により、MongooseはMongoDBでの作業効率を高めます。Node.jsのためのエレガントなMongoDBオブジェクトモデリング」と評されるMongooseは、GitHubで27Kのスターを獲得しており、開発者の間で広く使われ、評価されていることを反映しています。
OPSWAT フェローシップ・プログラムと重要脆弱性の発見
ベトナムを拠点とするOPSWAT 重要インフラ・サイバーセキュリティ大学院フェローシップ・プログラムは、大学院生に重要インフラの安全確保に関する実践的な経験を提供します。このプログラムの一環として、フェローはサイバーセキュリティの脆弱性を分析し、OPSWAT 専門家と協力して、マルウェアの検出、ファイルセキュリティ、脅威の防止などの分野における現実の課題に取り組む機会があります。
OPSWAT フェローシップ・プログラムでは、参加者はさまざまな製品、ライブラリ、オペレーティング・システムにわたって既知のCVEを体系的に調査し、再現します。このイニシアチブの一環として、著名なフェローの一人であるDat Phungは、実運用環境で広く採用されているMongooseを調査することにしました。2024年11月、彼はライブラリの詳細な分析を行っているときに、Mongooseに重大な脆弱性を発見した。この脆弱性は攻撃者が$whereの値を悪用することを可能にし、Node.jsアプリケーションサーバー上でリモートコード実行(RCE)につながる可能性がありました。この問題をMongooseに速やかに報告したところ、バージョン8.8.3の一部としてパッチがリリースされ、CVE-2024-53900がNational Vulnerability Database (NVD)に公開されました。
CVE-2024-53900 & CVE-2025-23061 タイムライン
- 2024年11月7日 Dat PhungがMongooseの重大な脆弱性を特定し、Snykにセキュリティレポートを提出。
- 2024年11月26日 Mongooseはこの脆弱性を修正したバージョン8.8.3をリリースしました。
- 2024年12月2日 National Vulnerability Database (NVD) がこの脆弱性の CVE-2024-53900 を公開しました。
- 2024年12月17日Mongooseの8.8.3パッチを分析したところ、Dat PhungがRCE(リモートコード実行)を可能にするバイパスを発見しました。詳細なセキュリティレポートがTideliftに提出されました。
- 2025年1月13日:Mongooseはバージョン8.9.5をリリースし、バイパスに効果的に対処する強化パッチを導入しました。
- 2025年1月15日:ナショナル・ヴァルネラビリティ・データベース(NVD)がCVE-2025-23061を公式に公開し、新たに特定された脆弱性の深刻さを強調。
Mongoose の Populate() メソッド
Mongoose は populate() という便利な機能も提供しており、 ドキュメント間のリレーションを扱う機能を強化しています。MongoDB のバージョン 3.2 以降では、$lookup という集約演算子で結合を行うことができますが、 Mongoose の populate() はより強力な代替機能を提供しています。これは特に、あるドキュメントが別のドキュメントを _id で参照しているような、異なる MongoDB コレクション間の関係を管理するのに便利です。[2]
Mongoose でスキーマを定義するとき、ref オプションを使ってフィールドが別のモデルを参照するように設定できます。populate() メソッドを使うと、参照先のフィールド(ObjectId)を関連モデルの完全なドキュメントに置き換えることができます。例えば、ブックストアのアプリケーションでは、bookSchemaの author フィールドはAuthorドキュメントを参照し、reviewフィールドはReviewsドキュメントを参照します。populate() メソッドを使用すると、開発者は、ブックモデルへのクエリの際に、authorフィールド (ObjectId) を完全なAuthorドキュメントに置き換えることができます。
populate()を使用すると、ブックモデルへのクエリの際に、著者フィールド(これはObjectIdです)を完全な著者ドキュメントに置き換えることができます:
さらに、Mongoose の populate() メソッドはカスタムクエリをサポートしており、 どの関連ドキュメントをどのように取得するかを定義できます。matchやoptionsといったプロパティを使うと、関連ドキュメントをフィルタしたりソートしたり制限したりスキップしたりできるので、柔軟なデータ取得が可能です。
CVE-2024-53900の分析
OPSWAT サイバーセキュリティ大学院フェローシッププログラムの一環として、既知のCVEを再現するためにMongooseを解析していたとき、Dat PhungはMongoDBドキュメント間のリレーションシップを処理する上で重要な役割を果たすpopulate()メソッドの内部動作の包括的なレビューを行った。populate() メソッドは文字列とオブジェクトの両方の引数をサポートしており、開発者は match オプションを使って取得するデータに特定のフィルタをかけることができます:
上の例では match オプションはフィルタオブジェクトで、 MongoDB のクエリオペレータを含めることができます。 詳しくはクエリおよび投影演算子 - MongoDB マニュアル v8.0 を参照ください。注目すべき演算子のひとつが$where で、これは MongoDB サーバ上で JavaScript を直接実行できるようにします。しかし、MongoDB サーバー上での実行には制限があり、 基本的な操作や関数しかサポートしていません。
Dat Phung はpopulate()メソッドのワークフローを理解するために Mongoose のソースコードを徹底的に分析しました。彼は、アプリケーションがモデル上でpopulate()メソッドを呼び出した後、populate() 関数がトリガーされることを突き止めました。この関数の中で Mongoose は _execPopulateQuery() 関数を呼び出し、MongoDB サーバー上で$where演算子を使ったクエリを実行します。その後で、外部コレクションからすべてのドキュメントを取得し、次のステップで展開します。
MongoDB からデータを取得した後、Mongoose はコールバック関数_done() を実行します。この関数は_assign() を呼び出してデータを準備し、assignVals()関数を呼び出して2つのモデルを「結合」します。
この脆弱性は、取得したデータが Mongoose のassignVals() 関数で処理されるときに発生する可能性があります。この関数はmatchオプションが配列かどうかをチェックし、配列の場合は各演算子をsift()関数に渡します。sift() 関数は同じ名前の外部ライブラリからインポートされたもので、 アプリケーションサーバー上でローカルにクエリを処理します。このローカル処理は、特にユーザが制御する入力を処理する場合に、セキュリティ上のリスクをもたらします。
これをさらに調査するために、Dat Phungは条件が満たされるようにmatchオプションの値を変更し、データフローの追加分析のためにsift() 関数を呼び出した。
条件が整ったので、$where演算子をsift()関数に渡しました。
siftライブラリは、配列やJSONオブジェクトなどのデータコレクションをMongoDBライクな構文でフィルタリングしたりクエリしたりするために設計された、軽量のJavaScriptユーティリティだ。公式ドキュメントによると、"Sift は JavaScript で MongoDB クエリを使うための小さなライブラリです" とのことです。sift()関数は、MongoDBライクなフィルター操作をデータベースサーバーではなくアプリケーションサーバー上で評価するため、信頼できない入力を処理する際にシステムを重大なセキュリティリスクにさらす可能性がある。
分析を続けると、フェローは sift ライブラリのcreateDefaultQueryTester()関数の中に問題があることを突き止めました。この関数は match 配列の各オペレーションを実行可能な JavaScript 関数に変換し、それを使って MongoDB ドキュメントデータをローカルでフィルタリングしたり処理したりします。そのためにcreateDefaultQueryTester()はcreateNamedOperation()関数をコールし、 match 配列から$whereなどの操作を引数に渡します。
createNamedOperationは、マッチ配列の各オペレーションについて、そのオペレーションがサポートされているかどうかをチェックし、対応する関数に渡す。
操作が$whereの場合、生の "params "値を使ってJavaScript関数が生成されます。この関数は、マッチ配列の$where演算子から派生したもので、ユーザーが制御することができます。
CVE-2024-53900悪用の詳細
MongoDBは$where操作によってJavaScript関数の実行を制限していますが、先に分析したように、sift()関数はそのような制限なしにこれらの関数を実行することができます。この入力検証と制限の欠如は、重大なセキュリティ脆弱性をもたらします。なぜなら、ユーザー入力によって直接制御される "params" 値が悪用され、コードインジェクション攻撃につながる可能性があるからです。この問題をより徹底的に調べるために、Dat Phungは次のようなクエリを作成した:
当初、クエリーは別のプロセスの実行に失敗し、次のようなエラーが発生した:
このエラーは、Mongoose が sift() 関数に制御を渡す前に MongoDB サーバーで$where演算を実行しようとしたことを示しています。しかし MongoDB の$where句では JavaScript 関数に制限があるため、エラーが発生してクエリが実行できません。その結果、Mongoose はsift() 関数に到達する前に処理を止めてしまいます。
この制限を回避するために、フェローはアプリケーションサーバーに存在する "global "変数を利用した。この方法によって、彼はMongoDBサーバーの制限を回避し、クエリがsift()関数に到達できるようにした:
この値では、Mongoose が MongoDB で$where演算を実行するときに、"global" 変数がないと三項演算子(typeof global != "undefined" ? global.process.mainModule.constructor._load("child_process").exec("calc") :1)で 1 を返し、MongoDB がエラーを投げるのを防ぎます。その結果、MongoDB サーバーでクエリが問題なく実行されます。
しかし、同じ値が、"global "変数が利用可能なアプリケーション・サーバー上で実行されるsift()関数に到達すると、以下の関数が作成されるトリガーとなる:
リモート・コード実行(RCE)の概念実証
ブログの冒頭で提供されたアプリケーションの例では、攻撃者が以下のリクエストを送信した場合、リモートコード実行(RCE)攻撃を成功させることができます:
このビデオは、8.8.3より前のバージョンのMongooseに影響するCVE-2024-53900のProof of Conceptを示しており、siftライブラリと一緒に$where演算子の悪用を防ぐための適切な入力検証が欠けています。
不完全な修正と CVE-2025-23061
Dat Phung氏のセキュリティレポートに基づいて、Mongooseは以前に特定された脆弱性(CVE-2024-53900)を解決するためのパッチを公開前に導入しました。該当のパッチ(Automattic/mongoose@33679bc)では、populate()に渡されるマッチプロパティ内で$whereの使用を禁止するチェックが追加されています。
このスニペットは、 populate() に渡された match プロパティが配列であるかどうかをチェックします。もしそうなら、コードは配列の各オブジェクトを繰り返し、 $where演算子を含むかどうかを調べます。もし$where が検出されるとエラーが発生し、悪意のあるペイロードが危険な sift() 関数に伝搬するのを防ぎます。
その結果、CVE-2024-53900を悪用するペイロードは、マッチ配列のオブジェクトが$whereを含むため、このチェックに失敗し、事実上sift()に到達できない。
このアップデートでは、1つの入れ子レベル内での$where の直接の使用は正しくブロックされますが、 $or演算子 (MongoDB と sift ライブラリの両方が完全にサポートしている構造) の中に埋め込まれた$where は 検出されません。
その結果、攻撃者は$or の下に$where を入れ子にして、パッチのシングルレベルチェックを逃れることができます。Mongoose はマッチ配列の各オブジェクトのトップレベルのプロパティだけを検査するので、 バイパスペイロードは検出されずに残り、最終的に sift ライブラリに到達し、 悪意のある RCE を可能にします。
CVE-2025-23061の概念実証
この修正が不完全であることを示すために、Dat PhungはMongoose 8.9.4(8.8.3より後)を使ってサンプルアプリケーションを作り直しました。 or句の中に$whereを入れ子にすることで、攻撃者はチェックをうまく回避してRCEすることができます。
この概念実証の悪用は、CVE-2025-23061が8.9.5より前のバージョンのMongooseでどのようにトリガーされるかを示し、攻撃者がサーバー上で任意のコードを実行できるようにします:
緩和と指導
上記で説明した脆弱性を軽減するために、お使いのシステムがMongooseの最新バージョンにアップデートされていることを確認してください。
SBOMエンジンを使用するMetaDefender Core この脆弱性を検出できる
OPSWAT MetaDefender CoreMetaDefender Coreは、先進的なSBOMSoftware Bill of Materials:Software 部品表)機能を備えており、企業がセキュリティリスクに対処するためのプロアクティブなアプローチを取ることを可能にします。ソフトウェアアプリケーションとその依存関係をスキャンすることによって、MetaDefender Core 、リストされたコンポーネント内のCVE-2024-53900やCVE-2025-23061のような既知の脆弱性を特定します。これにより、開発チームとセキュリティチームは、パッチの適用に優先順位をつけることができ、悪意者に悪用される前に潜在的なセキュリティリスクを軽減することができます。
以下は、MetaDefender Core SBOMで検出したCVE-2024-53900とCVE-2025-23061のスクリーンショットです:
さらに、CVEs は次のものによっても検出できます。 MetaDefender Software Supply Chainによって検出することもできます。