CVE-2018-16974: Unrestricted Upload of File with Dangerous Type (CWE-434)
value: An issue was discovered in Elefant CMS before 2.0.7. There is a PHP Code Execution Vulnerability in apps/filemanager/upload/drop.php by using /filemanager/api/rm/.htaccess to remove the .htaccess file, and then using a filename that ends in .php followed by space characters (for bypassing the blacklist).
CWE description: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
CVSS v3 base score: 9.8
Published: 2018-09-12T21:29Z
Repository: https://github.com/jbroadway/elefant
Language: PHP
Files changed: apps/filemanager/handlers/embed.php, apps/filemanager/handlers/redactor/upload.php, apps/filemanager/handlers/upload.php, apps/filemanager/handlers/upload/drop.php, apps/filemanager/lib/API.php, apps/filemanager/lib/FileManager.php
Commit date: 2018-09-11 02:13:26 -0500
Commit message: Fixed filemanager file upload validations, re: #287

Diff: diff --git a/apps/filemanager/handlers/embed.php b/apps/filemanager/handlers/embed.php
index 12308f06..cb11607e 100644
--- a/apps/filemanager/handlers/embed.php
+++ b/apps/filemanager/handlers/embed.php
@@ -108,11 +108,13 @@ switch ($_GET['action']) {
 		if (strpos ($_POST['newName'], '..') !== false || strpos ($_POST['newName'], '/') !== false) {
 			echo __ ('Invalid name');
 			return;
 		}
 		
-		if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_POST['newName'])) {
+		$_POST['newName'] = trim ($_POST['newName']);
+		
+		if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_POST['newName'])) {
 			echo __ ('Invalid file type');
 			return;
 		}
 		
 		$dest = ltrim ($_POST['dir'], '/') . '/' . $_POST['newName'];
diff --git a/apps/filemanager/handlers/redactor/upload.php b/apps/filemanager/handlers/redactor/upload.php
index 430355e9..7717b1d5 100644
--- a/apps/filemanager/handlers/redactor/upload.php
+++ b/apps/filemanager/handlers/redactor/upload.php
@@ -39,13 +39,13 @@ foreach ($_FILES['file']['error'] as $error) {
 		return;
 	}
 }
 
 // some browsers may urlencode the file name
-$_FILES['file']['name'] = urldecode ($_FILES['file']['name']);
+$_FILES['file']['name'] = trim (urldecode ($_FILES['file']['name']));
 
-if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_FILES['file']['name'])) {
+if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_FILES['file']['name'])) {
 	echo json_encode (array ('error' => __ ('Cannot upload executable files due to security.')));
 	return;
 }
 
 if (file_exists ($root . $_FILES['file']['name'])) {
diff --git a/apps/filemanager/handlers/upload.php b/apps/filemanager/handlers/upload.php
index 29bedbb8..780ea4ce 100644
--- a/apps/filemanager/handlers/upload.php
+++ b/apps/filemanager/handlers/upload.php
@@ -47,11 +47,11 @@ foreach ($_FILES['file']['error'] as $error) {
 		return;
 	}
 }
 
 for ($i = 0; $i < count ($_FILES['file']['name']); $i++) {
-	$_FILES['file']['name'][$i] = urldecode ($_FILES['file']['name'][$i]);
+	$_FILES['file']['name'][$i] = trim (urldecode ($_FILES['file']['name'][$i]));
 	if (@file_exists ($root . $_POST['path'] . '/' . $_FILES['file']['name'][$i])) {
 		$page->title = __ ('File Already Exists') . ': ' . $_FILES['file']['name'][$i];
 		echo '<p>' . __ ('A file by that name already exists.') . '</p>';
 		echo '<p><a href="/filemanager">' . __ ('Back') . '</a></p>';
 		return;
@@ -60,11 +60,11 @@ for ($i = 0; $i < count ($_FILES['file']['name']); $i++) {
 		$page->title = __ ('Invalid File Name') . ': ' . $_FILES['file']['name'][$i];
 		echo '<p>' . __ ('The file name contains invalid characters.') . '</p>';
 		echo '<p><a href="/filemanager">' . __ ('Back') . '</a></p>';
 		return;
 	}
-	if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_FILES['file']['name'][$i])) {
+	if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_FILES['file']['name'][$i])) {
 		$page->title = __ ('Invalid File Name') . ': ' . $_FILES['file']['name'][$i];
 		echo '<p>' . __ ('Cannot upload executable files due to security.') . '</p>';
 		echo '<p><a href="/filemanager">' . __ ('Back') . '</a></p>';
 		return;
 	}
