CVE-2023-49715
A unrestricted php file upload vulnerability exists in the import.json.php temporary copy functionality of WWBN AVideo dev master commit 15fed957fb. A specially crafted HTTP request can lead to arbitrary code execution when chained with an LFI vulnerability. An attacker can send a series of HTTP requests to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
WWBN AVideo dev master commit 15fed957fb
AVideo - https://github.com/WWBN/AVideo
4.3 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
CWE-434 - Unrestricted Upload of File with Dangerous Type
AVideo is a web application, mostly written in PHP, that can be used to create an audio/video sharing website. It allows users to import videos from various sources, encode and share them in various ways. Users can sign up to the website in order to share videos, while viewers have anonymous access to the publicly-available contents. The platform provides plugins for features like live streaming, skins, YouTube uploads and more.
The objects/aVideoEncoder.json.php
file can be used to add new videos. This functionality does not need special configuration to be used. However, the user performing the request needs permission to upload videos.
This page allows for uploading files with any content, but the extension needs to be one of the following (from include_config.php
):
$global['allowedExtension'] = ['gif', 'jpg', 'mp4', 'webm', 'mp3', 'm4a', 'ogg', 'zip', 'm3u8'];
The extension is checked in objects/aVideoEncoder.json.php
:
...
if (empty($_REQUEST['format']) || !in_array($_REQUEST['format'], $global['allowedExtension'])) {
$obj->msg = "aVideoEncoder.json: ERROR Extension not allowed File {$_REQUEST['format']}";
_error_log($obj->msg. ": " . json_encode($_REQUEST));
die(json_encode($obj));
}
...
When uploading a video this way, the filename is chosen based on both format
and resolution
[1]:
...
if (!empty($_FILES['video']['tmp_name'])) {
$resolution = '';
if (!empty($_REQUEST['resolution'])) {
$resolution = "_{$_REQUEST['resolution']}";
}
[1] $filename = "{$videoFileName}{$resolution}.{$_REQUEST['format']}";
$fsize = filesize($_FILES['video']['tmp_name']);
_error_log("aVideoEncoder.json: receiving video upload to {$filename} filesize=" . ($fsize) . " (" . humanFileSize($fsize) . ")" . json_encode($_FILES));
[2] $destinationFile = decideMoveUploadedToVideos($_FILES['video']['tmp_name'], $filename);
}
...
Given format=mp4
and resolution=1.php
, the resulting filename will be _1.php.mp4
.
decideMoveUploadedToVideos
will eventually move the uploaded file to videos/_1.php/_1.php.mp4
[2].
While this operation allows us to specify an invalid resolution such as “1.php”, this won’t directly represent an exploitable issue.
The page objects/import.json.php
, however, can be used to move the uploaded file to a path like /tmp/_1.php
. Let’s see how this happens:
...
[3] if (!preg_match("/.*\\.mp4$/i", $_POST['fileURI'])) {
return false;
}
...
[4] $obj->fileURI = pathinfo($_POST['fileURI']);
//get description
[5] $filename = $obj->fileURI['dirname'] . DIRECTORY_SEPARATOR . $obj->fileURI['filename'];
...
$tmpDir = sys_get_temp_dir();
[6] $tmpFileName = $tmpDir.DIRECTORY_SEPARATOR.$obj->fileURI['filename'];
[7] $source = $obj->fileURI['dirname'] . DIRECTORY_SEPARATOR . $obj->fileURI['basename'];
[8] if (!copy($source, $tmpFileName)) {
$obj->msg = "failed to copy $filename...\n";
die(json_encode($obj));
}
...
The fileURI
parameter must end by .mp4
[3].
At [4] the path components of fileURI
are extracted, and the filename
value is created by stripping off the extension [5].
At [6], tmpFileName
is made of the system’s temporary directory (tmp
) and the filename without extension, while source is going to be the path specified by fileURI
, including the extension [7].
Finally at [8], fileURI
is copied over to /tmp
, without its extension.
In conclusion, if we specify the videos/_1.php/_1.php.mp4
file just uploaded as fileURI
, it will be moved to /tmp/_1.php
. This effectively allows an attacker to upload an arbitrary .php
file to a known location, which can lead to arbitrary code execution when used in conjunction with a local file inclusion vulnerability. This is indeed the case, as demonstrated in TALOS-2023-1886.
It’s important to note, however, that there is a complication, as right at the end of objects/import.json.php
, the following include directive is executed:
require_once $global['systemRootPath'] . 'view/mini-upload-form/upload.php';
Inside upload.php
, the temporary file is moved to its final location, meaning it gets removed from /tmp
, thanks to this line:
decideMoveUploadedToVideos($tmp_name, $filenameMP4, $video->getType());
Despite this, the /tmp/_1.php
file will be available for a short period of time, which is enough for an attacker to execute a series of commands.
2023-12-14 - Vendor Disclosure
2023-12-15 - Vendor Patch Release
2024-01-10 - Public Release
Discovered by Claudio Bozzato of Cisco Talos.