
Documents CVE-2026-52618 with a PoC for OS command injection in @webfer/mcp-ansible-drupal via executeDeployment extraVars, plus detection guidance and mitigations.
executeDeployment extra varsspawn(cmd[0], cmd.slice(1), { shell: true }) re-joins an argument array into a
single /bin/sh -c string without escaping, so a shell metacharacter in an
extraVars value supplied to the executeDeployment MCP tool executes on the
host running the server.
This issue is fixed. Upgrade to 2.0.3 or later.
MCP tool arguments are attacker-influenced. They are produced by an LLM from
the content the agent processes — documents, web pages, tool output — so
indirect prompt injection puts an attacker in control of the values a tool
handler receives. A handler that feeds any of those values into a shell turns
that influence into code execution on the host running the MCP server. This is
the same model as the precedent case aws-mcp-server / CVE-2026-5058.
The consequence is that "the caller is a trusted LLM" is not a mitigation. Tool arguments are untrusted input.
src/helpers/runAnsible.ts in 2.0.0 built an argument array and then ran it
with the shell enabled:
const proc = spawn(ansibleCmd[0], ansibleCmd.slice(1), { shell: true });
With shell: true Node does not execute ansibleCmd[0] with the remaining
elements as argv. It joins the whole array into one string and hands it to
/bin/sh -c, with no quoting or escaping applied to the elements. Any element
carrying ;, &&, |, backticks or $() therefore breaks out of the intended
command.
The reachable path is the executeDeployment MCP tool
(src/tools/executeDeployment.ts), which forwards caller-supplied extraVars
into the array as --extra-vars key=value. A value of ; touch /tmp/marker ;
yields:
/bin/sh -c "ansible-playbook -i inv.ini play.yml ... --extra-vars deploy_env=; touch /tmp/marker ; echo INJECTED"
A secondary sink in the same version interpolated projectRoot into
execSync('ansible-vault encrypt ' + vaultPath).
poc/driver.mjs calls runAnsible directly with a crafted
extraVars value and checks for the marker file. ansible-playbook does not
need to be installed — the injected command runs in the same /bin/sh -c
string regardless of whether the leading binary resolves.
npm pack @webfer/[email protected] && tar xf webfer-mcp-ansible-drupal-2.0.0.tgz
node poc/driver.mjs
Expected on an affected version:
{"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"}
Validated 2026-06-01 in an isolated container and re-validated 2026-06-13 with
a benign touch marker. Run it only against infrastructure you are authorized
to test.
--extra-vars value reaching ansible-playbook that contains ;, &&,
||, |, a backtick or $(. Legitimate Ansible variable values essentially
never do.sh -c process whose command line contains ansible-playbook and a
second command after a separator — a process-tree signal (auditd, eBPF,
Falco, EDR) that does not depend on application logging.ansible-playbook, git and
ansible-vault are expected; a shell, an interpreter or a network client is
not.executeDeployment arguments contain shell
metacharacters. Log tool arguments — most MCP deployments do not, and it is
the only place the injected value is visible in application terms.MITRE ATT&CK T1059.004 Command and Scripting Interpreter: Unix Shell.
Upgrade to @webfer/mcp-ansible-drupal 2.0.3 or later. The current source
calls spawn(ansibleCmd[0], ansibleCmd.slice(1), { … }) with no shell option,
so the array is passed as argv and metacharacters lose their meaning.
The general rule for MCP tool handlers, and what the fix applies here:
shell: true when an argument array is already available. The
array form exists precisely to avoid the shell.execFile/spawn with an explicit argument vector.z.string() proves the value is a
string and nothing about what is in it. Allowlist where the value set is
known; reject shell metacharacters where it is not.NIST SP 800-53r5 SI-10; OWASP ASVS v4 §5.3.8; CWE-78 mitigations M1 and M2.
aws-mcp-server)Companion advisories from the same research pass: CVE-2026-52616, CVE-2026-52617.
MIT — see LICENSE.
| CVE | CVE-2026-52618 |
| CWE | CWE-78 (Improper Neutralization of Special Elements used in an OS Command) |
| Package | @webfer/mcp-ansible-drupal (npm) |
| Affected | 2.0.0 |
| Fixed in | 2.0.3 (published 2026-06-01) |
| CVSS v3.1 | AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 7.8 High (stdio transport). AV:N → 9.8 Critical if the server is wrapped in a network transport. |
| Reported by | s1ko (github.com/s1ko, [email protected]) |
| CVE assigned | 2026-07-13, MITRE CNA-LR |
| Date |
|---|
| Event |
|---|
| 2026-06-01 | Vulnerability dynamically validated against 2.0.0 |
| 2026-06-01 | 2.0.3 published to npm with the shell: true removed |
| 2026-06-13 | Re-validated on a second host |
| 2026-07-13 | MITRE CNA-LR assigns CVE-2026-52618, s1ko credited as discoverer |
| 2026-08-22 | This write-up published |