Jul 31, 2026·7 min read·2 visits
The `mcp` Ruby gem prior to 0.23.0 is vulnerable to unauthenticated, remote denial of service (OOM crash) due to unbounded HTTP body reading, unsafe JSON parsing depth, and unlimited standard input buffering.
An uncontrolled memory allocation vulnerability (CWE-770) exists in the Model Context Protocol (MCP) Ruby SDK (the `mcp` gem) prior to version 0.23.0. The SDK's StreamableHTTPTransport and StdioTransport layers fail to impose bounds on incoming payloads. An unauthenticated attacker can exploit these issues by transmitting massive, nested payloads to exhaust worker memory, leading to process termination.
The Model Context Protocol (MCP) is a protocol that facilitates secure integrations between Large Language Models (LLMs) and client environments. The MCP Ruby SDK (the mcp gem) provides the core primitives necessary to construct both MCP servers and client components. This SDK relies on distinct transport architectures, including HTTP-based request/response cycles (StreamableHTTPTransport) and standard input/output streams (StdioTransport). Because these transport layers interface directly with untrusted remote inputs, they form the primary attack surface of the application.
Prior to version 0.23.0, the mcp gem suffered from multiple resource validation omissions classified under CWE-770 (Allocation of Resources Without Limits or Throttling). Specifically, the transport handlers failed to inspect incoming request metadata or enforce physical constraints on the stream sizes before allocating memory blocks. Consequently, both remote and local actors could issue highly resource-intensive payloads, leading to rapid exhaustion of physical system memory.
This vulnerability introduces a High severity threat to availability. Exploitation results in immediate worker termination via the kernel's Out-Of-Memory (OOM) killer mechanism. Because these endpoints are commonly exposed to accept event-driven payloads from client applications, an unauthenticated network adversary can trigger the crash, generating an immediate and complete denial of service across the affected environment.
The underlying vulnerability is rooted in two distinct architectural transport pathways: the HTTP POST handling interface and the standard I/O processing loop. Each pathway fails to bound dynamic input aggregation, allowing untrusted actors to directly dictate heap allocation scales.
Within StreamableHTTPTransport (located at lib/mcp/server/transports/streamable_http_transport.rb), the server implements a handle_post method to process incoming HTTP POST request blocks. The application logic retrieves the raw body payload via request.body.read without passing a length limit argument or validating the value of the incoming HTTP Content-Length header. This forces the underlying Rack web server implementation to read and buffer the incoming socket stream until an End-Of-File (EOF) is encountered. When the input stream is massive, the Ruby engine attempts to construct an equivalently large contiguous string on the heap.
Following the raw string ingestion, the application passes the buffer to JSON.parse(body_string, symbolize_names: true). This operation exponentially multiplies memory consumption in three ways:
symbolize_names: true argument enforces the dynamic allocation of Ruby symbols for every key within the JSON payload. Dynamic symbols cannot be garbage collected efficiently, resulting in rapid Garbage Collection (GC) thrashing and high CPU utilization.In parallel, the standard input/output transport loop in StdioTransport ingested commands using the line-by-line reading method while @open && (line = $stdin.gets). Ruby's native IO#gets reads characters until it identifies the defined line terminator (\n). If a connected peer sends a stream of bytes containing no newline delimiters, the interpreter continuously appends the characters to an internal buffer. Memory usage increases proportionally with the duration of the stream, eventually causing host-wide memory exhaustion.
To understand the vulnerability and its corresponding fix, we inspect the changes introduced in commit 772e0cb1f9db69312006926eee59a7287ad50166. The patch targets both HTTP and Stdio layers, wrapping the reads in explicit boundary checks.
In the HTTP transport layer, the developer defined new hard limits to govern request handling:
# File: lib/mcp/server/transports/streamable_http_transport.rb
# Default upper bound on the JSON-RPC request body to prevent worker OOM.
DEFAULT_MAX_REQUEST_BYTES = 4 * 1024 * 1024
# Conservative bound on JSON nesting depth to protect the parser stack.
MAX_JSON_NESTING = 64The insecure request.body.read process was refactored. The application now evaluates the payload size through the newly introduced helper, read_bounded_body:
def read_bounded_body(request)
content_length = request.content_length
return if content_length && content_length.to_i > @max_request_bytes
# Reads exactly max_request_bytes + 1 to detect dynamic overflow
body = request.body.read(@max_request_bytes + 1)
return "" if body.nil?
return if body.bytesize > @max_request_bytes
body
endBy requesting @max_request_bytes + 1 bytes, the system can detect overflow attempts even when the client spoofs or omits the Content-Length header (such as when using chunked transfer encoding). If the buffer size exceeds the threshold, the function immediately returns nil, aborting allocation.
Additionally, the JSON deserializer is restricted to the safe nesting depth:
def parse_request_body(body_string)
JSON.parse(body_string, symbolize_names: true, max_nesting: MAX_JSON_NESTING)
rescue JSON::ParserError, TypeError
raise InvalidJsonError
endIn the StdioTransport layer, the unconstrained gets call was refactored to limit maximum line lengths:
def read_line(method, params)
line = @stdout.gets("\n", @max_line_bytes)
return line unless line && !line.end_with?("\n") && line.bytesize >= @max_line_bytes
# Error generation logic handles excess sizes immediatelyThis defensive logic effectively isolates the Ruby execution environment from resource depletion. The diagram below illustrates the input validation pipeline introduced in the patched version:
Exploiting CVE-2026-67432 requires zero authentication or special privileges. Since the HTTP POST handling path parses JSON requests before applying authentication hooks, any client with access to the endpoint can execute the attack.
An attacker targeting the HTTP interface can perform the following steps to trigger a denial of service:
/ with standard headers, but with an extremely large payload size (e.g., 512 MB).In localized configurations where the application leverages StdioTransport to communicate with client plugins, exploitation occurs through dynamic process communication. An untrusted plugin process can continuously write bytes to its stdout stream without inserting a newline (\n) character. The host program tries to accumulate this stream in memory, causing the parent process to exhaust its memory pool and crash.
The potential consequences of CVE-2026-67432 are severe for target availability. The CVSS base score of 7.5 highlights its potential impact as an unauthenticated remote exploit with low complexity.
When a worker process experiences an Out-Of-Memory condition, the operating system terminates the process immediately. In standard configurations, this termination causes an abrupt interruption of any ongoing transactions, user sessions, or LLM-driven actions. If the target environment does not employ process monitoring tools (like Systemd, Kubernetes liveness probes, or Supervisord) to auto-restart crashed processes, the application remains completely unavailable.
Even in environments configured to auto-restart, continuous exploitation (an attack loop) can lead to infinite crash cycles. This exhausts server processing resources, floods monitoring tools with alerts, and prevents any legitimate traffic from being processed. Additionally, because the memory consumption occurs during the raw read phase, it bypasses standard application-level logging, hindering post-incident forensic analysis.
To address this vulnerability, security teams must upgrade the mcp gem to version 0.23.0 or later. This release introduces safe defaults (4 MiB limit) and nesting protections for both the HTTP and Stdio transport systems.
To update the dependency within a Bundler-managed Ruby application, modify the Gemfile:
gem 'mcp', '>= 0.23.0'Following the modification, apply the update across the environment:
bundle update mcpFor deployments where immediate patching is not feasible, implement temporary mitigations on your reverse proxies or Web Application Firewalls (WAFs). For instance, configure Cloudflare or Nginx to limit client request bodies to 4 MiB on paths routed to MCP endpoints. This blocks large payloads before they reach the Ruby worker processes.
# Example Nginx restriction for MCP locations
location /mcp {
client_max_body_size 4M;
proxy_pass http://mcp_backend_server;
}CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
mcp Model Context Protocol | < 0.23.0 | 0.23.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-770 |
| Attack Vector | Network |
| CVSS v3.1 Score | 7.5 (High) |
| EPSS Score | 0.00436 (35.87%) |
| Impact | Denial of Service (OOM Crash) |
| Exploit Status | Proof-of-Concept |
| CISA KEV Status | Not Listed |
The software allocates memory or other resources on behalf of an untrusted actor without checking or enforcing limits on the requested allocation size.
CVE-2026-67430 is a medium-severity Denial of Service (DoS) vulnerability in the Model Context Protocol (MCP) Ruby SDK (packaged as the mcp gem) versions prior to 0.23.0. In stateful deployments using the StreamableHTTPTransport class, client session states are retained in an in-memory hash map. Because the transport implements a nil idle timeout by default, the background scavenger process is suppressed. Remote, unauthenticated attackers can flood the endpoint with initialize requests, rapidly consuming system memory and triggering an Out-of-Memory (OOM) crash.
An Improper Access Control vulnerability exists in the Model Context Protocol (MCP) Ruby SDK prior to version 0.23.0. The stateful transport implementation failed to bind established sessions to their original owners or connection contexts, enabling unauthorized actors with access to active session IDs to execute arbitrary tools or alter session state.
CVE-2026-67435 is a security vulnerability in the linuxfabrik-lib Python library prior to version 6.0.0. When performing HTTP requests with follow_redirects enabled, custom authentication headers (such as X-Auth-Token or X-Api-Key) are forwarded during cross-origin redirects. A malicious or compromised server can leverage this behavior to capture sensitive monitoring and administrative credentials, leading to potential unauthorized access and Server-Side Request Forgery (SSRF).
CVE-2026-67429 is a critical path traversal vulnerability in Flyto2 Core file-writing modules, including image.download and twelve other modules. By exploiting an insecure validation check that relied on user-controlled parameters, unauthenticated remote attackers can bypass directory confinement and write arbitrary files to the file system, leading to remote code execution.
CVE-2026-67427 is a capability bypass vulnerability in the Flyto2 Core workflow execution kernel. Due to a logical inconsistency in how dynamic parameters are resolved, the system evaluates environment variables via template interpolation prior to executing capability filter validation. This permits unprivileged workflow definitions to completely bypass denylist restrictions on the `env.get` module, exfiltrating critical host configurations, API tokens, and credentials via allowed communication channels.
An insecure credential forwarding vulnerability in Flyto2 Core prior to version 2.26.6 allows attackers to exfiltrate operator API keys. This occurs because the system forwards environment-derived API keys to user-controlled custom endpoints, bypassing SSRF guards designed only for private target validation.