IPv6 and NAT64 SSRF Bypasses That Slip Past IPv4 Filters
A filter rejects 127.0.0.1 and 10.0.0.0/8, so the SSRF is fixed—until the HTTP client accepts IPv6. Address families meet later in the network stack, and string-based blocklists rarely understand all of the translations.
You are testing address normalization, not collecting a bag of magic strings. Use a listener with A and AAAA records so you can see which family the server chooses.
IPv6 forms that expose weak checks
Loopback is [::1]. IPv4-mapped IPv6 can represent an IPv4 destination as [::ffff:127.0.0.1] or hexadecimal equivalents. Zero compression and leading zeros create multiple textual forms for the same address. A filter comparing strings may miss them; a proper IP parser reduces them to one binary address and checks the correct range.
Link-local addresses (fe80::/10), unique local addresses (fc00::/7), multicast, unspecified ::, and IPv4-mapped private ranges should all be denied for a public URL fetcher. Zone identifiers such as %25eth0 appear in scoped link-local URLs, though parser and operating-system support differs. Do not brute interface names on production.
Try each candidate against infrastructure you control first. Some URL parsers require brackets, some normalize mapped addresses to IPv4, and some reject zone IDs before a socket opens. Those differences tell you which layer handles validation.
NAT64 changes the destination after validation
NAT64 lets IPv6-only clients reach IPv4 services through a translation prefix, commonly the well-known 64:ff9b::/96 or a network-specific prefix. An IPv4 address can be embedded in the low 32 bits. A validator sees a globally shaped IPv6 literal; the gateway translates it to an IPv4 private or loopback destination if routing permits.
Do not assume the standard prefix is active. Query ipv4only.arpa from a controlled execution context if the product exposes DNS diagnostics, or infer NAT64 from callbacks to your dual-stack host. Blindly trying many prefixes adds traffic without evidence.
DNS64 creates a similar surprise: the resolver synthesizes AAAA records for an A-only hostname. Defenses must inspect the final connected address and understand translated destinations, not approve a hostname because its initial text looked public.
A safe validation ladder
- Confirm SSRF to your dual-stack listener.
- Log whether A, AAAA, or both were queried and connected.
- Test alternate IPv6 spelling against another address you own.
- Demonstrate mapped/NAT64 normalization with a benign controlled service.
- Probe one non-sensitive internal endpoint only if policy allows.
Response timing alone can mislead you because failed IPv6 connections often fall back to IPv4. Capture DNS and listener logs whenever possible.
In the report, state the submitted literal, canonical address, observed translation, and final service. Recommend a mature URL/IP library, checks on every resolved A and AAAA record, explicit denial of non-global ranges, redirect revalidation, and egress firewall rules for both families. NAT64 prefixes used by the environment should be decoded before policy decisions.
OWASP's SSRF Prevention Cheat Sheet stresses validation after resolution and defense in depth. PortSwigger's SSRF guide provides the exploitation model. CWE-918 remains the right category. The strongest finding is not “IPv6 accepted”; it is “an IPv6 representation reached a destination the policy explicitly meant to block.”