diff --git a/apps/filemanager/handlers/upload/drop.php b/apps/filemanager/handlers/upload/drop.php
index 3a537c7c..f5119b94 100644
--- a/apps/filemanager/handlers/upload/drop.php
+++ b/apps/filemanager/handlers/upload/drop.php
@@ -43,13 +43,13 @@ if (isset ($_FILES['file']['error']) && $_FILES['file']['error'] > 0) {
 	echo json_encode (array ('success' => false, 'error' => $errors[$_FILES['file']['error']]));
 	return;
 }
 
 // some browsers may urlencode the file name
-$_FILES['file']['name'] = urldecode ($_FILES['file']['name']);
+$_FILES['file']['name'] = trim (urldecode ($_FILES['file']['name']));
 
-if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_FILES['file']['name'])) {
+if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_FILES['file']['name'])) {
 	echo json_encode (array ('success' => false, 'error' => __ ('Cannot upload executable files due to security.')));
 	return;
 }
 
 if (@file_exists ($root . $_POST['path'] . '/' . $_FILES['file']['name'])) {
diff --git a/apps/filemanager/lib/API.php b/apps/filemanager/lib/API.php
index 7d1a1e1f..e61fd48d 100644
--- a/apps/filemanager/lib/API.php
+++ b/apps/filemanager/lib/API.php
@@ -11,11 +11,11 @@ use DB, FileManager, I18n, Restful, Zipper;
 class API extends Restful {
 	/**
 	 * Handle list directory requests (/filemanager/api/ls).
 	 */
 	public function get_ls () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 
 		$res = FileManager::dir ($file);
 		if (! $res) {
 			return $this->error (FileManager::error ());
 		}
@@ -41,20 +41,20 @@ class API extends Restful {
 	
 	/**
 	 * Handle Bitly link requests (/filemanager/api/bitly).
 	 */
 	public function get_bitly () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 		$link = $this->controller->absolutize ('/files/' . $file);
 		return BitlyLink::lookup ($link);
 	}
 
 	/**
 	 * Handle remove file requests (/filemanager/api/rm).
 	 */
 	public function post_rm () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 
 		$res = FileManager::unlink ($file);
 		if (! $res) {
 			return $this->error (FileManager::error ());
 		}
@@ -69,11 +69,11 @@ class API extends Restful {
 	/**
 	 * Handle remove folder requests (/filemanager/api/rmdir).
 	 * Note: Erases the contents of the folder as well.
 	 */
 	public function post_rmdir () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 
 		$res = FileManager::rmdir ($file, true);
 		if (! $res) {
 			return $this->error (FileManager::error ());
 		}
@@ -87,11 +87,11 @@ class API extends Restful {
 
 	/**
 	 * Handle rename requests (/filemanager/api/mv).
 	 */
 	public function post_mv () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 
 		$is_folder = FileManager::verify_folder ($file) ? true : false;
 		
 		if (! FileManager::rename ($file, $_POST['rename'])) {
 			return $this->error (FileManager::error ());
@@ -112,11 +112,11 @@ class API extends Restful {
 	/**
 	 * Handle drop requests (/filemanager/api/drop), which move files between
 	 * folders.
 	 */
 	public function post_drop () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 		
 		if (! FileManager::move ($file, $_POST['folder'])) {
 			return $this->error (FileManager::error ());
 		}
 
@@ -131,11 +131,11 @@ class API extends Restful {
 
 	/**
 	 * Handle make directory requests (/filemanager/api/mkdir).
 	 */
 	public function post_mkdir () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 		
 		if (! FileManager::mkdir ($file)) {
 			return $this->error (FileManager::error ());
 		}
 
@@ -151,11 +151,11 @@ class API extends Restful {
 	 *
 	 * Otherwise, individual `prop` and `value` parameters can
 	 * be used to set an individual property's value.
 	 */
 	public function post_prop () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 		if (! FileManager::verify_file ($file)) {
 			return $this->error (__ ('Invalid file name'));
 		}
 		
 		// handle multiple properties at once
@@ -198,11 +198,11 @@ class API extends Restful {
 	
 	/**
 	 * Handle unzip requests via (/filemanager/api/unzip).
 	 */
 	public function post_unzip () {
-		$file = urldecode (join ('/', func_get_args ()));
+		$file = trim (urldecode (join ('/', func_get_args ())));
 		if (! FileManager::verify_file ($file)) {
 			return $this->error (__ ('Invalid file name'));
 		}
 		
 		// make sure it's a zip file
diff --git a/apps/filemanager/lib/FileManager.php b/apps/filemanager/lib/FileManager.php
index bf77c25c..2f08c061 100644
--- a/apps/filemanager/lib/FileManager.php
+++ b/apps/filemanager/lib/FileManager.php
@@ -158,10 +158,13 @@ class FileManager {
 			self::$error = __ ('Unable to delete folders');
 			return false;
 		} elseif (! self::verify_file ($file)) {
 			self::$error = __ ('File not found');
 			return false;
+		} elseif (! self::verify_file_name ($file)) {
+			self::$error = __ ('Invalid file name');
+			return false;
 		} elseif (! unlink (self::root () . $file)) {
 			self::$error = __ ('Unable to delete') . ' ' . $file;
 			return false;
 		}
 		self::prop_delete ($file);
@@ -403,11 +406,11 @@ class FileManager {
 	 */
 	public static function verify_file_name ($name) {
 		if (! preg_match ('/^[a-zA-Z0-9 _-]+\.[a-zA-Z0-9_-]+$/', $name)) {
 			return false;
 		}
-		if (preg_match ('/\.php$/i', $name)) {
+		if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $name)) {
 			return false;
 		}
 		return true;
 	}
 

Vulnerable code: 		if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_POST['newName'])) {
$_FILES['file']['name'] = urldecode ($_FILES['file']['name']);
if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_FILES['file']['name'])) {
	$_FILES['file']['name'][$i] = urldecode ($_FILES['file']['name'][$i]);
	if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_FILES['file']['name'][$i])) {
$_FILES['file']['name'] = urldecode ($_FILES['file']['name']);
if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe)$/i', $_FILES['file']['name'])) {
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		$file = urldecode (join ('/', func_get_args ()));
		if (preg_match ('/\.php$/i', $name)) {
Fixed code: 		$_POST['newName'] = trim ($_POST['newName']);
		
		if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_POST['newName'])) {
$_FILES['file']['name'] = trim (urldecode ($_FILES['file']['name']));
if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_FILES['file']['name'])) {
	$_FILES['file']['name'][$i] = trim (urldecode ($_FILES['file']['name'][$i]));
	if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_FILES['file']['name'][$i])) {
$_FILES['file']['name'] = trim (urldecode ($_FILES['file']['name']));
if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $_FILES['file']['name'])) {
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		$file = trim (urldecode (join ('/', func_get_args ())));
		} elseif (! self::verify_file_name ($file)) {
			self::$error = __ ('Invalid file name');
			return false;
		if (preg_match ('/\.(php|phtml|pht|php3|php4|php5|phar|js|rb|py|pl|sh|bash|exe|htaccess|htpasswd)$/i', $name)) {