executeDeployment の extra vars を介した OS コマンドインジェクションspawn(cmd[0], cmd.slice(1), { shell: true }) は引数配列をエスケープせずに単一の /bin/sh -c 文字列へ再結合するため、executeDeployment MCP ツールに渡される extraVars の値に含まれるシェルメタ文字が、サーバーを実行しているホスト上で実行されます。
| CVE | CVE-2026-52618 |
| CWE | CWE-78 (OS コマンドに使用される特殊要素の不適切な中和) |
| パッケージ | @webfer/mcp-ansible-drupal (npm) |
| 影響を受けるバージョン | 2.0.0 |
| 修正バージョン | 2.0.3 (2026-06-01 公開) |
| CVSS v3.1 | AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 8.4 High (stdio トランスポート)。AV:N → サーバーがネットワークトランスポートでラップされている場合は 9.8 Critical。 |
| 報告者 | s1ko (github.com/s1ko, [email protected]) |
| CVE 割り当て | 2026-07-13, MITRE CNA-LR |
この問題は修正済みです。2.0.3 以降にアップグレードしてください。
MCP ツールの引数は攻撃者の影響を受けます。これらはエージェントが処理するコンテンツ — ドキュメント、Web ページ、ツールの出力 — から LLM によって生成されるため、間接的なプロンプトインジェクションによって攻撃者がツールハンドラーが受け取る値を制御できるようになります。これらの値のいずれかをシェルに渡すハンドラーは、その影響を MCP サーバーを実行しているホスト上でのコード実行に変えてしまいます。これは先行事例である aws-mcp-server / CVE-2026-5058 と同じモデルです。
その結果、「呼び出し元は信頼された LLM である」ことは緩和策にはなりません。ツールの引数は信頼できない入力です。
2.0.0 の src/helpers/runAnsible.ts は引数配列を構築した後、シェルを有効にして実行していました:
const proc = spawn(ansibleCmd[0], ansibleCmd.slice(1), { shell: true });
shell: true を指定すると、Node は ansibleCmd[0] を残りの要素を argv として実行しない。配列全体を1つの文字列に結合し、要素にクォートやエスケープを一切適用せずに /bin/sh -c へ渡す。したがって、;、&&、|、バッククォート、$() を含む要素は意図したコマンドから抜け出してしまう。
到達可能な経路は executeDeployment MCP ツール(src/tools/executeDeployment.ts)であり、呼び出し元が指定した extraVars を --extra-vars key=value として配列に転送する。値が ; touch /tmp/marker ; の場合、次のようになる:
/bin/sh -c "ansible-playbook -i inv.ini play.yml ... --extra-vars deploy_env=; touch /tmp/marker ; echo INJECTED"
同じバージョン内の別のシンクでは、projectRoot が
execSync('ansible-vault encrypt ' + vaultPath) に補間されていました。
poc/driver.mjs は、細工された extraVars の値で runAnsible を直接呼び出し、マーカーファイルの有無を確認します。ansible-playbook はインストールされている必要はありません。先頭のバイナリが解決されるかどうかに関係なく、注入されたコマンドは同じ /bin/sh -c 文字列内で実行されます。
npm pack @webfer/[email protected] && tar xf webfer-mcp-ansible-drupal-2.0.0.tgz
node poc/driver.mjs
影響を受けるバージョンで想定される出力:
{"target":"@webfer/mcp-ansible-drupal 2.0.0","sink":"runAnsible -> spawn(cmd[0], cmd.slice(1), {shell:true})","vector":"executeDeployment extraVars value","marker":"/tmp/PWNED_ansible","created":true,"verdict":"CONFIRMED - command injection executed"}
2026-06-01 に隔離コンテナ内で検証され、2026-06-13 に無害な touch マーカーを用いて再検証されました。テストを許可されているインフラに対してのみ実行してください。
ansible-playbook に到達する --extra-vars の値に ;、&&、||、|、バッククォート、または $( が含まれる場合。正当な Ansible 変数の値には事実上決して含まれません。ansible-playbook および 区切り文字の後に続く2つ目のコマンドを含む sh -c プロセス — アプリケーションのログに依存しないプロセスツリーのシグナル(auditd、eBPF、Falco、EDR)。ansible-playbook、git、ansible-vault は想定内ですが、シェル、インタプリタ、ネットワーククライアントは想定外です。executeDeployment の引数にシェルのメタ文字が含まれる MCP ツール呼び出しログ。ツールの引数をログに記録してください — ほとんどの MCP デプロイでは記録されておらず、アプリケーションの観点で注入された値が可視化される唯一の場所です。MITRE ATT&CK T1059.004 Command and Scripting Interpreter: Unix Shell。
@webfer/mcp-ansible-drupal 2.0.3 以降にアップグレードしてください。 現在のソースは shell オプションなしで spawn(ansibleCmd[0], ansibleCmd.slice(1), { … }) を呼び出しているため、配列は argv として渡され、メタ文字はその意味を失います。
MCP ツールハンドラの一般的なルール、および今回の修正がここに適用するもの:
shell: true を有効にしないでください。配列形式はまさにシェルを回避するために存在します。execFile/spawn を使用してください。z.string() は値が文字列であることを証明するだけで、その中身については何も証明しません。値の集合が既知の場合は許可リストを使用し、そうでない場合はシェルのメタ文字を拒否してください。NIST SP 800-53r5 SI-10、OWASP ASVS v4 §5.3.8、CWE-78 の緩和策 M1 および M2。
| 日付 | イベント |
|---|---|
| 2026-06-01 | 脆弱性を 2.0.0 に対して動的に検証 |
| 2026-06-01 | shell: true を削除した 2.0.3 を npm に公開 |
| 2026-06-13 | 2台目のホストで再検証 |
| 2026-07-13 | MITRE CNA-LR が CVE-2026-52618 を割り当て、s1ko を発見者としてクレジット |
| 2026-08-22 | 本記事を公開 |
aws-mcp-server)同じ調査パスからの関連アドバイザリ: CVE-2026-52616、 CVE-2026-52617。
MIT — LICENSE を参照してください。