FlawOpen

flawopen.com/Teardowns/cve-2026-14540-google-mcp-toolbox-ssrf

● CVE-2026-14540 · CVSS 8.0 · High
FlawOpen Security Research

CVE-2026-14540: Google MCP Toolbox SSRF

CVE-2026-14540 let MCP Toolbox HTTP requests follow redirects without checking their final network destination. Upgrade to 1.5.0 or later.[1], [3]

💡 Plain English Explainer (ELI5)

A mailroom checks the first address on a parcel, then follows any forwarding sticker without checking the new destination. An HTTP redirect is that sticker: if the server does not validate the final IP, a request aimed at an ordinary website can be forwarded into a private network. The fix checks the actual destination when connecting and checks redirected destinations too.[2], [4], [7]

Core Concepts & Subsystem Terms

Server-side request forgery (SSRF)
A service is induced to make an HTTP request to a destination chosen through attacker-influenced input. The request originates from the service, so it can reach networks that the caller cannot.[6]
HTTP redirect
A response can tell an HTTP client to make a follow-up request to another URL. A security check on only the first URL does not constrain where that follow-up goes.[4], [7]
DNS rebinding and TOCTOU
A hostname can resolve to one address during validation and a different address when the socket is opened. Checking the resolved destination at connection time closes this time-of-check/time-of-use gap.[2], [7]
Generic HTTP source and tool
MCP Toolbox components that build HTTP requests using configured endpoints and tool parameters. The advisory identifies these components as the affected path.[1], [2], [4]
CWE-918
Server-Side Request Forgery (SSRF): the application fetches a URL or destination without sufficient control over where the request can go.[6]

Framework mappings

Root Cause Analysis

In affected versions, the generic HTTP source created a Go http.Client with a timeout and transport but no destination-aware redirect policy or socket-time IP restriction. Go followed redirects by default. Because configured HTTP tools can incorporate parameter values into requests, a crafted value or redirect could move a request away from its intended destination. Checking only the original URL is insufficient: a redirect changes the destination, and DNS may return a different address between validation and connection. The upstream fix adds SSRFGuard, rejects blocked destinations in CheckRedirect, and checks the resolved IP again at the dial boundary. Private, loopback, link-local, and other non-global addresses are denied by default; explicit allow ranges and private-network access are configuration overrides and must be narrowly scoped.[1], [2], [4]

Step-by-Step Attack Flow

Step 1

Apply the three-gate reachability test

This chain needs all three: (1) an affected 0.3.0–1.4.0 deployment uses the generic HTTP source/tool; (2) a caller can invoke that path and control the URL/path or trigger a redirect; and (3) the Toolbox host can route to the destination. If any gate is absent, this chain cannot reach that destination through this path. The destination’s own authentication and access controls still apply.[1]

Step 2

A tool argument influences an outbound request

A configured HTTP tool combines its base endpoint with input values. The vendor CVE description says crafted path parameters can redirect the HTTP source toward another destination. An MCP user or agent is the caller of the tool; untrusted retrieved content can influence that caller only when the surrounding workflow allows it.[1], [2]

Step 3

The original URL check does not bound redirects

The affected client had no restrictive CheckRedirect hook and no target-IP validation. A server response can direct the HTTP client to a different URL, so validating only the configured base or first request does not constrain the follow-up request.[1], [2]

Step 4

The Toolbox makes the request from its own network position

The follow-up request originates from the Toolbox host and can reach destinations allowed by that host’s network placement. Internal services or other reachable endpoints may therefore be exposed to requests the MCP caller could not make directly. The CVE does not establish that every deployment exposes cloud credentials or that all redirects succeed.[1]

Step 5

Upgrade and verify egress boundaries

Upgrade to MCP Toolbox 1.5.0 or later. The fix validates configured literal IPs, checks redirect targets, and blocks disallowed resolved IPs at connection time to address DNS rebinding. Keep allowPrivateNetworks disabled unless required; configure explicit allowed ranges narrowly and enforce outbound network policy as a separate layer.[2], [3], [4]

Source Code: Flaw vs. Secure Implementation

This is a focused comparison of the upstream HTTP client path, not a complete replacement for the file. The merged fix adds an SSRFGuard: it checks redirect destinations and also rejects blocked resolved IPs at connection time, closing the DNS rebinding gap that redirect-time resolution alone would leave. The guard blocks private and other non-global addresses by default. allowPrivateNetworks, allowedIpRanges, and customBlockedIpRanges are powerful deployment controls; an allowlist exception should be limited to the exact required destination range.[2]

Source excerpt: internal/sources/http/http.go — HTTP client construction and redirect handling

UNPATCHED FLAW
client := http.Client{
	Timeout: duration,
	Transport: tr,
}

// With no CheckRedirect policy, the client follows HTTP redirects using
// the default policy. The transport also has no SSRF destination guard.
HARDENED SECURE PATCH
client := &http.Client{
	Timeout: duration,
	Transport: tr,
	CheckRedirect: func(req *http.Request, via []*http.Request) error {
		if len(via) >= 10 {
			return fmt.Errorf("stopped after 10 redirects")
		}
		hostname := req.URL.Hostname()
		if ip := net.ParseIP(hostname); ip != nil {
			if guard.IsIPBlocked(ip) {
				return fmt.Errorf("redirect to blocked IP %s denied", ip)
			}
			return nil
		}
		addrs, err := resolver.LookupHost(req.Context(), hostname)
		if err != nil {
			return fmt.Errorf("failed to resolve redirect host %s: %w", hostname, err)
		}
		for _, addr := range addrs {
			if ip := net.ParseIP(addr); ip != nil && guard.IsIPBlocked(ip) {
				return fmt.Errorf("redirect host %s resolves to blocked IP %s", hostname, addr)
			}
		}
		return nil
	},
}
// The same SSRFGuard is also applied at the socket dial boundary,
// so a DNS answer cannot change from a permitted address to a blocked one.

Engineering & System Hardening Checklist

Sources