🛡️ CVE-2026-63118 — mcp
Description
MCP Ruby SDK: Streamable HTTP transport lacks DNS-rebinding (Host/Origin) protection
Summary
MCP::Server::Transports::StreamableHTTPTransport (the Rack-mountable Streamable HTTP transport in the mcp gem) processes every incoming JSON-RPC request without ever inspecting the HTTP Host or Origin request headers. There is no AllowedHosts/AllowedOrigins allowlist and no DNS-rebinding guard anywhere in the transport. A local MCP server that binds a loopback or LAN HTTP port is therefore reachable by any web origin a victim's browser visits, via a DNS-rebinding attack: a malicious page rebinds its own hostname to 127.0.0.1, then drives the local MCP server cross-origin to enumerate and invoke its tools and exfiltrate their output. This is the standard browser-driven local-service attack that the MCP Streamable HTTP guidance exists to prevent.
Impact
- An attacker who can get a victim to open a web page can reach any MCP server the victim runs locally over the Streamable HTTP transport (e.g. a developer-tools or filesystem MCP server on
localhost). - Because the transport issues a session and dispatches
tools/list/tools/callfrom a foreignHost/Originwith no rejection, the attacker can drive arbitrary server-exposed tools and read their results, exfiltrating local data (files, secrets, command output) to the attacker's origin. - The blast radius is whatever the locally-running MCP server exposes. For MCP servers wired to filesystem, shell, or credential tools, this is sensitive-data disclosure and, depending on the tool set, local action execution.
Vulnerable code
File: lib/mcp/server/transports/streamable_http_transport.rb (gem mcp 0.18.0).
The Rack entrypoint and POST handler validate Accept, Content-Type, Mcp-Session-Id, and Mcp-Protocol-Version, but never Host or Origin:
```ruby
# call(env) -> handle_request(Rack::Request.new(env)) (line 56)
def handle_post(request)
required_types = @enable_json_response ? REQUIRED_POST_ACCEPT_TYPES_JSON : REQUIRED_POST_ACCEPT_TYPES_SSE
accept_error = validate_accept_header(request, required_types) # line 335 - checks Accept only
return accept_error if accept_error
content_type_error = validate_content_type(request) # line 338 - checks Content-Type only
return content_type_error if content_type_error
body_string = request.body.read
session_id = extract_session_id(request) # line 342 - reads HTTP_MCP_SESSION_ID
```
No statement anywhere in handle_post, handle_request, or any helper reads request.env["HTTP_HOST"] or request.env["HTTP_ORIGIN"].
The only request-env reads in the whole class are:
extract_session_id->request.env["HTTP_MCP_SESSION_ID"](line 489)validate_accept_header->request.env["HTTP_ACCEPT"](line 493)validate_content_type->request.env["CONTENT_TYPE"](line 512)validate_protocol_version_header->request.env["HTTP_MCP_PROTOCOL_VERSION"](line 546)
A repository-wide search of lib/ for HTTP_HOST, HTTP_ORIGIN, allowed_host, allowed_origin, rebind, or dns.rebind returns zero matches, confirming no allowlist or rebinding guard exists in the shipped library. The examples/ tree mounts Rack::Cors as application-level middleware, but that is example glue, not a transport-level control, and CORS does not stop a DNS-rebinding attack that arrives as a same-origin request after rebinding.
How the input reaches the sink (attack scenario)
1. A developer runs an MCP server over StreamableHTTPTransport, mounted as a Rack app on a local HTTP port (loopback or LAN).
2. The victim opens http://evil.attacker.com in a browser. The page resolves to the attacker's server, which then re-answers DNS for evil.attacker.com with 127.0.0.1 (DNS rebinding). The browser now treats requests to evil.attacker.com as going to the local MCP server, with Host: evil.attacker.com / Origin: http://evil.attacker.com.
3. The page POSTs an initialize request. The transport accepts it (it never looks at Host/Origin), creates a session, and returns Mcp-Session-Id.
4. The page then POSTs tools/call, and the transport executes the server's tool and returns its output to the foreign origin. Local data is exfiltrated.
Proof of concept (end-to-end reproduction)
Run against the real released gem mcp 0.18.0 (no stubs). The script builds an MCP::Server with a tool that returns sensitive local data, instantiates the real StreamableHTTPTransport, and drives it with Rack::Request env hashes carrying a forged Host/Origin. It then re-runs as a legitimate localhost client (negative control).
Install:
```
gem install mcp -v 0.18.0 # pulls addressable, json-schema, public_suffix
gem install rack # required by StreamableHTTPTransport
```
PoC (poc_f1_dnsrebind.rb):
```ruby
# frozen_string_literal: true
require "mcp"
require "rack"
require "json"
require "stringio"
puts "mcp gem version un
How this vulnerability can be exploited
This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. A user must actively cooperate. Rated impact: confidentiality high, integrity none, availability none.
Weakness class
CVE-2026-63118 is classified as CWE-346: Origin Validation Error. The origin of a message or request is not verified properly, so one source can pose as another.
Affected software
CVE-2026-63118 is recorded against 2 packages.
- mcp (fixed in 0.23.0)
- unknown
Timeline and source
Published on 30 July 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.
References
github.com (Web)
nvd.nist.gov (Advisory)
github.com (Web)
github.com (Package)
github.com (Web)
Details
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| mcp | — | 0.23.0 |
| unknown | — | — |
Similar Threats
- High ROOT-APP-PYPI-CVE-2025-66416
- High CVE-2026-67431
- Medium CVE-2026-63119
- Medium CVE-2026-67430
- High CVE-2026-67432
More CVE 2026 advisories
Browse all of CVE 2026 in the advisory index.
Free Vulnerability Check
Is your site affected by CVE-2026-63118?
BotEraser helps you identify potentially vulnerable plugins and themes by checking your installation against CVE-2026-63118 and other known CVE records.
Scan My Site Free →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.