🛡️ CVE-2026-44430 — mcp-registry
Description
MCP Registry has an unauthenticated SSRF: HTTP namespace verification dials 6to4 / NAT64 / site-local IPv6 addresses, bypassing private-address allowlist
Summary
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 is the same CWE-918 SSRF class fixed in GHSA-56c3-vfp2-5qqj on czlonkowski/n8n-mcp (CVSS 8.5 HIGH). The remediation pattern is identical: extend the blocklist with the IPv6 prefix families that embed IPv4.
The endpoint is unauthenticated — it is the login flow itself — so attack complexity is low aside from the host-level routing dependency.
Affected: latest main HEAD 23f4fda and current production v1.7.6 deployment at https://registry.modelcontextprotocol.io/v0/auth/http.
Details
Vulnerable code
internal/api/handlers/v0/auth/http.go:125-133:
```go
func isBlockedIP(ip net.IP) bool {
if ip == nil {
return true
}
return ip.IsLoopback() || ip.IsPrivate() ||
ip.IsLinkLocalUnicast() || ip.IsMulticast() ||
ip.IsUnspecified() ||
cgnatRange.Contains(ip)
}
```
Per Go source (src/net/ip.go), the relevant stdlib helpers cover:
| Helper | IPv6 coverage |
|---|---|
| IsLoopback | ::1, IPv4-mapped of 127/8 (via To4() fast-path) |
| IsPrivate | ULA fc00::/7 only — ip[0]&0xfe == 0xfc |
| IsLinkLocalUnicast | fe80::/10 only — ip[1]&0xc0 == 0x80 (NOT fec0::/10 which is 0xc0) |
| IsMulticast | ff00::/8 |
| IsUnspecified | :: |
The Registry's blocklist therefore does not cover:
| Prefix | Defined in | Why dangerous |
|---|---|---|
| 2002::/16 | RFC 3056 (6to4) | Bits 16-47 embed an arbitrary IPv4 address. 2002:a9fe:a9fe:: is the 6to4 encoding of 169.254.169.254 (AWS / Azure metadata). 2002:0a00:0001:: encodes 10.0.0.1. On hosts with 6to4 routing or any explicit 2002::/16 route, the dial reaches the embedded IPv4. |
| 64:ff9b::/96 | RFC 6052 (NAT64 well-known prefix) | Low 32 bits embed an IPv4 address. 64:ff9b::a9fe:a9fe translates to 169.254.169.254 on any NAT64-enabled network — which is the default in IPv6-only GKE node pools, AWS IPv6-only EC2, Azure IPv6 VMs with NAT64, and DNS64/NAT64 corporate networks. |
| 64:ff9b:1::/48 | RFC 8215 (local-use NAT64) | Same tunnelling concern, intended for operator-defined NAT64. |
| fec0::/10 | RFC 3879 (deprecated site-local) | Some BSD / older Linux stacks still honour these for routing into site-local internal networks. |
safeDialContext resolves DNS once and dials by IP (good — pins against rebinding TOCTOU), but the IP-allowlist gate is the security boundary, and that gate is incomplete.
Exposure surface
POST /v0/auth/http (and POST /v0.1/auth/http) is registered in internal/api/handlers/v0/auth/http.go:197-218 and routed unauthenticated in internal/api/router/v0.go:24,39:
```go
huma.Register(api, huma.Operation{
OperationID: "exchange-http-token...",
Method: http.MethodPost,
Path: pathPrefix + "/auth/http",
Summary: "Exchange HTTP signature for Registry JWT",
...
}, func(ctx context.Context, input *HTTPTokenExchangeInput) (...) {
response, err := handler.ExchangeToken(ctx, input.Body.Domain, ...)
...
})
```
The handler builds https://<attacker-domain>/.well-known/mcp-registry-auth (line 143) and dials via the safeDialContext-equipped client. The domain parameter is taken verbatim from the unauthenticated POST body.
Critical order-of-operations confirmation in CoreAuthHandler.ExchangeToken (internal/api/handlers/v0/auth/common.go:246-265):
1. ValidateDomainAndTimestamp(domain, timestamp) — domain format check (no IP literal, must contain dot)
2. DecodeAndValidateSignature(signedTimestamp) — hex decode
3. keyFetcher(ctx, domain) ← SSRF dial happens here
4. VerifySignatureWithKeys(...) ← only AFTER fetch
So the SSRF dial fires before any signature verification. Attacker needs only a valid RFC3339 timestamp (±15s window) and any hex string for signedTimestamp.
PoC
Tested against main HEAD 23f4fda (make dev-compose boots Registry on localhost:8080).
Step 1 — Set up attacker DNS
Configure attacker.example with the AAAA records:
```
attacker-6to4.example. AAAA 2002:a9fe:a9fe:: ; 6to4 -> 169.254.169.25
How this vulnerability can be exploited
This issue can be reached over the network, attack complexity is high, an attacker needs no privileges on the target. No user interaction is required. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality none, integrity low, availability none.
Weakness class
CVE-2026-44430 is classified as CWE-918: Server-Side Request Forgery (SSRF). The server fetches a URL supplied by the caller, which can be pointed at internal systems it alone can reach.
Affected software
CVE-2026-44430 is recorded against 3 packages.
- github.com/modelcontextprotocol/registry
- mcp-registry (fixed in 1.7.7)
- unknown
Timeline and source
Published on 8 May 2026 and last revised on 25 June 2026. No public exploit is currently recorded for this entry. A vendor advisory or fix has been published. Record sourced from NVD.
References
github.com (Web)
nvd.nist.gov (Advisory)
github.com (Web)
github.com (Web)
github.com (Package)
github.com (Web)
Details
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:L/A:N
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| github.com/modelcontextprotocol/registry | — | — |
| mcp-registry | — | 1.7.7 |
| unknown | — | — |
Similar Threats
- Low CVE-2026-45781
- Unknown CVE-2026-44427
- Medium CVE-2026-44428
- Medium CVE-2026-44429
More CVE 2026 advisories
Browse all of CVE 2026 in the advisory index.
Vulnerability Monitoring
Track new vulnerabilities in mcp-registry
CVE-2026-44430 is rated CVSS 4.0 Medium. BotEraser monitors your WordPress installation and notifies you when software you use appears in our vulnerability database.
Set Up Free Alerts →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.