## https://sploitus.com/exploit?id=5C5A7971-4F01-5F90-A72C-A9BB9373CCF7
# CVE-2026-52618 β @webfer/mcp-ansible-drupal: OS command injection via `executeDeployment` extra vars
`spawn(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.
| | |
|---|---|
| CVE | CVE-2026-52618 |
| CWE | CWE-78 (Improper Neutralization of Special Elements used in an OS Command) |
| Package | [`@webfer/mcp-ansible-drupal`](https://www.npmjs.com/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](https://github.com/s1ko), `s1ko@riseup.net`) |
| CVE assigned | 2026-07-13, MITRE CNA-LR |
**This issue is fixed. Upgrade to 2.0.3 or later.**
## Threat model
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.
## Execution
`src/helpers/runAnsible.ts` in 2.0.0 built an argument array and then ran it
with the shell enabled:
```js
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)`.
### Reproduction
[`poc/driver.mjs`](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/mcp-ansible-drupal@2.0.0 && tar xf webfer-mcp-ansible-drupal-2.0.0.tgz
node poc/driver.mjs
```
Expected on an affected version:
```json
{"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.
## Detection
- Any `--extra-vars` value reaching `ansible-playbook` that contains `;`, `&&`,
`||`, `|`, a backtick or `$(`. Legitimate Ansible variable values essentially
never do.
- A `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.
- Unexpected children of the MCP server process. `ansible-playbook`, `git` and
`ansible-vault` are expected; a shell, an interpreter or a network client is
not.
- MCP tool-call logs where `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](https://attack.mitre.org/techniques/T1059/004/).
## Mitigation
**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:
- Never enable `shell: true` when an argument array is already available. The
array form exists precisely to avoid the shell.
- Never build a command by string interpolation of a tool argument. Use
`execFile`/`spawn` with an explicit argument vector.
- Validate content, not just type. A Zod `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.
- Run the MCP server as an unprivileged user with the narrowest filesystem and
network reach the tool actually needs.
NIST SP 800-53r5 `SI-10`; OWASP ASVS v4 Β§5.3.8; CWE-78 mitigations M1 and M2.
## Timeline
| 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 |
## References
- npm package β https://www.npmjs.com/package/@webfer/mcp-ansible-drupal
- Repository β https://github.com/webfer/MCP-Ansible-Drupal
- Precedent for the MCP threat model β CVE-2026-5058 (`aws-mcp-server`)
Companion advisories from the same research pass:
[CVE-2026-52616](https://github.com/s1ko/CVE-2026-52616),
[CVE-2026-52617](https://github.com/s1ko/CVE-2026-52617).
## License
MIT β see [LICENSE](LICENSE).