Vulnerability: In FusionPBX up to v4.5.7, the file app\xml_cdr\xml_cdr_delete.php uses an unsanitized "rec" variable coming from the URL, which is base64 decoded and allows deletion of any file of the system.
Vulnerability ID: CVE-2019-16985
CWE: CWE-22
Severity score: 9.2
Project: https://github.com/fusionpbx/fusionpbx
Published: 2019-10-21
File: app/xml_cdr/xml_cdr_delete.php
Commit message: Update xml_cdr_delete.php
Commit author: FusionPBX

Vulnerable version (before fix):
  	The Initial Developer of the Original Code is 	Mark J Crane <markjcrane@fusionpbx.com> 	Copyright (C) 2008-2012 	All Rights Reserved.  	Contributor(s): //get posted values, if any 	if (sizeof($_REQUEST) > 0) { 		$xml_cdr_uuids = $_REQUEST["id"]; 		$recording_file_path = $_REQUEST["rec"];  		if (sizeof($xml_cdr_uuids) > 0) { 			foreach ($xml_cdr_uuids as $index => $xml_cdr_uuid) { 				// delete record from v_xml_cdr 				$sql = "delete from v_xml_cdr "; 				$sql .= "where xml_cdr_uuid = '".$xml_cdr_uuid."' "; 				$prep_statement = $db->prepare(check_sql($sql)); 				$prep_statement->execute(); 				unset($sql, $prep_statement); 				//delete recording from fs and v_call_recordings respectively, if any 				if ($recording_file_path[$index] != '' ) { 					$sql = "delete from v_call_recordings "; 					$sql .= "where call_recording_uuid = '".$xml_cdr_uuid."' "; 					$prep_statement = $db->prepare(check_sql($sql)); 					$prep_statement->execute(); 					unset($sql, $prep_statement); 					if (file_exists(base64_decode($recording_file_path[$index]))) { 						@unlink(base64_decode($recording_file_path[$index])); 					} 				} 			} 		} 	}  //set message and redirect the user 	$_SESSION["message"] = $text['message-delete'].": ".sizeof($xml_cdr_uuids); 	header("Location: xml_cdr.php".(($_SESSION['xml_cdr']['last_query'] != '') ? "?".$_SESSION['xml_cdr']['last_query'] : null));  ?>

Fixed version (after fix):
  	The Initial Developer of the Original Code is 	Mark J Crane <markjcrane@fusionpbx.com> 	Copyright (C) 2008-2019 	All Rights Reserved.  	Contributor(s): //get posted values, if any 	if (sizeof($_REQUEST) > 0) { 		$xml_cdr_uuids = $_REQUEST["id"]; 		if (sizeof($xml_cdr_uuids) > 0) { 			foreach ($xml_cdr_uuids as $index => $xml_cdr_uuid) { 				if (is_uuid($xml_cdr_uuid)) { 					// delete record from v_xml_cdr 					$sql = "delete from v_xml_cdr "; 					$sql .= "where xml_cdr_uuid = :xml_cdr_uuid "; 					$parameters['xml_cdr_uuid'] = $xml_cdr_uuid; 					$database = new database; 					$database->execute($sql, $parameters); 					unset($sql, $parameters);  					//get the call recordings 					$sql = "select * from v_call_recordings "; 					$sql .= "where call_recording_uuid = :xml_cdr_uuid "; 					$parameters['xml_cdr_uuid'] = $xml_cdr_uuid; 					$database = new database; 					$row = $database->select($sql, $parameters, 'row'); 					unset($sql, $parameters);  					//delete the call recording 					$call_recording_path = realpath($row['call_recording_path']); 					$call_recording_name = $row['call_recording_name']; 					if (file_exists($call_recording_path.'/'.$call_recording_name)) { 						@unlink($call_recording_path.'/'.$call_recording_name); 					}  					//delete the call recording meta data 					$sql = "delete from v_call_recordings "; 					$sql .= "where call_recording_uuid = :xml_cdr_uuid "; 					$parameters['xml_cdr_uuid'] = $xml_cdr_uuid; 					$database = new database; 					$database->execute($sql, $parameters); 					unset($sql, $parameters);  				} 			} 		} 	}  //set message and redirect the user 	$_SESSION["message"] = $text['message-delete'].": ".sizeof($xml_cdr_uuids); 	header("Location: xml_cdr.php");  ?>

