CVE-2018-12256: Unrestricted Upload of File with Dangerous Type (CWE-434)
value: admin/vqmods.app/vqmods.inc.php in LiteCart before 2.1.3 allows remote authenticated attackers to upload a malicious file (resulting in remote code execution) by using the text/xml or application/xml Content-Type in a public_html/admin/?app=vqmods&doc=vqmods request.
CWE description: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
CVSS v3 base score: 8.8
Published: 2018-08-16T20:29Z
Repository: https://github.com/litecart/litecart
Language: PHP
Files changed: public_html/admin/vqmods.app/vqmods.inc.php
Commit date: 2018-06-15 00:54:20 +0200
Commit message: ! Verify that uploads have valid xml and vqmod content

Diff: diff --git a/public_html/admin/vqmods.app/vqmods.inc.php b/public_html/admin/vqmods.app/vqmods.inc.php
index c510b2f8..41792bc2 100644
--- a/public_html/admin/vqmods.app/vqmods.inc.php
+++ b/public_html/admin/vqmods.app/vqmods.inc.php
@@ -47,24 +47,39 @@
     try {
       if (!isset($_FILES['vqmod']['tmp_name']) || !is_uploaded_file($_FILES['vqmod']['tmp_name'])) {
         throw new Exception(language::translate('error_must_select_file_to_upload', 'You must select a file to upload'));
       }
 
-      if (!in_array($_FILES['vqmod']['type'], array('text/xml', 'application/xml'))) {
-        throw new Exception(language::translate('error_must_provide_vqmod', 'You must provide a valid vQmod file'));
+      $dom = new DOMDocument('1.0', 'UTF-8');
+
+      $xml = file_get_contents($_FILES['vqmod']['tmp_name']); // DOMDocument::load() does not support Windows paths so we use DOMDocument::loadXML()
+
+      if (!@$dom->loadXML($xml)) {
+        throw new Exception(language::translate('error_invalid_xml_file', 'Invalid XML file'));
+      }
+
+      if (!$dom->getElementsByTagName('modification')) {
+        throw new Exception(language::translate('error_xml_file_is_not_valid_vqmod', 'XML file is not a valid vQmod file'));
       }
 
-      move_uploaded_file($_FILES['vqmod']['tmp_name'], FS_DIR_HTTP_ROOT . WS_DIR_HTTP_HOME .'vqmod/xml/'. $_FILES['vqmod']['name']);
+      $filename = FS_DIR_HTTP_ROOT . WS_DIR_HTTP_HOME .'vqmod/xml/'. pathinfo($_FILES['vqmod']['name'], PATHINFO_FILENAME) .'.xml';
+
+      if (is_file($filename)) {
+        unlink($filename);
+      }
+
+      move_uploaded_file($_FILES['vqmod']['tmp_name'], $filename);
 
       notices::add('success', language::translate('success_changes_saved', 'Changes saved successfully'));
       header('Location: '. document::ilink());
       exit;
 
     } catch (Exception $e) {
       notices::add('errors', $e->getMessage());
     }
   }
+
 ?>
 <h1><?php echo $app_icon; ?> <?php echo language::translate('title_vqmods', 'vQmods'); ?></h1>
 
 <?php echo functions::form_draw_form_begin('vqmods_form', 'post'); ?>
 

Vulnerable code:       if (!in_array($_FILES['vqmod']['type'], array('text/xml', 'application/xml'))) {
        throw new Exception(language::translate('error_must_provide_vqmod', 'You must provide a valid vQmod file'));
      move_uploaded_file($_FILES['vqmod']['tmp_name'], FS_DIR_HTTP_ROOT . WS_DIR_HTTP_HOME .'vqmod/xml/'. $_FILES['vqmod']['name']);
Fixed code:       $dom = new DOMDocument('1.0', 'UTF-8');

      $xml = file_get_contents($_FILES['vqmod']['tmp_name']); // DOMDocument::load() does not support Windows paths so we use DOMDocument::loadXML()

      if (!@$dom->loadXML($xml)) {
        throw new Exception(language::translate('error_invalid_xml_file', 'Invalid XML file'));
      }

      if (!$dom->getElementsByTagName('modification')) {
        throw new Exception(language::translate('error_xml_file_is_not_valid_vqmod', 'XML file is not a valid vQmod file'));
      $filename = FS_DIR_HTTP_ROOT . WS_DIR_HTTP_HOME .'vqmod/xml/'. pathinfo($_FILES['vqmod']['name'], PATHINFO_FILENAME) .'.xml';

      if (is_file($filename)) {
        unlink($filename);
      }

      move_uploaded_file($_FILES['vqmod']['tmp_name'], $filename);