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…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…