CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-6p96-cfg5-4vhp: Koel: Authenticated Full-Read SSRF via Subsonic Internet Radio Stations

highCVSS 7.7CVE-2026-54493
Summary Koel v9.6.0 validates radio station URLs on the regular web API, but the Subsonic-compatible radio endpoints do not apply the same SSRF protections. An authenticated user can create or update a radio station with a private URL and then use Koel's radio streaming feature to make the server fetch that URL and return the upstream response body. This was validated against v9.6.0 (352ea5ec27fa22294da8fb6beacb3d5552f0d09c) using the official phanan/koel:9.6.0 image. Details SafeUrl is applied on the web API, but not on the Subsonic endpoints Koel's regular radio API protects station URLs with SafeUrl and HasAudioContentType: - app/Http/Requests/API/Radio/RadioStationStoreRequest.php - app/Http/Requests/API/Radio/RadioStationUpdateRequest.php new SafeUrl(), new HasAudioContentType(), The Subsonic-compatible routes do not reuse those checks: - routes/subsonic.php - createInternetRadioStation.view - updateInternetRadioStation.view - app/Http/Requests/Subsonic/CreateInternetRadioStationRequest.php - app/Http/Requests/Subsonic/UpdateInternetRadioStationRequest.php return [ 'streamUrl' => ['required', 'string'], 'name' => ['required', 'string'], 'homepageUrl' => ['nullable', 'string'], ]; The result is a validation gap between two routes that create the same type of object. The unvalidated URL is stored and later fetched server-side The Subsonic controllers hand the supplied URL to the regular radio service without any SSRF validation: - app/Http/Controllers/Subsonic/CreateInternetRadioStationController.php - app/Http/Controllers/Subsonic/UpdateInternetRadioStationController.php - app/Services/RadioService.php The SSRF is triggered when the station is played: - app/Http/Controllers/StreamRadioController.php - app/Services/Radio/RadioStreamService.php - app/Services/Radio/RadioStreamProxy.php RadioStreamProxy::openStream() opens a web address supplied by the attacker (attacker-controlled URL) without proper checks: $stream = fopen($url, 'r', false, $conte

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
high — CVSS 7.7
Published
2026-07-15
Last updated
2026-07-15
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-6p96-cfg5-4vhp

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-54493coverage & exploitation statusNVD · CVE.org

More from GitHub Security Advisories