A station user with only View Station Page can call GET /api/station/{station_id}/vue/profile and receive the station’s Icecast/Shoutcast admin , source , and relay passwords in the JSON response.
Those credentials are treated as Broadcasting -sensitive in the Vue UI ( FrontendPanel.vue gates display on StationPermissions.Broadcasting ). The API route does not: the /vue group requires only StationPermissions::View , and ProfileAction always serializes the three password fields.
This is authenticated station-scoped privilege escalation / sensitive info disclosure — not unauthenticated, and not “admin can admin.” Confirmed end-to-end: a View-only session receives the passwords; the leaked admin_pw authenticates to Icecast admin ( /admin/stats → HTTP 200).
Tested: commit 3f9d6ff626745d9aa20a7158eec7a8583ed635e0 (rolling), Docker
Components: backend/config/routes/api_station_vue.php ( GET /vue/profile under View-only group) backend/src/Controller/Api/Stations/Vue/ProfileAction.php (unconditional password fields) Contrast (correct UI gate): frontend/components/Stations/Profile/FrontendPanel.vue
backend/config/routes/api_station_vue.php ( GET /vue/profile under View-only group)
backend/src/Controller/Api/Stations/Vue/ProfileAction.php (unconditional password fields)
Contrast (correct UI gate): frontend/components/Stations/Profile/FrontendPanel.vue
Authenticated account (session cookie or API key) with StationPermissions::View on a station
Without Broadcasting (or equivalent) on that station
Station frontend configured (Icecast/Shoutcast passwords present in frontend_config )
On multi-user / multi-role installs, a View-only staff account, guest observer, or View-scoped API key can obtain live broadcast frontend secrets that the product’s own UI treats as Broadcasting-only. With the leaked Icecast admin_pw , the attacker authenticates to the Icecast admin interface (listener/stats/admin actions such as kicking clients) without holding Broadcasting in AzuraCast.
Single-admin installs are lower practical risk; the API still violates the permission model.
Attacker holds station View only (no Broadcasting).
Attacker requests GET /api/station/{id}/vue/profile (browser session + CSRF, or API key).
Response includes frontendAdminPassword , frontendSourcePassword , frontendRelayPassword .
Attacker uses admin_pw against Icecast admin (e.g. /admin/stats ).
Environment: AzuraCast Docker, web on , Icecast on port 8000 , commit 3f9d6ff .
As Super Admin, create a role with only “View Station Page” for the target station. Create a user with that role (no Broadcasting, no Profile, no global admin).
As Super Admin, create a role with only “View Station Page” for the target station. Create a user with that role (no Broadcasting, no Profile, no global admin).
Log in as that View-only user in a browser (e.g. through Burp). Open the station management / Profile page so the Vue client loads profile props.
Log in as that View-only user in a browser (e.g. through Burp). Open the station management / Profile page so the Vue client loads profile props.
In Burp HTTP history (or Repeater), inspect:
In Burp HTTP history (or Repeater), inspect:
No X-API-Key required when using a normal login session.
Observe HTTP 200 with plaintext fields (values redacted here):
Confirm the same View-only principal is denied Broadcasting surfaces:
Confirm UI contrast: as the same user, the Profile Broadcasting credentials panel is not rendered ( FrontendPanel.vue v-if on Broadcasting). Secrets are still present in the API response from step 4.
Confirm UI contrast: as the same user, the Profile Broadcasting credentials panel is not rendered ( FrontendPanel.vue v-if on Broadcasting). Secrets are still present in the API response from step 4.
Impact check : authenticate to Icecast with the leaked admin password:
Impact check : authenticate to Icecast with the leaked admin password:
Wrong credentials → HTTP 401 / empty body.
Expected: Password fields omitted (or request denied) unless the caller has Broadcasting. Actual: View is enough; passwords always returned.
Route group applies only View; /profile adds no Broadcasting middleware (unlike /files / /podcasts ):
Controller always returns frontend passwords:
UI-only gate (does not protect the API):
Preferred: In ProfileAction (or a response mapper), omit frontendAdminPassword , frontendSourcePassword , and frontendRelayPassword unless the ACL grants StationPermissions::Broadcasting .
Do not rely on the Vue v-if alone for secrets; keep it as defense-in-depth.
Avoid gating the entire /vue/profile response on Broadcasting if View still needs non-secret profile fields strip secrets server-side instead.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
