{"id":"CVE-2026-67431","aliases":["EUVD-2026-50501","GHSA-5p9g-j988-pcwv"],"status":"published","title":"MCP Ruby SDK: Ruby SSE Session Poisoning","description":"MCP Ruby SDK is the official Ruby SDK for Model Context Protocol servers and clients. Prior to 0.23.0, MCP::Server::Transports::StreamableHTTPTransport in the mcp gem does not bind a session ID to a session owner, allowing an attacker with a stolen session ID to send tools/call requests that execute in the victim's session. This issue is fixed in version 0.23.0.","severity":{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00363,"percentile":0.3057,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-284"],"published":"2026-07-29T19:15:32Z","modified":"2026-10-28T02:31:07Z","fixes":[{"ecosystem":"RubyGems","package":"mcp","fixed_in":["0.23.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"RubyGems","package":"mcp","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"0.22.0"},{"fixed":"0.23.0"}]}]},{"source":"osv","ecosystem":"RubyGems","package":"mcp","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.23.0"}]}],"versions":["0.1.0","0.10.0","0.11.0","0.12.0","0.13.0","0.14.0","0.15.0","0.16.0","0.17.0","0.18.0","0.19.0","0.2.0","0.20.0","0.21.0","0.22.0","0.3.0","0.4.0","0.5.0","0.6.0","0.7.0","0.7.1","0.8.0","0.9.0","0.9.1","0.9.2"]}],"products":[{"vendor":"modelcontextprotocol","product":"ruby-sdk"}],"references":[{"url":"https://github.com/modelcontextprotocol/ruby-sdk/security/advisories/GHSA-5p9g-j988-pcwv","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/modelcontextprotocol/ruby-sdk/commit/35466605319a34e4c7808712ae9bb1ca1afb2356","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.23.0","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-67431","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-5p9g-j988-pcwv"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/67xxx/CVE-2026-67431.json","tags":["advisory"]},{"url":"https://github.com/modelcontextprotocol/ruby-sdk","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-67431","title":"MCP Ruby SDK: Ruby SSE Session Poisoning","description":"MCP Ruby SDK is the official Ruby SDK for Model Context Protocol servers and clients. Prior to 0.23.0, MCP::Server::Transports::StreamableHTTPTransport in the mcp gem does not bind a session ID to a session owner, allowing an attacker with a stolen session ID to send tools/call requests that execute in the victim's session. This issue is fixed in version 0.23.0.","status":"published","published":"2026-07-29T19:15:32.34Z","modified":"2026-07-30T14:35:45.395Z","cvss":[{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high","assessor":"cna"}],"cwes":["CWE-284"],"updated_at":"2026-11-02T14:20:06.562031Z"}],"euvd":[{"id":"EUVD-2026-50501","description":"MCP Ruby SDK is the official Ruby SDK for Model Context Protocol servers and clients. Prior to 0.23.0, MCP::Server::Transports::StreamableHTTPTransport in the mcp gem does not bind a session ID to a session owner, allowing an attacker with a stolen session ID to send tools/call requests that execute in the victim's session. This issue is fixed in version 0.23.0.","status":"published","published":"2026-07-29T19:15:32Z","modified":"2026-07-30T14:35:45Z","cvss":[{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T18:02:28.93005Z"}],"ghsa":[{"id":"GHSA-5p9g-j988-pcwv","title":"MCP Ruby SDK: Ruby SSE Session Poisoning","description":"### Summary\n**Vulnerability**: Missing Session Ownership Validation in the Ruby MCP SDK's Streamable and SSE HTTP transport implementation. Any attacker with a stolen session ID can execute tools with the victim's session. This is a silent attack - the victim's session is compromised and being used for unauthorized actions, but it is hard to know for the victim\n\n### Details\nhttps://github.com/modelcontextprotocol/ruby-sdk/blob/main/lib/mcp/server/transports/streamable_http_transport.rb#L260-L278\n\n**Victim** starts a legitimate MCP session and receives session ID abc-123\n**Attacker** obtains the session ID (various means - network sniffing, logs, etc. out of scope for this analysis)\n**Attacker** sends POST to /messages/abc-123 with a tool call\n**Server** accepts the request (no ownership validation!)\n**Server** executes the tool and sends response to victim's SSE stream\n**Victim** receives attacker's response, thinking it's legitimate\n\n### PoC\n[attacker_client.py](https://github.com/user-attachments/files/26044817/attacker_client.py)\n[legitimate_client.py](https://github.com/user-attachments/files/26044818/legitimate_client.py)\n\n**Prerequisites**\n\n1. Python 3.8+\n2. Install dependencies: `requests`\n\n**Running the Demo**\n\n1. **Terminal 1:** Start the Ruby MCP Server\n\n`ruby streamable_http_server.rb`\n\nMakes use of https://github.com/modelcontextprotocol/ruby-sdk/blob/main/examples/streamable_http_server.rb\nThis server has a tool call notification_tool which the clients call\n\n2. **Terminal 2:**  Start Victim Client\n\n`python3 legitimate_client.py`\n\n3.  **Terminal 3** - Attacker Client:\n\nCopy the session ID from Terminal 1 and run:\n\n```bash\npython3 attacker_client.py abc-123-def-456\n```\n4. **Back to Terminal 2** - Victim sees the injected response:\n\n### Impact\n\n- **Integrity:** HIGH - Attacker can execute unauthorized tools and modify state\n- **Availability:** LOW - Attacker can disrupt victim's session with injected responses\n\n### Additional Details\nSession Hijacking Protection in MCP Implementations\n\nThe MCP specification recommends - \"[MCP servers SHOULD bind session IDs to user-specific information](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-4)\".\n\n#### User Binding - Comparison other SDKs\n**csharp-sdk**\n\n- https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.AspNetCore/SseHandler.cs#L36\n- https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.AspNetCore/SseHandler.cs#L95-L98\n\n**Go-sdk**\n\n- https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/streamable.go#L66\n- https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/streamable.go#L316-L320\n\n#### Comparison with the Stream Replacement vulnerability \n\n**Stream Replacement**\nhttps://github.com/modelcontextprotocol/ruby-sdk/security/advisories/GHSA-qvqr-5cv7-wh35\n\n- **Target**: SSE stream connection\n- **Attack Vector**: GET /mcp\n- **What happens to Victim**: The connection is disconnected and doesn't receive reponses \n\n**Session Poisoning**\n\n- **Target**: Tool execution\n- **Attack Vector**: POST /mcp\n- **What happens to Victim**: The connection stays connected and receives responses of tool calls by attacker","status":"published","published":"2026-07-30T14:44:28Z","modified":"2026-07-30T14:44:29Z","cvss":[{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-284"],"updated_at":"2026-11-02T14:40:54.767849Z"}],"nvd":[{"id":"CVE-2026-67431","description":"MCP Ruby SDK is the official Ruby SDK for Model Context Protocol servers and clients. Prior to 0.23.0, MCP::Server::Transports::StreamableHTTPTransport in the mcp gem does not bind a session ID to a session owner, allowing an attacker with a stolen session ID to send tools/call requests that execute in the victim's session. This issue is fixed in version 0.23.0.","status":"published","published":"2026-07-29T20:17:12.12Z","modified":"2026-10-24T13:10:00.147Z","cvss":[{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"}],"cwes":["CWE-284"],"updated_at":"2026-11-02T14:12:33.629616Z"}],"osv":[{"id":"CVE-2026-67431","title":"MCP Ruby SDK: Ruby SSE Session Poisoning","description":"MCP Ruby SDK is the official Ruby SDK for Model Context Protocol servers and clients. Prior to 0.23.0, MCP::Server::Transports::StreamableHTTPTransport in the mcp gem does not bind a session ID to a session owner, allowing an attacker with a stolen session ID to send tools/call requests that execute in the victim's session. This issue is fixed in version 0.23.0.","status":"published","published":"2026-07-29T19:15:32.34Z","modified":"2026-10-28T02:31:07.853502867Z","cvss":[{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-284"],"updated_at":"2026-11-02T15:45:14.46177Z"},{"id":"GHSA-5p9g-j988-pcwv","title":"MCP Ruby SDK: Ruby SSE Session Poisoning","description":"### Summary\n**Vulnerability**: Missing Session Ownership Validation in the Ruby MCP SDK's Streamable and SSE HTTP transport implementation. Any attacker with a stolen session ID can execute tools with the victim's session. This is a silent attack - the victim's session is compromised and being used for unauthorized actions, but it is hard to know for the victim\n\n### Details\nhttps://github.com/modelcontextprotocol/ruby-sdk/blob/main/lib/mcp/server/transports/streamable_http_transport.rb#L260-L278\n\n**Victim** starts a legitimate MCP session and receives session ID abc-123\n**Attacker** obtains the session ID (various means - network sniffing, logs, etc. out of scope for this analysis)\n**Attacker** sends POST to /messages/abc-123 with a tool call\n**Server** accepts the request (no ownership validation!)\n**Server** executes the tool and sends response to victim's SSE stream\n**Victim** receives attacker's response, thinking it's legitimate\n\n### PoC\n[attacker_client.py](https://github.com/user-attachments/files/26044817/attacker_client.py)\n[legitimate_client.py](https://github.com/user-attachments/files/26044818/legitimate_client.py)\n\n**Prerequisites**\n\n1. Python 3.8+\n2. Install dependencies: `requests`\n\n**Running the Demo**\n\n1. **Terminal 1:** Start the Ruby MCP Server\n\n`ruby streamable_http_server.rb`\n\nMakes use of https://github.com/modelcontextprotocol/ruby-sdk/blob/main/examples/streamable_http_server.rb\nThis server has a tool call notification_tool which the clients call\n\n2. **Terminal 2:**  Start Victim Client\n\n`python3 legitimate_client.py`\n\n3.  **Terminal 3** - Attacker Client:\n\nCopy the session ID from Terminal 1 and run:\n\n```bash\npython3 attacker_client.py abc-123-def-456\n```\n4. **Back to Terminal 2** - Victim sees the injected response:\n\n### Impact\n\n- **Integrity:** HIGH - Attacker can execute unauthorized tools and modify state\n- **Availability:** LOW - Attacker can disrupt victim's session with injected responses\n\n### Additional Details\nSession Hijacking Protection in MCP Implementations\n\nThe MCP specification recommends - \"[MCP servers SHOULD bind session IDs to user-specific information](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices#mitigation-4)\".\n\n#### User Binding - Comparison other SDKs\n**csharp-sdk**\n\n- https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.AspNetCore/SseHandler.cs#L36\n- https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.AspNetCore/SseHandler.cs#L95-L98\n\n**Go-sdk**\n\n- https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/streamable.go#L66\n- https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/streamable.go#L316-L320\n\n#### Comparison with the Stream Replacement vulnerability \n\n**Stream Replacement**\nhttps://github.com/modelcontextprotocol/ruby-sdk/security/advisories/GHSA-qvqr-5cv7-wh35\n\n- **Target**: SSE stream connection\n- **Attack Vector**: GET /mcp\n- **What happens to Victim**: The connection is disconnected and doesn't receive reponses \n\n**Session Poisoning**\n\n- **Target**: Tool execution\n- **Attack Vector**: POST /mcp\n- **What happens to Victim**: The connection stays connected and receives responses of tool calls by attacker","status":"published","published":"2026-07-30T14:44:28Z","modified":"2026-07-30T15:00:31.159150886Z","cvss":[{"version":"4.0","score":8.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-284"],"updated_at":"2026-11-02T15:46:38.553953Z"}]}}
