Skip to content

fix(auth/http): extend SSRF blocklist to IPv6 6to4/NAT64/site-local prefixes#1250

Merged
rdimitrov merged 1 commit intomainfrom
fix/ssrf-block-ipv6-tunneled-prefixes
May 4, 2026
Merged

fix(auth/http): extend SSRF blocklist to IPv6 6to4/NAT64/site-local prefixes#1250
rdimitrov merged 1 commit intomainfrom
fix/ssrf-block-ipv6-tunneled-prefixes

Conversation

@rdimitrov
Copy link
Copy Markdown
Member

The HTTP namespace-verification fetcher's isBlockedIP composed its
blocklist from Go stdlib's Is* helpers plus a manual CGNAT range. That
covers IPv4 cloud metadata, RFC1918, ULA, and link-local — but Go's
classification helpers don't recognise IPv6 prefix families that embed
or tunnel to arbitrary IPv4:

  • 2002::/16 RFC 3056 6to4 — bits 16-47 are an arbitrary IPv4
  • 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)

On hosts with 6to4 routing or a NAT64 next-hop (the default on
IPv6-only GKE node pools, AWS IPv6-only EC2, Azure IPv6 VMs with
NAT64, and many corporate DNS64/NAT64 networks), an attacker who
controls DNS for a publisher domain can cause the registry to dial
169.254.169.254, 10.0.0.0/8, etc. via the IPv6 wrapper. Same
SSRF class as GHSA-56c3-vfp2-5qqj on n8n-mcp.

Define the four prefixes via a new mustCIDR helper (panic on
malformed CIDR at init so a typo can't fail-open silently) and iterate
them in isBlockedIP after the stdlib gates. Refactor the existing
CGNAT IIFE to use the same helper for consistency. IPv4-mapped IPv6
(::ffff:0:0/96) needs no special-casing: Go's Is* helpers honour the
To4() fast-path, so e.g. ::ffff:10.0.0.1 is already classified as
IsPrivate.

Adds 17 test cases to TestIsBlockedIP covering each new prefix
family, IPv4-mapped IPv6 sanity checks, and just-outside-the-range
positive controls (2001::1, 2003::1, 64:ff9c::1).

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

…refixes

The HTTP namespace-verification fetcher's `isBlockedIP` composed its
blocklist from Go stdlib's `Is*` helpers plus a manual CGNAT range. That
covers IPv4 cloud metadata, RFC1918, ULA, and link-local — but Go's
classification helpers don't recognise IPv6 prefix families that embed
or tunnel to arbitrary IPv4:

  - 2002::/16       RFC 3056 6to4 — bits 16-47 are an arbitrary IPv4
  - 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)

On hosts with 6to4 routing or a NAT64 next-hop (the default on
IPv6-only GKE node pools, AWS IPv6-only EC2, Azure IPv6 VMs with
NAT64, and many corporate DNS64/NAT64 networks), an attacker who
controls DNS for a publisher domain can cause the registry to dial
`169.254.169.254`, `10.0.0.0/8`, etc. via the IPv6 wrapper. Same
SSRF class as GHSA-56c3-vfp2-5qqj on n8n-mcp.

Define the four prefixes via a new `mustCIDR` helper (panic on
malformed CIDR at init so a typo can't fail-open silently) and iterate
them in `isBlockedIP` after the stdlib gates. Refactor the existing
CGNAT IIFE to use the same helper for consistency. IPv4-mapped IPv6
(::ffff:0:0/96) needs no special-casing: Go's `Is*` helpers honour the
`To4()` fast-path, so e.g. `::ffff:10.0.0.1` is already classified as
`IsPrivate`.

Adds 17 test cases to `TestIsBlockedIP` covering each new prefix
family, IPv4-mapped IPv6 sanity checks, and just-outside-the-range
positive controls (`2001::1`, `2003::1`, `64:ff9c::1`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rdimitrov rdimitrov merged commit f5f40bd into main May 4, 2026
5 checks passed
@rdimitrov rdimitrov deleted the fix/ssrf-block-ipv6-tunneled-prefixes branch May 4, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant