MCP Ruby SDK: Ruby SSE Session PoisoningGHSA-5p9g-j988-pcwv
Fix: modelcontextprotocol/ruby-sdk@3546660GHSA-5p9g-j988-pcwv is a CWE-284 vulnerability in mcp. A fix is available for mcp — see the affected versions and patch details below.
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for GHSA-5p9g-j988-pcwv.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
Real-World Exposure
mcpReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects RubyGems packages — download data is not available via public APIs for these ecosystems.
Description
Summary
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
Details
Victim starts a legitimate MCP session and receives session ID abc-123 Attacker obtains the session ID (various means - network sniffing, logs, etc. out of scope for this analysis) Attacker sends POST to /messages/abc-123 with a tool call Server accepts the request (no ownership validation!) Server executes the tool and sends response to victim's SSE stream Victim receives attacker's response, thinking it's legitimate
PoC
attacker_client.py legitimate_client.py
Prerequisites
- Python 3.8+
- Install dependencies:
requests
Running the Demo
- Terminal 1: Start the Ruby MCP Server
ruby streamable_http_server.rb
Makes use of https://github.com/modelcontextprotocol/ruby-sdk/blob/main/examples/streamable_http_server.rb This server has a tool call notification_tool which the clients call
- Terminal 2: Start Victim Client
python3 legitimate_client.py
- Terminal 3 - Attacker Client:
Copy the session ID from Terminal 1 and run:
python3 attacker_client.py abc-123-def-456
- Back to Terminal 2 - Victim sees the injected response:
Impact
- Integrity: HIGH - Attacker can execute unauthorized tools and modify state
- Availability: LOW - Attacker can disrupt victim's session with injected responses
Additional Details
Session Hijacking Protection in MCP Implementations
The MCP specification recommends - "MCP servers SHOULD bind session IDs to user-specific information".
User Binding - Comparison other SDKs
csharp-sdk
- https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.AspNetCore/SseHandler.cs#L36
- https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol.AspNetCore/SseHandler.cs#L95-L98
Go-sdk
- https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/streamable.go#L66
- https://github.com/modelcontextprotocol/go-sdk/blob/main/mcp/streamable.go#L316-L320
Comparison with the Stream Replacement vulnerability
Stream Replacement https://github.com/modelcontextprotocol/ruby-sdk/security/advisories/GHSA-qvqr-5cv7-wh35
- Target: SSE stream connection
- Attack Vector: GET /mcp
- What happens to Victim: The connection is disconnected and doesn't receive reponses
Session Poisoning
- Target: Tool execution
- Attack Vector: POST /mcp
- What happens to Victim: The connection stays connected and receives responses of tool calls by attacker
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 💎RubyGems | mcp | all versions | 0.23.0bundle update mcp --conservative |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for mcp, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update mcp to 0.23.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-5p9g-j988-pcwv is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Frequently Asked Questions
Is GHSA-5p9g-j988-pcwv in your dependencies?
Find it across RubyGems, including transitive dependencies.