GHSA-qff7-q5fm-8p76: Azuracast/azuracast AzuraCast Has Missing Permissions Check Vulnerability

MODERATE
CVSS 6.5
CWE-862
Published
Advisory Summary

GHSA-qff7-q5fm-8p76 is a MODERATE (CVSS 6.5) azuracast has missing permissions check vulnerability (CWE-862: Missing Authorization) affecting Azuracast/azuracast libraries in the Packagist ecosystem. The issue involves azuracast has missing permissions check on media file download, allowing cross-station data exfiltration. Remediation is available by upgrading affected packages to version 0.23.6.

GHSA-qff7-q5fm-8p76 at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-qff7-q5fm-8p76
Severity MODERATE
CVSS v3.1 Base Score 6.5
CWE Classification CWE-862: Missing Authorization
Affected Product Azuracast/azuracast
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 6.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix GHSA-qff7-q5fm-8p76

To remediate this vulnerability, upgrade all affected Azuracast/azuracast dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

azuracast/azuracast fixed: 0.23.6
Upgrade azuracast/azuracast to 0.23.6
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for azuracast/azuracast

How to Check if You Are Affected

To determine whether your application is affected by GHSA-qff7-q5fm-8p76, inspect your project dependencies for Azuracast/azuracast:

inspect dependencies for azuracast/azuracast

Analyze the terminal output to locate any version of Azuracast/azuracast in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.23.6), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In Azuracast/azuracast, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## 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: ```php // 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 ```bash # 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: ```php // 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)); ```

GHSA-qff7-q5fm-8p76 Affected Azuracast/azuracast Versions

1 component(s)
Fixed Versions:
0.23.6
Product / Package Ecosystem Affected Versions Fixed Version
azuracast/azuracast
Packagist
< 0.23.6
0.23.6

References

Frequently Asked Questions

What is GHSA-qff7-q5fm-8p76?
GHSA-qff7-q5fm-8p76 is a security vulnerability in Azuracast/azuracast involving AzuraCast has Missing Permissions Check on Media File Download, Allowing Cross-Station Data Exfiltration under CWE-862: Missing Authorization.
Which Azuracast/azuracast versions are affected?
Affected versions include releases prior to 0.23.6 across affected packages: azuracast/azuracast.
What version fixes GHSA-qff7-q5fm-8p76?
Upgrading to 0.23.6 (or the latest patched release for your respective branch) resolves the vulnerability.
Is azuracast/azuracast affected?
Yes, azuracast/azuracast is affected in releases prior to 0.23.6. Upgrade your dependency to 0.23.6 or later.
What is the CVSS score of GHSA-qff7-q5fm-8p76?
GHSA-qff7-q5fm-8p76 has a CVSS v3.1 base score of 6.5 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N.
What CWE is associated with GHSA-qff7-q5fm-8p76?
GHSA-qff7-q5fm-8p76 is classified under CWE-862: Missing Authorization (The product does not perform an authorization check when an actor attempts to access a resource or perform an action.).
How can I check Packagist dependencies for GHSA-qff7-q5fm-8p76?
Run the dependency tree command: 'inspect dependencies for azuracast/azuracast'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-qff7-q5fm-8p76",
  "modified" : "2026-05-04T21:19:24Z",
  "published" : "2026-05-04T21:19:24Z",
  "aliases" : [ ],
  "summary" : "AzuraCast has Missing Permissions Check on Media File Download, Allowing Cross-Station Data Exfiltration",
  "details" : "## Summary\n\nThe `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.\n\n## Details\n\nIn `backend/config/routes/api_station.php`, the `/file/{id}` route group (lines 407-429) defines four endpoints:\n\n```php\n// Line 407-429\n$group->group(\n    '/file/{id}',\n    function (RouteCollectorProxy $group) {\n        // GET /file/{id} — has Permissions check ✓\n        $group->get('', ...)->add(new Middleware\\Permissions(StationPermissions::Media, true));\n\n        // PUT /file/{id} — has Permissions check ✓\n        $group->put('', ...)->add(new Middleware\\Permissions(StationPermissions::Media, true));\n\n        // DELETE /file/{id} — has Permissions check ✓\n        $group->delete('', ...)->add(new Middleware\\Permissions(StationPermissions::DeleteMedia, true));\n\n        // GET /file/{id}/play — NO Permissions check ✗\n        $group->get('/play', Controller\\Api\\Stations\\Files\\PlayAction::class)\n            ->setName('api:stations:files:play');\n    }\n);\n```\n\nThe 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.\n\nThe 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.\n\nThis 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.\n\n## PoC\n\n```bash\n# Step 1: Create two stations (Station A and Station B) in a multi-tenant AzuraCast instance.\n# Upload media files to Station B.\n\n# Step 2: Create a user with permissions ONLY on Station A. Generate an API key for this user.\nAPI_KEY=\"user-with-only-station-a-access\"\n\n# Step 3: Enumerate and download media from Station B (station_id=2) using sequential IDs\n# This should return 403 Forbidden, but instead returns the file content\ncurl -H \"X-API-Key: $API_KEY\" https://target/api/station/2/file/1/play -o stolen1.mp3\n# HTTP 200 OK — file downloaded successfully\n\ncurl -H \"X-API-Key: $API_KEY\" https://target/api/station/2/file/2/play -o stolen2.mp3\n# HTTP 200 OK — file downloaded successfully\n\n# Step 4: Verify the same user is correctly blocked on other endpoints in the same group\ncurl -H \"X-API-Key: $API_KEY\" https://target/api/station/2/file/1\n# HTTP 403 Forbidden — permission check works here\n```\n\n## Impact\n\n- Any authenticated user can download the full media library of any station in the instance, regardless of their assigned permissions.\n- 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.\n- Media IDs use auto-increment integers (`HasAutoIncrementId` trait on `StationMedia`), enabling trivial enumeration of all media files.\n- The confidentiality impact is High: full media file contents (MP3, FLAC, etc.) are exposed.\n\n## Recommended Fix\n\nAdd the `Permissions` middleware to the `/play` route, matching the pattern used by the adjacent routes:\n\n```php\n// backend/config/routes/api_station.php, line 426-427\n// Before:\n$group->get('/play', Controller\\Api\\Stations\\Files\\PlayAction::class)\n    ->setName('api:stations:files:play');\n\n// After:\n$group->get('/play', Controller\\Api\\Stations\\Files\\PlayAction::class)\n    ->setName('api:stations:files:play')\n    ->add(new Middleware\\Permissions(StationPermissions::Media, true));\n```",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "azuracast/azuracast"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.23.6"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 0.23.5"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/AzuraCast/AzuraCast/security/advisories/GHSA-qff7-q5fm-8p76"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/AzuraCast/AzuraCast/commit/ba92dc3f0ea15a9c0ba0f4557d99a9a26004108f"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/AzuraCast/AzuraCast"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-862" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-04T21:19:24Z",
    "nvd_published_at" : null
  }
}