CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-27896
7.00.05%

Case-Insensitive Chaos: Bypassing Security Controls in MCP Go SDK

Alon Barad
Alon Barad
Software Engineer

Feb 27, 2026·5 min read·10 visits

PoC Available

Executive Summary (TL;DR)

The MCP Go SDK used Go's standard `encoding/json`, which happily accepts `Method` instead of `method`. Security tools (WAFs) often expect strict JSON-RPC compliance and only block `method`. This mismatch allows attackers to bypass filters by simply capitalizing JSON keys.

A high-severity interpretation conflict in the Model Context Protocol (MCP) Go SDK allows attackers to bypass security intermediaries. By exploiting Go's standard library JSON parsing behavior, which is case-insensitive by default, attackers can smuggle malicious payloads past WAFs that strictly adhere to the case-sensitive JSON-RPC 2.0 specification.

The Hook: When "Helpful" Libraries Betray You

In the world of secure coding, "robustness" is often a double-edged sword. We are taught Postel's Law: "be conservative in what you do, be liberal in what you accept from others." But in security, being liberal is just another way of saying "please exploit me."

The Model Context Protocol (MCP) is the new connective tissue for LLMs, allowing AI models to talk to local tools and servers. The Go SDK (modelcontextprotocol/go-sdk) is the standard implementation for Go developers building these tools. You'd expect it to follow the JSON-RPC 2.0 spec to the letter. The spec is clear: field names are case-sensitive.

However, the Go SDK developers made a fatal, yet common, mistake: they trusted the standard library. Go's encoding/json is notoriously friendly. It tries very hard to map JSON keys to struct fields, ignoring case differences if an exact match isn't found. While this is great for developer velocity, it creates a massive "Interpretation Conflict" (CWE-436) when placed behind a security intermediary that actually follows the rules.

The Flaw: A Tale of Two Parsers

The vulnerability lies in how different systems interpret the same data stream. This is a classic semantic gap. On one side, you have a WAF or a sidecar proxy. These security devices are typically configured to be fast and pedantic. If the rule says block method: "exec", they look for those exact bytes. They assume that Method: "exec" is invalid JSON-RPC and ignore it, or let it pass because it doesn't match the blocklist.

On the other side, you have the vulnerable MCP Go SDK. It defines its request structures with standard Go tags:

type Request struct {
    Method string `json:"method"`
    // ... other fields
}

When json.Unmarshal sees a JSON payload like {"Method": "..."}, it looks for a struct field named Method. It finds it. It checks the tag json:"method". It notices the case doesn't match, but because Go is "helpful," it says, "Eh, close enough," and populates the field anyway.

This behavior violates the JSON-RPC 2.0 specification, which explicitly states that member names are case-sensitive. The SDK was accepting malformed protocol messages that were semantically valid to the Go runtime but syntactically invalid to the protocol spec.

The Code: The Smoking Gun

The fix required a fundamental shift in how the SDK handles JSON. You can't just tell the standard encoding/json to stop being case-insensitive; it's hardcoded behavior. The maintainers had to rip out the standard library and replace it with segmentio/encoding/json, a third-party library that offers more granular control.

Here is the critical change in internal/json/json.go. Notice the explicit disable switch:

// BEFORE: Using standard library (implicit in other files)
// err := json.Unmarshal(data, &v)
 
// AFTER: Using segmentio with case-sensitivity enforced
func Unmarshal(data []byte, v any) error {
    dec := json.NewDecoder(bytes.NewReader(data))
    // This line kills the vulnerability
    dec.DontMatchCaseInsensitiveStructFields()
    return dec.Decode(v)
}

The patch (Commit 7b8d81c264074404abdf5aa16e2cf0c2d9c64cc0) introduces this wrapper and replaces all calls to json.Unmarshal throughout the codebase (mcp/client.go, mcp/server.go, etc.). It forces the parser to be pedantic: if the JSON key is Method but the struct tag expects method, the field is ignored (left zero-valued), or the parsing fails depending on validation logic.

The Exploit: Bypassing the Guard Dog

Let's construct a real-world attack scenario. Imagine an MCP server exposing a sensitive tool, say filesystem/write. A security proxy sits in front of it, configured to allow filesystem/read but block filesystem/write.

The WAF rule might look something like this (pseudocode):

rule:
  match:
    json_key: "method"
    value: "filesystem/write"
  action: BLOCK

The Attack Chain:

  1. Normal Request (Blocked): The attacker sends valid JSON-RPC: {"jsonrpc": "2.0", "method": "filesystem/write", "params": {...}} The WAF sees method matches the blocklist. Request Dropped.

  2. Bypass Request (Allowed): The attacker capitalizes the key: {"jsonrpc": "2.0", "Method": "filesystem/write", "params": {...}}

    The WAF parses the JSON. It looks for the key method. It does not find it (it sees Method). It assumes this is a non-standard field or simply not the field it's watching. It forwards the request.

  3. Execution: The Go SDK receives the payload. json.Unmarshal matches Method (JSON) to Method (Struct). The handler for filesystem/write is invoked. The file is overwritten.

This is a textbook HTTP Request Smuggling attack, but adapted for the JSON-RPC application layer.

The Fix: Strictness is Security

The mitigation is straightforward but requires a dependency change. If you are using modelcontextprotocol/go-sdk, you must upgrade to version 1.3.1 immediately. This version enforces strict case matching, aligning the Go implementation with the JSON-RPC 2.0 spec and other language implementations (like TypeScript).

If you cannot upgrade immediately, your defense-in-depth strategy needs a patch. You must reconfigure your WAFs and proxies to inspect all case variations of the method key. However, this is a losing battle. Can you guarantee your WAF will catch mEthod, METHOD, and Method? What about id vs ID?

This vulnerability serves as a reminder: when implementing strict protocols, do not rely on standard libraries that prioritize developer experience over specification compliance. Explicitly validate input, or use parsers that allow you to turn off "fuzzy" matching features.

Official Patches

Model Context ProtocolFix commit replacing standard JSON decoder

Fix Analysis (1)

Technical Appendix

CVSS Score
7.0/ 10
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N
EPSS Probability
0.05%
Top 85% most exploited

Affected Systems

Model Context Protocol (MCP) Go SDK < 1.3.1Go applications implementing MCP servers using the vulnerable SDKGo applications implementing MCP clients using the vulnerable SDK

Affected Versions Detail

Product
Affected Versions
Fixed Version
go-sdk
modelcontextprotocol
< 1.3.11.3.1
AttributeDetail
CWE IDCWE-436 (Interpretation Conflict)
Secondary CWECWE-178 (Improper Handling of Case Sensitivity)
CVSS v4.07.0 (High)
Attack VectorNetwork (AV:N)
EPSS Score0.00048 (Low Probability)
ImpactSecurity Bypass (Subsequent System Integrity)

MITRE ATT&CK Mapping

T1203Exploitation for Client Execution
Execution
T1027Obfuscated Files or Information
Defense Evasion
CWE-436
Interpretation Conflict

Vulnerability Timeline

Fix commit pushed to repository
2026-02-18
CVE-2026-27896 and GHSA-wvj2-96wp-fq3f published
2026-02-26

References & Sources

  • [1]GHSA-wvj2-96wp-fq3f Advisory
  • [2]Go Standard Library JSON Documentation

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.