Command Blacklist Bypass in hdresearch/mcp-shell (CVE-2026-78811)

mcp-shell is an MCP server that lets an AI client such as Claude Desktop run shell commands. It advertises "secure command execution" through a blacklist of dangerous commands. The blacklist checks only the first word of the command, and then the entire string is handed to a shell. Anything after a ; runs unchecked.
CVE: CVE-2026-78811 · Severity: High (CVSS 4.0: 8.5 · CVSS 3.1: 7.8) · CWE: CWE-78, OS Command Injection · Affected: hdresearch/mcp-shell run_command MCP tool, all commits through e522726b533d46ee442e5710bf0ba46ad6142945 (December 15, 2025). No tagged releases. No fix known — repository HEAD is still e522726 as of October 28, 2026.
What Goes Wrong
The run_command handler in src/index.ts does the following:
const command = request.params.arguments?.command as string;
const baseCommand = command.trim().split(/\s+/)[0];
if (!(await commandExists(baseCommand))) {
throw new Error(`Command not found: ${baseCommand}`);
}
if (!validateCommand(baseCommand)) {
throw new Error(`Command not allowed: ${baseCommand}`);
}
const { stdout, stderr } = await execa(command, [], {
shell: true,
env: process.env,
});
validateCommand only asks whether that first word is in a set of 20 or so names (rm, rmdir, sudo, dd, and similar). The full string then runs with shell: true, so ;, &&, |, $() and backticks are all live. The first word decides whether the command is allowed, and the rest runs unchecked.
They say the first impression is the most lasting. This blacklist took that to heart: it reads the first word, forms its opinion, and never looks again. Everything after the semicolon gets in on the strength of a polite ls.
Proof of Concept
This is the same harmless command reported in the public issue (hdresearch/mcp-shell#17):
ls ; rmdir TEST_TO_REMOVE
ls passes the check. rmdir is on the blacklist, but it never reaches it, so the directory is removed.
Even Without Metacharacters, the Blacklist Is Thin
The list does not include sh, bash, python, curl, mv or many other commands, so a blacklisted action can usually be done another way. Patching the shell injection alone would not make the tool safe to expose to untrusted input.
Impact
Arbitrary command execution as the user running the MCP server: file changes or deletion, reading data, and anything else that user can do. The server runs over stdio under an MCP client, so the attacker is whoever controls the command argument. That could be a malicious or compromised client, another MCP server in the same session, or content that steers an AI agent into making a crafted call (prompt injection through a web page, document, or tool output). Users who treat the blacklist as a safety boundary are the ones exposed.
What To Do
No patched version exists. Until one does:
- Don't run mcp-shell against untrusted content, documents, or other MCP servers.
- Run it in a container or VM with no credentials mounted.
- Require human approval for every tool call, and read the command before approving.
For maintainers:
- Run commands without a shell: pass the executable and an argument array to
execa. - Replace the blacklist with an allowlist of permitted executables, with validated arguments.
- Or document the tool plainly as unsafe, and remove the "secure" claims from the README.
Disclosure Timeline
| Date | Event |
|---|---|
| Jan 28, 2026 | Reported publicly as hdresearch/mcp-shell#17, since the repository has no SECURITY.md or private contact |
| Jun 13, 2026 | CVE requested from MITRE |
| Sep 2, 2026 | CVE-2026-78811 assigned |
| Oct 28, 2026 | Repository HEAD still e522726; no fix committed. Advisory published |
References
- CVE-2026-78811 (cve.org)
- Public issue #17
- Source at the affected commit:
src/index.ts - hdresearch/mcp-shell
The Big Picture
MCP servers often get the same first-impression treatment from the teams that install them. One look at the README, one line in a config file, and no second glance while the server runs with a developer's full privileges.
mcp-audit gives security teams visibility into which MCP servers are connected across developer machines, what tools they expose, and where the risk is. A tool called run_command should show up in your inventory before it shows up in a CVE.
Credit
Discovered and reported by Bandana (HackWitHer), APIsec Research Labs.