Patch:
patch: @@ -17,7 +17,7 @@
 
 	The Initial Developer of the Original Code is
 	Mark J Crane <markjcrane@fusionpbx.com>
-	Copyright (C) 2008-2012
+	Copyright (C) 2008-2019
 	All Rights Reserved.
 
 	Contributor(s):
@@ -45,33 +45,47 @@
 //get posted values, if any
 	if (sizeof($_REQUEST) > 0) {
 		$xml_cdr_uuids = $_REQUEST["id"];
-		$recording_file_path = $_REQUEST["rec"];
-
 		if (sizeof($xml_cdr_uuids) > 0) {
 			foreach ($xml_cdr_uuids as $index => $xml_cdr_uuid) {
-				// delete record from v_xml_cdr
-				$sql = "delete from v_xml_cdr ";
-				$sql .= "where xml_cdr_uuid = '".$xml_cdr_uuid."' ";
-				$prep_statement = $db->prepare(check_sql($sql));
-				$prep_statement->execute();
-				unset($sql, $prep_statement);
-				//delete recording from fs and v_call_recordings respectively, if any
-				if ($recording_file_path[$index] != '' ) {
-					$sql = "delete from v_call_recordings ";
-					$sql .= "where call_recording_uuid = '".$xml_cdr_uuid."' ";
-					$prep_statement = $db->prepare(check_sql($sql));
-					$prep_statement->execute();
-					unset($sql, $prep_statement);
-					if (file_exists(base64_decode($recording_file_path[$index]))) {
-						@unlink(base64_decode($recording_file_path[$index]));
+				if (is_uuid($xml_cdr_uuid)) {
+					// delete record from v_xml_cdr
+					$sql = "delete from v_xml_cdr ";
+					$sql .= "where xml_cdr_uuid = :xml_cdr_uuid ";
+					$parameters['xml_cdr_uuid'] = $xml_cdr_uuid;
+					$database = new database;
+					$database->execute($sql, $parameters);
+					unset($sql, $parameters);
+
+					//get the call recordings
+					$sql = "select * from v_call_recordings ";
+					$sql .= "where call_recording_uuid = :xml_cdr_uuid ";
+					$parameters['xml_cdr_uuid'] = $xml_cdr_uuid;
+					$database = new database;
+					$row = $database->select($sql, $parameters, 'row');
+					unset($sql, $parameters);
+
+					//delete the call recording
+					$call_recording_path = realpath($row['call_recording_path']);
+					$call_recording_name = $row['call_recording_name'];
+					if (file_exists($call_recording_path.'/'.$call_recording_name)) {
+						@unlink($call_recording_path.'/'.$call_recording_name);
 					}
+
+					//delete the call recording meta data
+					$sql = "delete from v_call_recordings ";
+					$sql .= "where call_recording_uuid = :xml_cdr_uuid ";
+					$parameters['xml_cdr_uuid'] = $xml_cdr_uuid;
+					$database = new database;
+					$database->execute($sql, $parameters);
+					unset($sql, $parameters);
+
 				}
 			}
 		}
 	}
 
 //set message and redirect the user
 	$_SESSION["message"] = $text['message-delete'].": ".sizeof($xml_cdr_uuids);
-	header("Location: xml_cdr.php".(($_SESSION['xml_cdr']['last_query'] != '') ? "?".$_SESSION['xml_cdr']['last_query'] : null));
+	header("Location: xml_cdr.php");
 
 ?>
path: app/xml_cdr/xml_cdr_delete.php