flawopen.com/Teardowns/cve-2024-27980-nodejs-windows-batch-injection
CVE-2024-27980:Node.js Windowsの問題
Windowsではshellオプションなしでも、spawn()とspawnSync()でバッチファイルを起動するとコマンド注入が起きる可能性がありました。
プログラムがバッチファイルへ引数を渡しても、Windowsではコマンドインタープリターを介して実行します。インタープリターは引数をコマンド構文として解釈することがあります。Node.jsは各引数の安全なエスケープを推測せず、バッチファイルの直接起動を拒否して修正しました。
主要な概念と専門用語
child_process.spawn()- 実行ファイルと引数配列を指定してプロセスを起動するNode.js非同期APIです。
バッチファイル- Windowsシェルが解釈する.bat/.cmdファイルで、共通のargv解析を行う通常の実行ファイルとは異なります。
シェルメタ文字- コマンドインタープリターにより意味が変わり、コマンド構造を変更し得る文字です。
EINVAL- Node.jsがバッチファイルの危険な直接起動を拒否するときに返すエラーコードです。
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-77
根本原因の分析 (Root Cause)
Windowsでは.batと.cmdはシェルに解釈され、引数を常に曖昧さなくエスケープできるとは限りません。Node.jsの告知によると、shellを有効にしていなくてもchild_process.spawn()とspawnSync()への悪意ある引数でコマンドを注入できました。upstreamパッチは大文字小文字を区別せず.bat/.cmdの末尾を検出し、uv_spawn()の前にEINVALを返します。この修正はsanitizeBatchArg()を実装したものではなく、shell: trueが安全という意味でもありません。後続の告知では他のバッチ拡張子に対する修正が不完全とされました(CVE-2024-36138)。両方の修正を含むバージョンを使ってください。
ステップ・バイ・ステップの攻撃フロー
アプリがプロセスAPIを呼ぶ
Windows上でアプリがchild_process.spawn()またはspawnSync()を使い、外部データの影響を受ける引数とともに.bat/.cmdを起動します。
対象はバッチファイル
バッチファイルにはシェルの規則が適用されます。引数配列だけではメタ文字の解釈を防げません。
Windowsが引数を解釈する
脆弱な動作ではshell: falseでも引数がコマンド構文として解釈される可能性がありました。
Node.jsが直接起動を拒否
修正は.bat/.cmdの直接起動でEINVALを返します。CVE-2024-36138では他の拡張子に残る不備が判明したため、両方の修正を含む版へ更新します。
ソースコード比較:脆弱 vs 堅牢化
// セキュリティパッチ適用前の src/process_wrap.cc の抜粋
node::Utf8Value file(env->isolate(), file_v);
options.file = *file;
// バッチファイルを拒否せずにファイル名を uv_spawn() に渡していた。
// CVE-2024-27980 の上流パッチからの抜粋
node::Utf8Value file(env->isolate(), file_v);
options.file = *file;
// バッチファイルの引数は常に曖昧さなくエスケープできるとは限らない。
if (IsWindowsBatchFile(options.file))
err = UV_EINVAL;
if (err == 0) {
err = uv_spawn(env->event_loop(), &wrap->process_, &options);
wrap->MarkAsInitialized();
}
エンジニアリング&システム堅牢化チェックリスト
- ✓CVE-2024-27980とCVE-2024-36138の修正を含むサポート対象Node.jsへ更新します。
- ✓外部データを使ってバッチスクリプトを起動せず、検証済み引数で実行ファイルを直接呼び出します。
- ✓シェルが避けられない場合はshell: trueをコマンド実行とみなし、厳密な検証と認可を適用します。
- ✓Windowsの回帰テストでspawn()/
spawnSync()とバッチ拡張子を確認し、EINVALで安全に拒否されることを検証します。