CVE-2026-44430
Description
The MCP Registry provides MCP clients with a list of MCP servers, like an app store for MCP servers. Prior to 1.7.7, the Registry's HTTP-based namespace verification (POST /v0/auth/http, POST /v0.1/auth/http) uses safeDialContext (internal/api/handlers/v0/auth/http.go:67-110) to refuse dialling private/internal addresses when fetching the well-known public-key file from a publisher-supplied domain. The blocklist (isBlockedIP, lines 125-133) relies entirely on Go stdlib's IsLoopback / IsPrivate / IsLinkLocalUnicast / IsMulticast / IsUnspecified plus a manual CGNAT range. None of these cover IPv6 6to4 (2002::/16), NAT64 (64:ff9b::/96 and 64:ff9b:1::/48 per RFC 8215), or deprecated site-local (fec0::/10) — all of which encode arbitrary IPv4 in the address bits and tunnel to RFC1918 / cloud-metadata services on dual-stack / NAT64-enabled hosts. This vulnerability is fixed in 1.7.7.
Affected products
1- Range: < 1.7.7
Patches
1f5f40bd98084fix(auth/http): extend SSRF blocklist to IPv6 6to4/NAT64/site-local prefixes (#1250)
2 files changed · +73 −7
internal/api/handlers/v0/auth/http.go+45 −7 modified@@ -109,27 +109,65 @@ func safeDialContext(ctx context.Context, network, addr string) (net.Conn, error return nil, fmt.Errorf("dial %s: all resolved public addresses failed: %w", host, lastErr) } +// mustCIDR parses a CIDR literal at init; panics on malformed input so a +// typo surfaces at startup rather than letting a nil *IPNet through to the +// blocklist check (which would silently fail-open). +func mustCIDR(s string) *net.IPNet { + _, n, err := net.ParseCIDR(s) + if err != nil { + panic(fmt.Sprintf("auth: invalid CIDR %q: %v", s, err)) + } + return n +} + // cgnatRange covers RFC 6598 Carrier-Grade NAT (100.64.0.0/10), which the // stdlib does not classify via any Is* helper but is reachable on some // cloud / mobile networks where it shadows internal infrastructure. -var cgnatRange = func() *net.IPNet { - _, n, _ := net.ParseCIDR("100.64.0.0/10") - return n -}() +var cgnatRange = mustCIDR("100.64.0.0/10") + +// blockedIPv6Prefixes are IPv6 ranges that either embed an arbitrary IPv4 +// address (and therefore tunnel into RFC1918 / cloud-metadata space on +// hosts with the corresponding routing) or are routed into site-local +// internal networks. None of these are caught by Go's per-class Is* +// helpers. +// +// 2002::/16 RFC 3056 6to4 — bits 16-47 are an IPv4 address +// 64:ff9b::/96 RFC 6052 NAT64 well-known — low 32 bits are IPv4 +// 64:ff9b:1::/48 RFC 8215 NAT64 local-use — same IPv4-embedding shape +// fec0::/10 RFC 3879 site-local (deprecated, still routed by some +// stacks) +var blockedIPv6Prefixes = []*net.IPNet{ + mustCIDR("2002::/16"), + mustCIDR("64:ff9b::/96"), + mustCIDR("64:ff9b:1::/48"), + mustCIDR("fec0::/10"), +} // isBlockedIP reports whether an IP must not be dialled by the namespace // verification fetcher. Covers loopback (127/8, ::1), RFC1918 + ULA via // IsPrivate, link-local (169.254/16, fe80::/10 — includes cloud metadata // 169.254.169.254), unspecified (0.0.0.0, ::), all multicast (admin-scoped -// 239/8 and ff00::/8 in addition to link-local-multicast), and CGNAT. +// 239/8 and ff00::/8 in addition to link-local-multicast), CGNAT, and +// IPv6 prefix families that tunnel to or embed arbitrary IPv4 addresses +// (see blockedIPv6Prefixes). IPv4-mapped IPv6 (::ffff:0:0/96) is handled +// implicitly: the stdlib Is* helpers honour the To4() fast-path, so e.g. +// ::ffff:10.0.0.1 is correctly classified as IsPrivate. func isBlockedIP(ip net.IP) bool { if ip == nil { return true } - return ip.IsLoopback() || ip.IsPrivate() || + if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsMulticast() || ip.IsUnspecified() || - cgnatRange.Contains(ip) + cgnatRange.Contains(ip) { + return true + } + for _, p := range blockedIPv6Prefixes { + if p.Contains(ip) { + return true + } + } + return false } // NewDefaultHTTPKeyFetcherWithClient creates a new HTTP key fetcher with a custom HTTP client.
internal/api/handlers/v0/auth/http_internal_test.go+28 −0 modified@@ -30,10 +30,38 @@ func TestIsBlockedIP(t *testing.T) { // Blocked — Carrier-Grade NAT (RFC 6598) {"100.64.0.1", true}, {"100.127.255.254", true}, + // Blocked — IPv6 6to4 (RFC 3056 2002::/16); bits 16-47 are an + // arbitrary IPv4 address, so 2002:a9fe:a9fe:: tunnels to + // 169.254.169.254 and 2002:0a00:0001:: tunnels to 10.0.0.1. + {"2002:a9fe:a9fe::", true}, + {"2002:0a00:0001::", true}, + {"2002::1", true}, + // Blocked — IPv6 NAT64 well-known prefix (RFC 6052 64:ff9b::/96); + // low 32 bits embed an IPv4 address. + {"64:ff9b::a9fe:a9fe", true}, + {"64:ff9b::a00:1", true}, + // Blocked — IPv6 NAT64 local-use prefix (RFC 8215 64:ff9b:1::/48). + {"64:ff9b:1::1", true}, + {"64:ff9b:1:abcd::", true}, + // Blocked — IPv6 deprecated site-local (RFC 3879 fec0::/10); + // still routed into internal networks by some stacks. + {"fec0::1", true}, + {"feff::1", true}, + // Blocked — IPv4-mapped IPv6 (::ffff:0:0/96) inherits the + // classification of the wrapped IPv4 via To4() fast-path; covered + // here as an explicit regression guard. + {"::ffff:127.0.0.1", true}, + {"::ffff:10.0.0.1", true}, + {"::ffff:169.254.169.254", true}, // Allowed — public {"1.1.1.1", false}, {"8.8.8.8", false}, {"2606:4700:4700::1111", false}, + {"2001:4860:4860::8888", false}, + // Allowed — just outside the new IPv6 blocks + {"2001::1", false}, // outside 2002::/16 + {"2003::1", false}, // outside 2002::/16 on the other side + {"64:ff9c::1", false}, // outside 64:ff9b::/96 // Allowed — outside CGNAT range {"100.63.255.255", false}, {"100.128.0.1", false},
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
6- github.com/modelcontextprotocol/registry/security/advisories/GHSA-r48c-v28r-pf6vnvdExploitVendor Advisory
- github.com/advisories/GHSA-r48c-v28r-pf6vghsaADVISORY
- github.com/modelcontextprotocol/registry/commit/f5f40bd98084466eaf18fe48ea62a0d534caa774ghsa
- github.com/modelcontextprotocol/registry/pull/1250ghsa
- github.com/modelcontextprotocol/registry/releases/tag/v1.7.7ghsa
- nvd.nist.gov/vuln/detail/CVE-2026-44430ghsa
News mentions
0No linked articles in our index yet.