Brief · Builders
Nine MCP servers, one mistake: your agent's tools are a shell prompt
On Oct 27, VulnCheck published nine CVEs against nine different MCP servers in about ten seconds, and if you squint, eight of them are the same bug. An agent calls a tool, the tool takes a parameter like a repo name, a file path or an email subject, and the server pastes that string straight into a shell command. If the string contains $(...) or backticks, the shell runs whatever is inside, with the permissions of whoever started the server, which is usually you.
The part worth sitting with is who writes that string. It isn't you, and it often isn't the model either, because it comes from whatever the agent was reading at the time. The VulnCheck record for the ATXP Gemini CLI extension spells it out: "Attackers can plant instructions in email or SMS content to inject $() or backtick substitutions into parameters like message_id, subject, or query, running commands as the user."
None of these are household names, nothing is known to be exploited, and only one has a fix. So this isn't a fire drill, but it is a decent excuse to look at what's actually plugged into your agents.
What's actually confirmed
- Nine CVEs, eight of them command injection. Per the CVE records (VulnCheck is the CNA), eight are OS command injection (CWE-78) and the ninth, nmap-mcp, is a different problem: its allowlist lets
exploit,dos,bruteandintrusiveNSE script categories through to nmap. So it's one mistake rather than one bug, and the accurate count is eight plus a cousin. - One fix so far. Only ai-sdlc has a patch, per ThreatAft and the CVE records: commit 717d7ec ("fix: spawn gh without a shell in check_issue and check_pr_status") landed Oct 23 and shipped on Oct 27 in the plugin's 0.32.0 release, including version 0.32.0 of its MCP server package on npm,
@ai-sdlc/plugin-mcp-server. The GitHub issues for the other eight were all still open when I checked on Oct 28, and four of the CVE records give the affected range as a commit hash, because none of those four projects (esp-mcp, Kali-GPT, nmap-mcp, hagent) has a release newer than the vulnerable code to upgrade to. - Not in KEV, no reported exploitation. None of the nine is in CISA's Known Exploited Vulnerabilities catalog (version 2026.10.27), and ThreatAft says "There are no published indicators of compromise for these CVEs, and no reported in-the-wild exploitation."
- The scores undersell the attack path. The seven 8.5s (CVSS 4.0) are all scored local access, low privileges (AV:L/PR:L), which describes the agent calling the tool, not the attacker who wrote the email it read. ThreatAft's warning is blunt: "Any triage rule that drops AV:L findings, or that treats PR:L as evidence of an insider-only risk, will discard this entire class."
The nine
| CVE | Server | What reaches the shell | Fix (as of Oct 28) |
|---|---|---|---|
| CVE-2026-117558 | ai-sdlc MCP server (through 0.31.2) | repo argument into gh issue view / gh pr view via execSync() |
Commit 717d7ec, released in plugin and @ai-sdlc/plugin-mcp-server 0.32.0 (Oct 27) |
| CVE-2026-117559 | DroidMind (through 0.4.0) | $(...) or backticks slip past its shell allowlist, so blocked commands like reboot run on connected Android devices |
None named |
| CVE-2026-117560 | ATXP Gemini CLI extension (through 1.0.0) | message_id, subject, query and other tool parameters |
None named |
| CVE-2026-117561 | sf-mcp (through 1.3.2) | Salesforce CLI flag values, because its escapeValue() helper doesn't neutralize backticks or $() |
None named |
| CVE-2026-117567 | esp-mcp (through commit 28afddf) | target, port, project_path, project_name via asyncio.create_subprocess_shell |
None named |
| CVE-2026-117568 | mcp-ffmpeg (through 1.0.5) | videoPath and outputDir, so the payload can sit in an existing file or folder name |
None named |
| CVE-2026-117569 | Kali-GPT (through commit a608c74) | Scanner parameters like target, ports, domain, url via subprocess.run(shell=True) |
None named |
| CVE-2026-117570 | nmap-mcp (through commit 3636afa) | Not injection: a permissive NSE script allowlist (CWE-183), scored 4.8 | None named |
| CVE-2026-117573 | hagent (through commit 761eec6) | args to its Arduino and ESP32 tools via subprocess.run(shell=True) |
None named |
Scores are 8.5 (CVSS 4.0) for seven of them, 7.3 for mcp-ffmpeg and 4.8 for nmap-mcp, per the CVE records.
The lesson isn't these nine
DroidMind had a shell allowlist and sf-mcp had an escape helper, and both still lost to $() or backticks, which tells you something about bolting a filter onto a shell. ThreatAft's practical point is the one I'd keep: treat any MCP server that shells out as having this defect until someone reads the code, because the check is a code search, not a CVE lookup. The patterns to grep for are shell=True, create_subprocess_shell, execSync and child_process.exec, and the safe version passes an argument array (execFile in Node, a list without shell=True in Python) so there's no shell to inject into. That's exactly what the ai-sdlc fix did.
It's also the same story as the approval prompt nobody was watching: the dangerous part of an agent is rarely the model, it's the plumbing that turns text into actions.
Meanwhile, in OpenHands
A separate VulnCheck CVE from Oct 26 is the more serious one if you use it. CVE-2026-112346 says the OpenHands Software Agent SDK's DockerWorkspace and Apptainer launchers, before 1.52.0, bound the sandbox agent server to all network interfaces with an empty session_api_keys list, so "Attackers who can reach the published container port can send unauthenticated HTTP and WebSocket requests to execute arbitrary shell commands as root in the sandbox, access the mounted workspace, and read plaintext secrets from the secrets store." It's scored 9.3 (CVSS 4.0) and network-reachable, which is a different league from the nine above.
The good news is the fix is old news: version 1.52.0 shipped Oct 5, and only the disclosure is new. The fix (PR #5209) publishes the DockerWorkspace ports on the host's loopback address instead of every interface. The PR and the release notes only mention DockerWorkspace. The Apptainer launcher's code didn't change in 1.52.0 and still starts the agent server on 0.0.0.0. If you use Apptainer, don't count on the upgrade alone: set a session API key. It's also not in KEV, and the CVE record keeps the damage inside the sandbox: root in the container, the mounted workspace and the secrets store, not the host itself. That's still your workspace and your secrets, though.
The score
I'd call it a 3, patch normally. The bug class is real and the scoring hides it, but these are small projects, nothing is in KEV and nobody has reported exploitation. If you run one of the nine, or an OpenHands SDK older than 1.52.0 (or its Apptainer launcher without a session API key) on a shared network, treat your own copy as more urgent than the score.
What to do:
-
List the MCP servers you actually run.
- Why: these servers are configured per client, in files on each machine, so nobody has a central list unless you make one, and you can't patch what you don't know is installed.
- How: run
claude mcp listfor Claude Code (project servers also live in.mcp.json),codex mcp listfor Codex (servers live under[mcp_servers.*]in~/.codex/config.tomlor a project's.codex/config.toml), and check~/.cursor/mcp.jsonplus any project.cursor/mcp.jsonfor Cursor. Compare the names against the table above, and remember that a config file is code too, as the Downloads folder brief showed.
-
Remove the ones nobody maintains.
- Why: eight of the nine have no fix, and a project whose CVE gives the affected range as a commit hash may never cut a release you can upgrade to.
- How: remove anything you don't use (
claude mcp remove <name>in Claude Code, or delete its block from the client's config file), and drop servers whose last commit is old and whose security issue sits unanswered. For ai-sdlc, update the plugin to 0.32.0 or later and check that the MCP server it runs,@ai-sdlc/plugin-mcp-server, is 0.32.0 or later, the first release with commit 717d7ec. The CVE record still says only "through 0.31.2", so a scanner may not mark 0.32.0 as fixed.
-
Grep the code of every server you keep.
- Why: per ThreatAft, any server that shells out should be assumed to have this defect until checked, and the next batch won't have CVEs yet.
- How: in each server's source, search for
shell=True,create_subprocess_shell,execSyncandchild_process.exec, and look at whether a tool parameter ends up inside that command string. If it does, treat the server as injectable whatever its allowlist says.
-
Run MCP servers as a low-privilege user in a box.
- Why: the injected command runs "as the MCP server user," so the blast radius is whatever that user can touch, including your SSH keys and cloud logins.
- How: per ThreatAft, run it under a dedicated low-privilege account in a container or sandbox with no credentials in its environment. My suggestion, not vendor guidance: for a stdio server, set the client's
commandtodockerwith args likerun --rm -i --user 65534:65534 --cap-drop ALL --security-opt no-new-privileges -v /the/folder/it/needs:/work <image>(keep-i, leave out-t), never mount your home directory,~/.sshor the Docker socket, and add--network noneif the tool doesn't need the internet. More on this in the Builders checklist.
-
Keep approval prompts on, and read the arguments.
- Why: a payload has to pass through a tool call, and an approval prompt shows you that call before it runs, but only if bypass mode is off and someone actually looks.
- How: don't run agents with skip-permissions flags on your main machine. In Claude Code, set
permissions.disableBypassPermissionsModeto"disable"in your settings (or managed settings for a team), and when a prompt shows a repo name or file path containing$(or backticks, say no. The approval prompt brief covers why rubber-stamping defeats the point.
-
If you use the OpenHands SDK, upgrade to 1.52.0 or later and keep the agent server off the network.
- Why: CVE-2026-112346 gives anyone who can reach the sandbox port unauthenticated root shell in the sandbox plus your mounted workspace and stored secrets, and the fix has been out since Oct 5.
- How: check with
pip show openhands-sdk openhands-workspace openhands-agent-serverand upgrade all three (pip install -U openhands-sdk openhands-workspace openhands-agent-server). Then rundocker port <container>and confirm the agent server port maps to127.0.0.1, not0.0.0.0. If you use the Apptainer launcher,docker portwon't apply. SetOH_SESSION_API_KEYS_0(orSESSION_API_KEY) before launching instead, because both launchers forward it and 1.52.0 didn't change how Apptainer binds. My suggestion, not vendor guidance: set that key for Docker too, since the fix changes where the port is published, and never forward the port to a shared network or the internet. If an older SDK ran sandboxes on a shared network, rotate any token that was in its secrets store or mounted workspace.
Want the next one?
One email, weekly-ish. The week's briefs, how worried to be, and what to do about it. No doom.
Subscribe on Beehiiv ↗Free. Opens Beehiiv in a new tab. Unsubscribe whenever, I won't be offended.Sources
- ThreatAft: The MCP Command Injection Wave — Nine Servers, Eight Without a Fix, and a Scoring Model That Hides the Risk (Oct 28, 2026)
- CVE-2026-117558, ai-sdlc MCP server (cve.org)
- CVE-2026-117559, DroidMind (cve.org)
- CVE-2026-117560, ATXP Gemini CLI extension (cve.org)
- CVE-2026-117561, sf-mcp (cve.org)
- CVE-2026-117567, esp-mcp (cve.org)
- CVE-2026-117568, mcp-ffmpeg (cve.org)
- CVE-2026-117569, Kali-GPT (cve.org)
- CVE-2026-117570, nmap-mcp (cve.org)
- CVE-2026-117573, hagent (cve.org)
- VulnCheck advisory: ai-sdlc MCP server through 0.31.2 command injection
- ai-sdlc fix commit 717d7ec: spawn gh without a shell (Oct 23, 2026)
- ai-sdlc plugin v0.32.0 release, includes 717d7ec (Oct 27, 2026)
- CVE-2026-112346, OpenHands Software Agent SDK (cve.org)
- VulnCheck advisory: OpenHands Software Agent SDK unauthenticated sandbox agent server RCE
- OpenHands software-agent-sdk v1.52.0 release (Oct 5, 2026)
- OpenHands PR #5209: publish Docker ports on loopback
- CISA Known Exploited Vulnerabilities catalog (JSON)
- Claude Code docs: MCP
- Claude Code docs: permissions
- OpenAI Codex docs: MCP
- Cursor docs: MCP