CVE-2026-54491
PHP vulnerability analysis and mitigation

Overview

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).

Technical details

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).

Impact

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).

Exploitability

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).

Exploitation steps

  1. Obtain a low-privilege account: Register or obtain any authenticated (non-admin) user account on the target Koel instance.
  2. Set up an attacker-controlled redirect server: Host a web server (e.g., 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).
  3. Submit a malicious podcast feed URL: Authenticate to the Koel API and send 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.
  4. Trigger server-side fetch: Koel's 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.
  5. Exfiltrate data: If the internal endpoint returns 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.
  6. DNS rebinding alternative: For the DNS rebinding vector, configure the attacker domain's DNS with a very short TTL. At validation time, the domain resolves to a public IP (passing 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).

Indicators of compromise

  • Network: Outbound HTTP requests from the Koel server to 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.
  • Network: Unusual DNS queries from the Koel server for attacker-controlled domains with very short TTLs (potential DNS rebinding indicator).
  • Logs: Koel application logs showing POST /api/podcasts or Subsonic createPodcastChannel requests with external feed URLs from non-admin users, followed by outbound connections to internal IP ranges.
  • Logs: Web server access logs showing requests to /api/podcasts, /api/radio/stations, or Subsonic endpoints (createPodcastChannel, createInternetRadioStation) with URLs pointing to attacker-controlled domains.
  • Logs: Cloud provider access logs (e.g., AWS CloudTrail) showing IMDS credential requests (GetCallerIdentity, AssumeRole) from the Koel server's instance role shortly after podcast/radio station creation events.

Mitigation and workarounds

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).

Community reactions

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).

Additional resources


SourceThis report was generated using AI

Related PHP vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-54493HIGH7.7
  • PHP logoPHP
  • phanan/koel
NoYesAug 19, 2026
CVE-2026-54491HIGH7.1
  • PHP logoPHP
  • phanan/koel
NoYesAug 19, 2026
CVE-2026-61807MEDIUM6.3
  • PHP logoPHP
  • snipe/snipe-it
NoYesAug 19, 2026
CVE-2026-54494MEDIUM5.3
  • PHP logoPHP
  • phanan/koel
NoYesAug 19, 2026
CVE-2026-54492MEDIUM4.3
  • PHP logoPHP
  • phanan/koel
NoYesAug 19, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management