Skip to content

SSRF Guard Bypass via IPv6 Transition Addresses (NAT64/6to4) in TREK

Moderate
jubnl published GHSA-9hpq-mrpx-mcxg Jul 18, 2026

Package

docker mauriceboe/TREK (docker)

Affected versions

< 3.3.0

Patched versions

3.4.0

Description

SSRF Guard Bypass via IPv6 Transition Addresses (NAT64/6to4) in TREK

Summary

The SSRF protection in TREK's ssrfGuard.ts does not block IPv6 transition addresses (NAT64 64:ff9b::/96, 6to4 2002::/16, Teredo 2001:0000::/32). An attacker who controls a DNS record can point it to a NAT64 or 6to4 address that embeds a private IPv4 target (e.g., 64:ff9b::169.254.169.254 for the AWS metadata service), bypassing the SSRF guard while the underlying network stack routes the connection to the embedded private IPv4 address.

Affected Component

  • File: server/src/utils/ssrfGuard.ts
  • Functions: isAlwaysBlocked(), isPrivateNetwork(), checkSsrf()
  • Commit: f9c992e

Root Cause

The isAlwaysBlocked function checks for loopback (127.*, ::1), unspecified (0.*), link-local (169.254.*, fe80:), and IPv4-mapped variants (::ffff:127.*, ::ffff:169.254.*). The isPrivateNetwork function checks RFC 1918 ranges, CGNAT, IPv6 ULA (fc00::/7), and IPv4-mapped RFC 1918 variants.

Neither function recognizes IPv6 transition addresses that embed an IPv4 address:

  • NAT64 (64:ff9b::/96): 64:ff9b::7f00:1 embeds 127.0.0.1
  • 6to4 (2002::/16): 2002:7f00:0001:: embeds 127.0.0.1
  • Teredo (2001:0000::/32): last 32 bits encode a client IPv4 (XOR'd with 0xFFFFFFFF)

When a hostname resolves to such an address, checkSsrf() classifies it as "allowed" because none of the regex patterns or prefix checks match these ranges.

Affected Code Paths

Multiple user-facing features use the vulnerable SSRF guard:

  1. Google Maps list import (server/src/services/placeService.ts:723): importGoogleList(tripId, url) accepts a user-provided URL, calls checkSsrf(url), then fetches it with safeFetchFollow().

  2. Naver Maps list import (server/src/services/placeService.ts:846): importNaverList(tripId, url) same pattern.

  3. Webhook notifications (server/src/services/notifications.ts:320,457): webhook delivery URLs validated by checkSsrf().

  4. Immich/Synology/AirTrail integrations: Server URLs validated by checkSsrf() or safeFetch().

  5. Plugin egress policy (server/src/nest/plugins/runtime/egress-policy.ts): isBlockedIp() checks IPv4-mapped and IPv4-compatible forms but also misses NAT64/6to4/Teredo.

Proof of Concept

  1. Set up a DNS record for attacker.example.com with an AAAA record pointing to 64:ff9b::a9fe:a9fe (NAT64 encoding of 169.254.169.254).

  2. As a TREK user, import a Google Maps list using the URL http://attacker.example.com/.

  3. checkSsrf() resolves attacker.example.com to 64:ff9b::a9fe:a9fe. Neither isAlwaysBlocked() nor isPrivateNetwork() recognizes this as a blocked address. The request proceeds.

  4. On a deployment with NAT64 gateway support (common in IPv6-only cloud environments), the network stack translates the connection to 169.254.169.254, reaching the cloud metadata service.

Impact

  • Cloud credential theft: In cloud environments with NAT64, the SSRF guard bypass allows reading cloud metadata endpoints, potentially exposing IAM credentials, instance identity tokens, and other sensitive data.
  • Internal network scanning: Mapping internal services by observing connection success/failure.
  • Data exfiltration: Reading responses from internal services (the response is processed by safeFetchFollow and its callers).

Suggested Fix

Add checks for IPv6 transition address ranges in both isAlwaysBlocked and isPrivateNetwork. For each recognized transition prefix, extract the embedded IPv4 address and validate it against the existing IPv4 blocklists:

// NAT64 well-known prefix (64:ff9b::/96) - last 4 bytes are the IPv4 address
if (/^64:ff9b:/i.test(addr)) {
  const embedded = extractEmbeddedIpv4FromNat64(addr);
  if (embedded && (isAlwaysBlocked(embedded) || isPrivateNetwork(embedded))) return true;
}

// 6to4 (2002::/16) - bytes 2-5 are the IPv4 address
if (/^2002:/i.test(addr)) {
  const embedded = extractEmbeddedIpv4From6to4(addr);
  if (embedded && (isAlwaysBlocked(embedded) || isPrivateNetwork(embedded))) return true;
}

// Teredo (2001:0000::/32) - last 4 bytes XOR 0xFFFFFFFF
if (/^2001:0000:/i.test(addr) || /^2001:0:/i.test(addr)) {
  const embedded = extractEmbeddedIpv4FromTeredo(addr);
  if (embedded && (isAlwaysBlocked(embedded) || isPrivateNetwork(embedded))) return true;
}

Severity

Moderate (CWE-918: Server-Side Request Forgery)

Credit

tonghuaroot (tonghuaroot@gmail.com)

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
High
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N

CVE ID

No known CVE

Weaknesses

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. Learn more on MITRE.

Credits