CVE-2026-38924: Unauthenticated RCE in the Serena MCP Server

CVE-2026-38924: Unauthenticated RCE in the Serena MCP Server

BY Ziv Eli

MCP

Vulnerability Research

7 MIN READ

Affected: serena-agent <= 0.1.4, when started with --transport sse or --transport streamable-http. Patched on March 29th 2026 (commit b00ae29);
CVE-2026-38924 was assigned by MITRE and is pending publication on cve.org at the time of writing.

A developer runs serena start-mcp-server --transport sse on a cloud VM. Out of the box, the server binds to 0.0.0.0:8000 with no authentication. Every tool it exposes, including shell execution, is now reachable from the network.

The process has the developer's permissions with access to their SSH keys, their AWS credentials, their .env files, their source code and more - that’s by design.
Serena exposes those capabilities as callable tools over HTTP, with no authentication in front. An attacker who reaches the port gets the same tool surface as the agent, including shell execution. In affected versions, that took 4 HTTP requests and no credentials.

Serena RCE POC - whoami and list fake ssh key via an mcp tool remote code execution

Serena RCE POC - whoami and list fake ssh key via an mcp tool remote code execution


What is Serena?

Serena is an open-source MCP server that gives AI coding agents semantic retrieval and editing capabilities across a codebase. Think of it as a backend that lets an LLM read, search, and modify source code through MCP. It ships as the serena-agent package on PyPI. The project has over 26K GitHub stars, 1,700 forks, and roughly 89K PyPI downloads in the last month, making it one of the more widely adopted MCP servers available.

MCP servers expose local capabilities to AI models over a structured JSON-RPC interface including file access, code search, and shell execution. They typically run on the developer's machine. Serena supports three transports: stdio (direct integration), SSE, and streamable HTTP. The latter two open a network socket.

How I found it

I was auditing MCP servers as part of broader research into the attack surface that AI coding agents expose. Serena caught my attention because it ships an execute_shell_command tool, which is a powerful capability for any MCP server to offer.

Binding to 0.0.0.0:8000

The first thing I checked was the transport layer. Running serena start-mcp-server --transport sse and inspecting the listening socket showed it binding to 0.0.0.0:8000. That means every interface on the machine, not just localhost. I checked the code to confirm this was not an accident: the create_mcp_server method in src/serena/mcp.py hardcoded host="0.0.0.0" as the default, and the CLI flag in src/serena/cli.py did the same.

No Authentication Required

The next question was authentication. I searched for auth middleware, token validation or any gating at all, but there was none. The FastMCP server instantiated at src/serena/mcp.py accepted any connection unconditionally. Anyone who can reach the port can complete the JSON-RPC handshake. On a cloud VM, that could be the entire internet.

RCE: Toxic Combination

At that point the problem was obvious. A network-reachable server with zero authentication exposes execute_shell_command, which passes its input directly to subprocess.Popen with shell=True. Three issues that are each manageable in isolation, but in combination give you an unauthenticated RCE on a default install.

Root cause

Three conditions, all present in versions <= 0.1.4: network exposure by default (0.0.0.0), no authentication on HTTP transports, and unsanitized shell execution via subprocess.Popen with shell=True.

The vulnerable code path in src/serena/util/shell.py:

