vm2 NodeVMは、ホストプロセスのTLS信頼ストアを置き換える可能性があります。

概要

vm2 3.11.6では、明示的に許可された場合に、ホストの`tls`モジュールが`NodeVM`に公開されます。モジュールオブジェクトは読み取り専用としてラップされていますが、その関数は依然としてプロセス全体で共有されるホストの状態に対して実行されます。Node.jsのバージョンで`tls.setDefaultCACertificates()`が提供されている場合、サンドボックス内のコードは、その後のホスト領域のTLSクライアントが信頼する認証局を置き換えることができます。

この脆弱性を悪用するには、許可された`tls`と`url`の組み込み機能のみが必要です。`fs`、`process`、`module`、`child_process`、外部パッケージ、または一般的な`'*'`の組み込み機能の許可は必要ありません。ホストのHTTPSリクエストは、サンドボックスの実行前に攻撃者の証明書を拒否しましたが、サンドボックスがデフォルトのCAリストを置き換えた後、同じ証明書を受け入れ、アプリケーションのマーカーを返しました。

これは、意図されたサンドボックスの境界を越えています。攻撃者は、ホストのHTTPSクライアントが攻撃者によって制御されるCAを信頼するようにさせることができ、これにより、攻撃者がその後の宛先またはネットワークパスに影響を与えることができる場合、認証情報の窃取や応答の改ざんが可能になります。リストを置き換えることで、通常の信頼ルートが削除され、関連性のないホストのTLSトラフィックが中断される可能性があります。

### 詳細

脆弱な境界は、`lib/builtin.js`にあるデフォルトの組み込みローダーです。危険と分類されない組み込み機能は、再帰的な読み取り専用ブリッジを通じて公開されます。

```js
builtins.set(key, special ? special : vm => vm.readonly(hostRequire(key)));
```

読み取り専用のラッパーは、サンドボックスのプロキシを介した通常のプロパティの代入を防止します。ただし、`tls.setDefaultCACertificates()`のような関数をサンドボックス内でローカルに実行させるわけではありません。この関数は、現在のNode.jsスレッドのデフォルトのCAリストを変更し、独自の`ca`オプションを提供しないその後のTLS接続に影響を与えます。

危険な組み込み機能のリストから、`dns`が除外されるようになりました。これは、`dns.setServers()`がプロセス全体で共有されるホストのネットワークの状態を変更するためです。現在、Node.jsでは、`tls`を通じて同様のプロセス全体での信頼の変更が可能ですが、`tls`自体は除外されていません。

通常のサンドボックス環境では、直接的なコールは不要です。ネイティブのTLS関数は、実際のホスト領域の配列を必要とします。許可されたホスト`url`モジュールが、それを提供します。

```js
const tls = require('tls');
const {URLSearchParams} = require('url');

const hostArray = new URLSearchParams(
'ca=' + encodeURIComponent(attackerCaPem)
).getAll('ca');

tls.setDefaultCACertificates(hostArray);
```

`URLSearchParams.getAll()`はホスト領域で実行され、攻撃者によって制御されるPEM文字列を含むホスト配列を返します。vm2は、その配列をサンドボックスにマッピングします。マッピングされた値がTLS関数に渡されると、ブリッジはそれを元のホスト配列に変換し、ネイティブの型チェックを満たします。その後、TLS関数は、ホストスレッドのデフォルトのCAセットを置き換えます。

完全な脆弱性攻撃の流れは以下の通りです。

```text
攻撃者制御のNodeVMプログラム
-> 許可された`tls`と`url`の組み込み関数のみを使用
-> `URLSearchParams.getAll()`は、攻撃者のCA PEMを含むホスト配列を作成
-> ブリッジは、TLSに渡されたマッピングされた配列を元の配列に変換
-> `tls.setDefaultCACertificates()`は、ホストのデフォルトの信頼ルートを置き換える
-> その後のホスト領域のHTTPSクライアントは、攻撃者によって署名された証明書を受け入れる
-> 攻撃者は、アプリケーションのTLSトラフィックを監視または変更できる
```

このAPIは、Node.js 22.19.0以降の22.x系、およびNode.js 24.5.0以降の24.x系で利用可能です。

### 実証コード

添付のPoCは、完全にローカルで実行できます。一時的なCAを作成し、そのCAによって署名されたローカルHTTPSサーバーを起動し、その後、ホスト領域から2つのリクエストを実行します。

1. Node.js 22.19.0以降の22.x系、またはNode.js 24.5.0以降の24.x系をインストールします。OpenSSLも利用可能である必要があります。
2. 添付の`poc`ディレクトリで、以下のコマンドを実行します。

```bash
npm install --ignore-scripts
node poc.js
```

3. 脆弱な結果は、以下のすべての特性を持ちます。

- 最初のホストへのリクエストが、証明書の検証エラーにより失敗します。
- サンドボックスは、1つのホストレルムCAエントリを提供したことを報告します。
- 2番目のホストへのリクエストは、HTTP 200のステータスコードで成功します。
- 2番目のレスポンスボディは、`VM2_POC_HOST_TLS_RESPONSE_3e71c8`です。

サーバーは、ローカルループバックインターフェースでのみリッスンしており、このPoCは外部へのリクエストを行いません。

### 影響

これは、プロセス全体に適用されるTLSセキュリティ設定を囲む、不適切なサンドボックスの認可境界です。

`tls`および`url`の組み込み機能が設定された`NodeVM`にコードを送信できる攻撃者は、以下のことが可能になります。

- その後のホスト側のHTTPSおよびTLSクライアントで使用されるCAを置き換える。
- 攻撃者が署名した証明書を提示するサービスを、ホストが認証するようにする。
- 攻撃者がDNS、ルーティング、プロキシ、または後続のリクエスト先を操作できる場合、ホストの認証情報、APIトークン、セッションデータ、リクエストボディ、およびレスポンスを傍受する。
- 設定、Webhook、アップデート、ID、およびパッケージ取得のトラフィックを含む、信頼されたレスポンスを改ざんする。
- 通常の信頼されたルートを削除し、関連性のないホストのTLS接続を失敗させる。

このPoCから、攻撃者は直接的なファイルアクセスやコマンド実行の機能を取得することはできません。重要な影響は、サンドボックスの権限外にある、ホストプロセスの認証信頼決定を制御できることです。明示的に独自のCAリストを提供する接続は影響を受けず、既にキャッシュされたTLSセッションは変更されない可能性があります。

影響を受けるパッケージ:
- npm (JavaScript) vm2: 影響を受けるバージョンは、3.11.3以上、3.11.6以下、3.11.7で修正済み。