
Cloud Vulnerability DB
A community-led vulnerabilities database
CVE-2026-54494 is a full-read Server-Side Request Forgery (SSRF) vulnerability in Koel, an open-source personal music streaming server. The flaw exists in App\Helpers\Network::isPublicHost(), which uses PHP's filter_var() with FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE to classify IP addresses — a check that fails to recognize IPv6 transition address forms (NAT64 64:ff9b::/96 per RFC 6052 and 6to4 2002::/16 per RFC 3056) that embed private or loopback IPv4 addresses. All versions of phanan/koel up to and including 9.7.0 are affected; version 9.7.1 contains the fix. The vulnerability was reported by researcher tonghuaroot, published as GHSA-rjg7-r26h-cfp2 on June 4, 2026, and added to the GitHub Advisory Database on July 15, 2026. It carries a CVSS v4.0 base score of 5.3 (Medium) (GitHub Advisory, Koel Advisory).
The root cause (CWE-918) is that PHP's filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) correctly rejects RFC 1918 addresses, loopback, link-local, and IPv4-mapped IPv6 (::ffff:a.b.c.d), but treats NAT64 (64:ff9b::/96) and 6to4 (2002::/16) transition wrappers as globally routable public addresses. This means addresses like 64:ff9b::7f00:1 (encoding 127.0.0.1), 64:ff9b::a9fe:a9fe (encoding the AWS/GCP IMDS endpoint 169.254.169.254), or 2002:a00:1:: (encoding 10.0.0.1) all pass the guard and return true. The vulnerable guard is the sole SSRF defense in front of App\Values\Podcast\EpisodePlayable::createForEpisode(), which fetches a podcast episode via Http::sink($file)->get($url) and streams the full response body back to the requesting user. Because the <enclosure url> in a podcast RSS feed is fully attacker-controlled, and any authenticated Koel user can subscribe to an arbitrary feed, an attacker can publish a feed with an enclosure whose hostname resolves via AAAA record to a NAT64/6to4 wrapper of an internal IP; on hosts with NAT64 or 6to4/dual-stack routing (standard on IPv6-only AWS/GCP subnets), the kernel routes the connection to the embedded private IPv4 (GitHub Advisory, Koel Advisory).
Successful exploitation enables a full-read SSRF attack, where the Koel server fetches an internal HTTP endpoint and returns the complete response body to the attacker. The most critical impact on cloud-hosted deployments is theft of cloud instance metadata service (IMDS) credentials — for example, querying http://[64:ff9b::a9fe:a9fe]/latest/meta-data/iam/security-credentials/ on AWS to obtain IAM role tokens, enabling lateral movement and privilege escalation within the cloud environment. Internal-only HTTP services reachable from the Koel host (admin panels, databases with HTTP interfaces, localhost daemons) are also exposed. The redirect callback in EpisodePlayable reuses the same flawed isSafeUrl() guard, so a redirect chain to a NAT64/6to4 host is equally bypassed (GitHub Advisory, Koel Advisory).
A working proof-of-concept is included in the public advisory, demonstrating that isPublicHost() returns true for NAT64 and 6to4 wrappers of private IPs and that Http::sink()->get() successfully retrieves internal content. Exploitation requires an authenticated Koel account (low privilege) and a Koel server running on a host with NAT64 or 6to4/dual-stack routing — the default configuration on IPv6-only AWS and GCP subnets. No evidence of in-the-wild exploitation or threat actor attribution has been reported. The CVE status is listed as Reserved, and no CISA KEV catalog entry or EPSS score is currently available (GitHub Advisory, Koel Advisory).
int.attacker.example) with an AAAA record pointing to a NAT64 or 6to4 wrapper of the target internal IP — for example, 64:ff9b::a9fe:a9fe to target the AWS/GCP IMDS endpoint (169.254.169.254), or 2002:a00:1:: to target 10.0.0.1.<enclosure url> points to the attacker-controlled domain (e.g., http://int.attacker.example/latest/meta-data/iam/security-credentials/).EpisodePlayable::createForEpisode() to call isSafeUrl() on the enclosure URL.isPublicHost() resolves the AAAA record to the NAT64/6to4 address, passes it through filter_var(FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE), which returns true (incorrectly classifying it as public).Http::sink($file)->get($url) connects to the internal endpoint via the kernel's NAT64/6to4 routing, reads the full response body (e.g., IAM credentials), and streams it back to the attacker as the episode content (GitHub Advisory, Koel Advisory).64:ff9b::/96 (NAT64) or 2002::/16 (6to4) ranges, particularly to 64:ff9b::a9fe:a9fe (IMDS) or RFC 1918-embedding 6to4 addresses; unexpected connections to 169.254.169.254 or internal subnet IPs originating from the Koel process.[64:ff9b::...]:port or [2002:...]:port endpoints; access logs on internal services showing requests with a User-Agent consistent with Guzzle/Laravel HTTP client.application/json, text/plain) from the Koel server.Upgrade to Koel version 9.7.1, which was released on June 4, 2026, and contains the fix in commit 5f6ce2c. The fix replaces the hand-rolled filter_var predicate with the mlocati/ip-lib library's RangeType enum, which correctly classifies NAT64 (64:ff9b::/96) as T_RESERVED and 6to4 (2002::/16) by the embedded IPv4 type, and adds an extractEmbeddedIpv4() helper to unwrap transition addresses before the private-range check. Additionally, the fix introduces IP pinning via CURLOPT_RESOLVE (DNS pinning) to prevent DNS rebinding attacks on redirect hops. No configuration-based workaround is available for unpatched versions; the only remediation is upgrading to v9.7.1 or later (Koel Release, Fix Commit).
The vulnerability was reported by researcher tonghuaroot and acknowledged by Koel maintainer phanan, who merged the fix PR (#2549) on June 4, 2026. The advisory notes that this SSRF class was not covered by the preceding v9.7.0 security scope, which the maintainer acknowledged as an oversight. No significant broader media coverage or notable community commentary beyond the GitHub advisory and PR discussion has been identified (Koel PR, Koel Advisory).
Source: This report was generated using AI
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."