# Simplified from src/serena/util/shell.py, lines 28-31
process = subprocess.Popen(
    command,        # attacker-controlled string
    shell=True,     # interpreted by /bin/sh
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
# Simplified from src/serena/util/shell.py, lines 28-31
process = subprocess.Popen(
    command,        # attacker-controlled string
    shell=True,     # interpreted by /bin/sh
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
# Simplified from src/serena/util/shell.py, lines 28-31
process = subprocess.Popen(
    command,        # attacker-controlled string
    shell=True,     # interpreted by /bin/sh
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)

From the attacker's side, the flow is straightforward. 

First, open a connection to the /sse endpoint and read back a session ID from the event stream. Second, send the MCP initialize handshake over the /messages/ endpoint. No API key or token. Third, call tools/list and confirm that execute_shell_command is available in the default tool set. Fourth, call tools/call with that tool name and any shell command. The server runs it as the Serena process user and returns the output.
Four HTTP requests - no credentials at any step.

A secondary issue deepens the exposure. Serena does have a read-only check in one place: when a caller uses the MCP tool QueryProjectTool to invoke other tools indirectly, Serena verifies the target tool is read-only before running it. That gate would block execute_shell_command through that path. But the ProjectServer class exposes a separate /query_project HTTP endpoint that calls the same tools directly, without that check. So even if execute_shell_command were removed from the default MCP tool set (closing the primary path), an attacker could still reach it through this second endpoint.

Impact

Any remote attacker who can reach port 8000 can execute arbitrary operating system commands as the user running Serena. On a host with a public IP and no inbound firewall rule, that means anyone on the internet. On a host behind NAT, that still includes every peer on the local network (e.g LAN, shared WiFi, cloud VPC). Concretely, this puts the endpoint at risk for:

  1. Credential theft: Read ~/.ssh/, ~/.aws/credentials, .env files, API tokens, and anything accessible to the process user.

  2. Filesystem access: Read or modify any file the user can access, including source code, configuration, and databases.

  3. Persistent access: Install a reverse shell, add SSH keys, or create cron jobs that survive the Serena process shutting down.

  4. Lateral movement: Use stolen credentials or network access to pivot to other systems.

The default 0.0.0.0 bind maximized the exposure window. Anyone running Serena with --transport sse or --transport streamable-http without explicitly overriding the host was reachable, potentially from the public internet.

Disclosure timeline

Date

Event

2026-03-29

Reported via GitHub Security Advisory (GHSA-m922-r24v-6wff) to maintainer opcode81.

2026-03-29 (same day)

Maintainer acknowledged the 0.0.0.0 binding issue and pushed fix commit b00ae29, changing the default listen address to 127.0.0.1 and updating documentation.

2026-03-29

Maintainer closed the advisory as "informational," declining to issue a CVE. Position: the threat model and impact were limited, and users on main receive the fix automatically.

2026-06-10

CVE-2026-38924 assigned by MITRE (CVE Assignment Team, service request 2016407). MITRE's description: "An issue in Oraios AI serena-agent v.0.1.4 and before allows a remote attacker to execute arbitrary code because the system binds its MCP HTTP/SSE server to 0.0.0.0 by default with no authentication."

The maintainer, opcode81, responded within hours and shipped the fix the same day. That is fast by any standard, and the binding change to 127.0.0.1 was the right remediation for the network exposure component of the chain.

The maintainer considered the binding the core issue and closed the advisory as informational. I requested CVE-2026-38924 through MITRE separately so that users on older versions would have a citable reference. The vulnerability is patched in current code.

The GHSA filing carries a CVSS v3.1 base score of 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), scored by the reporter. NVD had not published an independent score at the time of writing. The vector metrics reflect the factual properties of the bug: network attack vector, low complexity, no privileges required, no user interaction, full impact to confidentiality, integrity, and availability.

Users running serena-agent <= 0.1.4 with --transport sse or --transport streamable-http should update immediately. Users on current versions that include commit b00ae29 or later are not exposed to this network-reachable attack path. 

Takeaway

This is not a novel class of vulnerability - missing authentication plus shell injection is a well known combination. What makes it relevant is the context: MCP servers are proliferating fast, they run with the user's full permissions, and they expose powerful system-level tools by design.

If you maintain an MCP server that supports HTTP or SSE transports, three checks from this finding:

  1. Bind to localhost by default: If the server does not need to be network-reachable, default to 127.0.0.1. Require an explicit, documented opt-in for 0.0.0.0.

  2. Authenticate network callers: stdio-based MCP servers inherit the parent process's security boundary. HTTP-based transports do not. If the server exposes tools that can modify state or execute commands, any network caller should be authenticated before reaching them.

  3. Gate high-privilege tools: A tool that calls subprocess.Popen(command, shell=True) should not be in the default set for network-facing transports. Require explicit opt-in, or exclude it from HTTP/SSE entirely.

The MCP ecosystem is moving fast, securing its place as one of the main components in the modern software supply chain. Developers are wiring language models into their environments with tools that have broad system access. Those tools deserve the same scrutiny as any other network service handling untrusted input.

© 2026 Dash Security, INC

|

© 2026 Dash Security, INC