Vulnerability GHSA-qff7-q5fm-8p76

Medium Risk
MEDIUM RISK
CVSS Score: 6.5
Score Range: 4.0–6.9
Medium severity vulnerabilities (CVSS 4.0–6.9). Important issues that meaningfully reduce security confidence.
4 months ago
May 04, 2026 at 09:19 PM UTC
AzuraCast has Missing Permissions Check on Media File Download, Allowing Cross-Station Data Exfiltration
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5

Summary

AzuraCast has Missing Permissions Check on Media File Download, Allowing Cross-Station Data Exfiltration

Details

Summary

The GET /api/station/{station_id}/file/{id}/play endpoint, handled by PlayAction, is missing the Middleware\Permissions check that protects all sibling routes in the same /file/{id} route group. Any authenticated user can download media files from any station, regardless of whether they have permissions on that station. In multi-tenant deployments, this enables cross-station media exfiltration.

Details

In backend/config/routes/api_station.php, the /file/{id} route group (lines 407-429) defines four endpoints:

// Line 407-429
$group->group(
    '/file/{id}',
    function (RouteCollectorProxy $group) {
        // GET /file/{id} — has Permissions check ✓
        $group->get('', ...)->add(new Middleware\Permissions(StationPermissions::Media, true));

        // PUT /file/{id} — has Permissions check ✓
        $group->put('', ...)->add(new Middleware\Permissions(StationPermissions::Media, true));

        // DELETE /file/{id} — has Permissions check ✓
        $group->delete('', ...)->add(new Middleware\Permissions(StationPermissions::DeleteMedia, true));

        // GET /file/{id}/play — NO Permissions check ✗
        $group->get('/play', Controller\Api\Stations\Files\PlayAction::class)
            ->setName('api:stations:files:play');
    }
);

The middleware chain for the /play endpoint is: GetStation → RequireStation → RequireLogin → StationSupportsFeature(Media) → PlayAction. The RequireLogin middleware (backend/src/Middleware/RequireLogin.php) only verifies a valid session/API key exists — it does not check station-level permissions.

The controller at backend/src/Controller/Api/Stations/Files/PlayAction.php:84 calls $this->mediaRepo->requireForStation($id, $station), which verifies the media belongs to the station but performs no authorization check. The findForStation method (StationMediaRepository.php:46-66) accepts both auto-increment integer IDs and unique IDs, making enumeration trivial via sequential integers.

This is notably similar to the regression fixed in commit 7fbc7dd (2026-02-26), which restored a missing group-level Permissions middleware on the adjacent /files group. The /play route was missed in that fix.

PoC

# Step 1: Create two stations (Station A and Station B) in a multi-tenant AzuraCast instance.
# Upload media files to Station B.

# Step 2: Create a user with permissions ONLY on Station A. Generate an API key for this user.
API_KEY="user-with-only-station-a-access"

# Step 3: Enumerate and download media from Station B (station_id=2) using sequential IDs
# This should return 403 Forbidden, but instead returns the file content
curl -H "X-API-Key: $API_KEY" https://target/api/station/2/file/1/play -o stolen1.mp3
# HTTP 200 OK — file downloaded successfully

curl -H "X-API-Key: $API_KEY" https://target/api/station/2/file/2/play -o stolen2.mp3
# HTTP 200 OK — file downloaded successfully

# Step 4: Verify the same user is correctly blocked on other endpoints in the same group
curl -H "X-API-Key: $API_KEY" https://target/api/station/2/file/1
# HTTP 403 Forbidden — permission check works here

Impact

  • Any authenticated user can download the full media library of any station in the instance, regardless of their assigned permissions.
  • In multi-tenant deployments (e.g., hosting providers running multiple radio stations), a user of Station A can exfiltrate all copyrighted audio content from Station B.
  • Media IDs use auto-increment integers (HasAutoIncrementId trait on StationMedia), enabling trivial enumeration of all media files.
  • The confidentiality impact is High: full media file contents (MP3, FLAC, etc.) are exposed.

Recommended Fix

Add the Permissions middleware to the /play route, matching the pattern used by the adjacent routes:

// backend/config/routes/api_station.php, line 426-427
// Before:
$group->get('/play', Controller\Api\Stations\Files\PlayAction::class)
    ->setName('api:stations:files:play');

// After:
$group->get('/play', Controller\Api\Stations\Files\PlayAction::class)
    ->setName('api:stations:files:play')
    ->add(new Middleware\Permissions(StationPermissions::Media, true));

Related Vulnerabilities

Other vulnerabilities affecting the same packages

High Risk
4 months ago
AzuraCast Vulnerable to Liquidsoap Code Injection via Incomplete cleanUpString-to-toRawString Migration in Remote Relay Password Field
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-q4ph-8x8g-95f8
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-q4ph-8x8g-95f8
Medium Risk
4 months ago
AzuraCast's Missing RequireInternalConnection on Liquidsoap API Allows Low-Privilege Metadata Injection and Broadcast Disruption
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-4fm3-ggg2-c6qx
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-4fm3-ggg2-c6qx
High Risk
4 months ago
AzuraCast has Password Reset Poisoning via Untrusted X-Forwarded-Host Header that Leads to Account Takeover and 2FA Bypass
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-gv7r-3mr9-h5x8
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-gv7r-3mr9-h5x8
High Risk
4 months ago
AzuraCast has Path Traversal in `currentDirectory` Parameter that Enables Remote Code Execution via Media Upload
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-vp2f-cqqp-478j
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.5 GHSA-vp2f-cqqp-478j
High Risk
6 months ago
AzuraCast: RCE via Liquidsoap string interpolation injection in station metadata and playlist URLs
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.3 GHSA-93fx-5qgc-wr38
0.3.1 - 0.9.4 and 0.9.4.1 and 0.9.4.2 - 0.9.5 and 0.9.5.1 - 0.9.6 and 0.9.6.1 and 0.9.6.2 and 0.9.6.5 - 0.9.7 and 0.9.7.1 - 0.9.8 and 0.9.8.1 - 0.23.3 GHSA-93fx-5qgc-wr38
View all vulnerabilities for these packages

Impacted packages

Timeline

Published
4 months ago
May 04, 2026 at 09:19 PM UTC
Fixed (0.23.6)
5 months ago
April 24, 2026 at 05:38 AM UTC
Last Modified
23 hours ago
September 27, 2026 at 11:56 AM UTC