GHSA-5P9G-J988-PCWV

Vulnerability from github – Published: 2026-07-30 14:44 – Updated: 2026-07-30 14:44
VLAI
Summary
MCP Ruby SDK: Ruby SSE Session Poisoning
Details

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

https://github.com/modelcontextprotocol/ruby-sdk/blob/main/lib/mcp/server/transports/streamable_http_transport.rb#L260-L278

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

  1. Python 3.8+
  2. Install dependencies: requests

Running the Demo

  1. 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

  1. Terminal 2: Start Victim Client

python3 legitimate_client.py

  1. Terminal 3 - Attacker Client:

Copy the session ID from Terminal 1 and run:

python3 attacker_client.py abc-123-def-456
  1. 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
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.22.0"
      },
      "package": {
        "ecosystem": "RubyGems",
        "name": "mcp"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.23.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-67431"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-284"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-30T14:44:28Z",
    "nvd_published_at": "2026-07-29T20:17:12Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n**Vulnerability**: Missing Session Ownership Validation in the Ruby MCP SDK\u0027s Streamable and SSE HTTP transport implementation. Any attacker with a stolen session ID can execute tools with the victim\u0027s session. This is a silent attack - the victim\u0027s 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\u0027s SSE stream\n**Victim** receives attacker\u0027s response, thinking it\u0027s 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\u0027s 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\u0027t 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",
  "id": "GHSA-5p9g-j988-pcwv",
  "modified": "2026-07-30T14:44:28Z",
  "published": "2026-07-30T14:44:28Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/ruby-sdk/security/advisories/GHSA-5p9g-j988-pcwv"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-67431"
    },
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/ruby-sdk/commit/35466605319a34e4c7808712ae9bb1ca1afb2356"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/modelcontextprotocol/ruby-sdk"
    },
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/ruby-sdk/releases/tag/v0.23.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "MCP Ruby SDK: Ruby SSE Session Poisoning"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…