
Cloud Vulnerability DB
A community-led vulnerabilities database
CVE-2026-54491 is a Server-Side Request Forgery (SSRF) vulnerability in the Koel self-hosted music streaming application, classified as an incomplete fix for the prior CVE-2026-47260. The vulnerability affects Koel versions up to and including 9.7.0 (composer package phanan/koel), with the patched version being 9.7.1. It was published on June 4, 2026, and added to the GitHub Advisory Database on July 15, 2026. The CVSS v3.1 base score is 7.1 (High) (GitHub Advisory).
The root cause (CWE-918: Server-Side Request Forgery) stems from an incomplete remediation of CVE-2026-47260 in Koel v9.3.5, which added an initial isSafeUrl() check to several server-side fetchers but applied the per-redirect-hop Guzzle on_redirect callback only to the EpisodePlayable path. All other fetchers — synchronizeEpisodes (via Poddle::fromUrl), getStreamableUrl, AddRadioStation, and isPodcastObsolete — either had only the bypassable initial check or no check at all. Two distinct bypass vectors exist: (1) Redirect SSRF — an attacker supplies a URL on an attacker-controlled host that passes the initial isSafeUrl() check, then issues an HTTP 302 redirect to an internal address (e.g., 169.254.169.254 for cloud metadata or 127.0.0.1); (2) DNS rebinding (TOCTOU) — because isPublicHost() resolves DNS at validation time while the HTTP client resolves again at connect time with no IP pinning, an attacker can flip DNS between the two resolutions. Affected endpoints are reachable via the native REST API (POST /api/podcasts, POST /api/radio/stations) and the Subsonic API (createPodcastChannel, createInternetRadioStation, refreshPodcasts) by any authenticated, non-admin user (GitHub Advisory, PR #2546).
A successful exploit allows any authenticated (non-admin) user to cause the Koel server to issue HTTP requests to arbitrary internal or cloud-metadata endpoints from the server's network position. On cloud-hosted instances (AWS, GCP, Azure) using IMDSv1, an attacker can redirect requests to 169.254.169.254 and retrieve temporary IAM credentials, enabling a full cloud-account pivot. Beyond credential theft, the vulnerability enables probing of internal-only admin panels and internal network services, effectively turning the Koel server into an attacker-controlled pivot point into the trusted internal network — even when Koel itself is deployed on a private network (GitHub Advisory).
A mechanism proof-of-concept (PoC) was developed by the reporter (kiffa-australis256) and verified on PHP 8.2 + Guzzle 7, demonstrating that Poddle::fromUrl and getStreamableUrl paths leak internal secrets while the patched EpisodePlayable path correctly blocks the same attack. The full PoC kit (poc.php, attacker_router.php, internal_router.php) is available on request per the advisory. The only precondition is a single low-privilege authenticated account. No CISA KEV listing or in-the-wild exploitation evidence has been reported as of the advisory date. The CVE status is listed as "Reserved" and no EPSS score is currently published (GitHub Advisory).
attacker.example.com) that responds to HTTP GET requests with a 302 Location header pointing to the internal target (e.g., http://169.254.169.254/latest/meta-data/iam/security-credentials/ for AWS IMDSv1, or http://127.0.0.1:<port>/ for internal services).POST /api/podcasts (or Subsonic createPodcastChannel) with the feed URL set to http://attacker.example.com/feed.xml. The initial isSafeUrl() check passes because the attacker's domain resolves to a public IP.PodcastService::addPodcast or Poddle::fromUrl fetches the attacker URL. The attacker server responds with 302 Location: http://169.254.169.254/latest/meta-data/.... Because no per-hop redirect validation exists on this path, Guzzle follows the redirect to the internal target.Access-Control-Allow-Origin: *, the response body (e.g., IAM credentials) is reflected back through parsed podcast fields or getStreamableUrl. Otherwise, exploit is blind via status code or timing differences.isPublicHost()); at connect time, DNS is re-resolved and returns a private IP (e.g., 127.0.0.1), which the HTTP client connects to without re-validation (GitHub Advisory, PR #2546).169.254.169.254 (AWS/GCP/Azure IMDS), 100.100.100.200 (Alibaba Cloud IMDS), or RFC-1918 addresses (10.x.x.x, 172.16-31.x.x, 192.168.x.x) originating from the PHP/Guzzle process.POST /api/podcasts or Subsonic createPodcastChannel requests with external feed URLs from non-admin users, followed by outbound connections to internal IP ranges./api/podcasts, /api/radio/stations, or Subsonic endpoints (createPodcastChannel, createInternetRadioStation) with URLs pointing to attacker-controlled domains.GetCallerIdentity, AssumeRole) from the Koel server's instance role shortly after podcast/radio station creation events.Upgrade to Koel version 9.7.1 (phanan/koel via Composer), which centralizes all outbound HTTP through a SafeHttp service that applies per-hop redirect validation and IP pinning via CURLOPT_RESOLVE (defeating DNS rebinding) on every fetch path, including Poddle::fromUrl, getStreamableUrl, isPodcastObsolete, HasAudioContentType, and SafeUrl (GitHub Advisory, Commit 5f6ce2c). As a network-level workaround prior to patching, restrict the Koel server's outbound HTTP access using firewall rules to block connections to RFC-1918 addresses and cloud metadata IP ranges (169.254.169.254, 100.100.100.200). Additionally, enabling AWS IMDSv2 (token-required mode) on EC2 instances mitigates the cloud credential theft scenario, as IMDSv2 requires a PUT-based token that cannot be obtained via a simple GET-redirect SSRF (GitHub Advisory).
The vulnerability was reported by security researcher kiffa-australis256 and acknowledged by Koel maintainer phanan, who merged two pull requests (#2546 and #2549) within 24 hours of the report to address the redirect-SSRF and DNS rebinding legs respectively. The maintainer noted in PR #2549 that the incomplete coverage of the original fix was "on me," reflecting transparency about the remediation gap. CodeRabbit's automated review flagged additional test coverage weaknesses in the initial fix, which were addressed before merge. No broader media coverage or significant social media discussion has been identified (GitHub Advisory, PR #2549).
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."