CVE-2022-35153: Improper Encoding or Escaping of Output (CWE-116)
value: FusionPBX 5.0.1 was discovered to contain a command injection vulnerability via /fax/fax_send.php.
CWE description: The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.
CVSS v3 base score: 9.8
Published: 2022-08-18T05:15Z
Repository: https://github.com/fusionpbx/fusionpbx
Language: PHP
Files changed: secure/fax_to_email.php
Commit date: 2022-06-29 10:38:36 -0600
Commit message: Add escapeshellarg to the variables used in the command line.
Diff: diff --git a/secure/fax_to_email.php b/secure/fax_to_email.php
index 6694abfc94..9b8632a429 100644
--- a/secure/fax_to_email.php
+++ b/secure/fax_to_email.php
@@ -271,11 +271,11 @@ if (!function_exists('fax_split_dtmf')) {
 		unset($tmp_array);
 
 		$tmp_array = explode("=", $_SERVER["argv"][9]);
 		$fax_prefix = $tmp_array[1];
 		unset($tmp_array);
-	
+
 		$tmp_array = explode("=", $_SERVER["argv"][10]);
 		$mail_from_address = $tmp_array[1];
 		unset($tmp_array);
 
 		//$tmp_array = explode("=", $_SERVER["argv"][10]);
@@ -379,11 +379,11 @@ if (!function_exists('fax_split_dtmf')) {
 		}
 
 		if (isset($fax_forward_number) && strlen($fax_forward_number) > 0) {
 			//show info
 				echo "fax_forward_number: $fax_forward_number\n";
-		
+
 			//add fax to the fax queue or send it directly
 			if ($_SESSION['fax_queue']['enabled']['boolean'] == 'true') {
 				//build an array to add the fax to the queue
 				$array['fax_queue'][0]['fax_queue_uuid'] = uuid();
 				$array['fax_queue'][0]['domain_uuid'] = $domain_uuid;
@@ -425,47 +425,47 @@ if (!function_exists('fax_split_dtmf')) {
 				}
 
 				$route_array = outbound_route_to_bridge($domain_uuid, $fax_forward_number);
 				if (count($route_array) == 0) {
 					//send the internal call to the registered extension
-						$fax_uri = "user/".$fax_forward_number."@".$domain_name;
+						$fax_uri = "user/".escapeshellarg($fax_forward_number)."@".escapeshellarg($domain_name);
 						$fax_variables = "";
 				}
 				else {
 					//send the external call
 						$fax_uri = $route_array[0];
 						$fax_variables = "";
 						foreach($_SESSION['fax']['variable'] as $variable) {
-							$fax_variables .= $variable.",";
+							$fax_variables .= escapeshellarg($variable).",";
 						}
 				}
 
 				//build the dial string
 				$dial_string = "absolute_codec_string='PCMU,PCMA',";
-				$dial_string .= "accountcode='"                  . $fax_accountcode         . "',";
-				$dial_string .= "sip_h_X-accountcode='"          . $fax_accountcode         . "',";
-				$dial_string .= "domain_uuid="                   . $domain_uuid . ",";
-				$dial_string .= "domain_name="                   . $domain_name . ",";
-				$dial_string .= "origination_caller_id_name='"   . $fax_caller_id_name      . "',";
-				$dial_string .= "origination_caller_id_number='" . $fax_caller_id_number    . "',";
-				$dial_string .= "fax_ident='"                    . $fax_caller_id_number    . "',";
-				$dial_string .= "fax_header='"                   . $fax_caller_id_name      . "',";
-				$dial_string .= "fax_file='"                     . $fax_file                . "',";
+				$dial_string .= "accountcode='"                  . escapeshellarg($fax_accountcode)         . "',";
+				$dial_string .= "sip_h_X-accountcode='"          . escapeshellarg($fax_accountcode)         . "',";
+				$dial_string .= "domain_uuid="                   . escapeshellarg($domain_uuid)             . ",";
+				$dial_string .= "domain_name="                   . escapeshellarg($domain_name)             . ",";
+				$dial_string .= "origination_caller_id_name='"   . escapeshellarg($fax_caller_id_name)      . "',";
+				$dial_string .= "origination_caller_id_number='" . escapeshellarg($fax_caller_id_number)    . "',";
+				$dial_string .= "fax_ident='"                    . escapeshellarg($fax_caller_id_number)    . "',";
+				$dial_string .= "fax_header='"                   . escapeshellarg($fax_caller_id_name)      . "',";
+				$dial_string .= "fax_file='"                     . escapeshellarg($fax_file)                . "',";
 
 				if ($fax_send_mode != 'queue') {
 					//add more ot the dial string
 						$dial_string .= $fax_variables;
-						$dial_string .= "mailto_address='"     . $mail_to_address   . "',";
-						$dial_string .= "mailfrom_address='"   . $mail_from_address . "',";
-						$dial_string .= "fax_uri=" . $fax_uri  . ",";
+						$dial_string .= "mailto_address='"     . escapeshellarg($mail_to_address)   . "',";
+						$dial_string .= "mailfrom_address='"   . escapeshellarg($mail_from_address) . "',";
+						$dial_string .= "fax_uri="             . escapeshellarg($fax_uri)  . ",";
 						$dial_string .= "fax_retry_attempts=1" . ",";
 						$dial_string .= "fax_retry_limit=20"   . ",";
 						$dial_string .= "fax_retry_sleep=180"  . ",";
 						$dial_string .= "fax_verbose=true"     . ",";
 						$dial_string .= "fax_use_ecm=off"      . ",";
 						$dial_string .= "api_hangup_hook='lua fax_retry.lua'";
-						$dial_string = "{" . $dial_string . "}" . $fax_uri." &txfax('".$fax_file."')";
+						$dial_string = "{" . $dial_string . "}" . escapeshellarg($fax_uri)." &txfax('".escapeshellarg($fax_file)."')";
 
 					//get the event socket information
 						$sql = "select * from v_settings ";
 						$database = new database;
 						$row = $database->select($sql, $parameters, 'row');
@@ -627,27 +627,10 @@ if (!function_exists('fax_split_dtmf')) {
 				//log the success
 					$fp = fopen($fax_to_email_queue_dir."/emailed_faxes.log", "a");
 					fwrite($fp, $fax_file_name." received on ".$fax_extension." emailed to ".$fax_email." ".$fax_messages."\n");
 					fclose($fp);
 			}
-			else {
-				//create an instruction log to email messages once the connection to the mail server has been restored
-					$fp = fopen($fax_to_email_queue_dir."/failed_fax_emails.log", "a");
-					fwrite($fp, PHP_BINDIR."/php ".$_SERVER["DOCUMENT_ROOT"].PROJECT_PATH."/secure/fax_to_email.php email='".$fax_email."' extension=".$fax_extension." name='".$fax_file."' messages='".$fax_messages."' domain=".$domain_name." caller_id_name='".$caller_id_name."' caller_id_number=".$caller_id_number." retry=true\n");
-					fclose($fp);
-				//create a script to do the delayed mailing
-					$fp = fopen($_SESSION['server']['temp']['dir']."/failed_fax_emails.sh", "w");
-					fwrite($fp, "rm ".$_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
-					fwrite($fp, "mv ".$fax_to_email_queue_dir."/failed_fax_emails.log ".$_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
-					fwrite($fp, "chmod 777 ".$_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
-					fwrite($fp, $_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
-					fclose($fp);
-					$tmp_response = exec("chmod 777 ".$_SESSION['server']['temp']['dir']."/failed_fax_emails.sh");
-				//note we use batch in order to execute when system load is low.  Alternatively this could be replaced with AT.
-					$tmp_response = exec("at -f ".$_SESSION['server']['temp']['dir']."/failed_fax_emails.sh now + 3 minutes");
-				
-			}
 		}
 	}
 
 //open the file for writing
 	if ($output_type == "file") {

Vulnerable code: 	
		
						$fax_uri = "user/".$fax_forward_number."@".$domain_name;
							$fax_variables .= $variable.",";
				$dial_string .= "accountcode='"                  . $fax_accountcode         . "',";
				$dial_string .= "sip_h_X-accountcode='"          . $fax_accountcode         . "',";
				$dial_string .= "domain_uuid="                   . $domain_uuid . ",";
				$dial_string .= "domain_name="                   . $domain_name . ",";
				$dial_string .= "origination_caller_id_name='"   . $fax_caller_id_name      . "',";
				$dial_string .= "origination_caller_id_number='" . $fax_caller_id_number    . "',";
				$dial_string .= "fax_ident='"                    . $fax_caller_id_number    . "',";
				$dial_string .= "fax_header='"                   . $fax_caller_id_name      . "',";
				$dial_string .= "fax_file='"                     . $fax_file                . "',";
						$dial_string .= "mailto_address='"     . $mail_to_address   . "',";
						$dial_string .= "mailfrom_address='"   . $mail_from_address . "',";
						$dial_string .= "fax_uri=" . $fax_uri  . ",";
						$dial_string = "{" . $dial_string . "}" . $fax_uri." &txfax('".$fax_file."')";
			else {
				//create an instruction log to email messages once the connection to the mail server has been restored
					$fp = fopen($fax_to_email_queue_dir."/failed_fax_emails.log", "a");
					fwrite($fp, PHP_BINDIR."/php ".$_SERVER["DOCUMENT_ROOT"].PROJECT_PATH."/secure/fax_to_email.php email='".$fax_email."' extension=".$fax_extension." name='".$fax_file."' messages='".$fax_messages."' domain=".$domain_name." caller_id_name='".$caller_id_name."' caller_id_number=".$caller_id_number." retry=true\n");
					fclose($fp);
				//create a script to do the delayed mailing
					$fp = fopen($_SESSION['server']['temp']['dir']."/failed_fax_emails.sh", "w");
					fwrite($fp, "rm ".$_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
					fwrite($fp, "mv ".$fax_to_email_queue_dir."/failed_fax_emails.log ".$_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
					fwrite($fp, "chmod 777 ".$_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
					fwrite($fp, $_SESSION['server']['temp']['dir']."/fax_email_retry.sh\n");
					fclose($fp);
					$tmp_response = exec("chmod 777 ".$_SESSION['server']['temp']['dir']."/failed_fax_emails.sh");
				//note we use batch in order to execute when system load is low.  Alternatively this could be replaced with AT.
					$tmp_response = exec("at -f ".$_SESSION['server']['temp']['dir']."/failed_fax_emails.sh now + 3 minutes");
				
			}
Fixed code: 

						$fax_uri = "user/".escapeshellarg($fax_forward_number)."@".escapeshellarg($domain_name);
							$fax_variables .= escapeshellarg($variable).",";
				$dial_string .= "accountcode='"                  . escapeshellarg($fax_accountcode)         . "',";
				$dial_string .= "sip_h_X-accountcode='"          . escapeshellarg($fax_accountcode)         . "',";
				$dial_string .= "domain_uuid="                   . escapeshellarg($domain_uuid)             . ",";
				$dial_string .= "domain_name="                   . escapeshellarg($domain_name)             . ",";
				$dial_string .= "origination_caller_id_name='"   . escapeshellarg($fax_caller_id_name)      . "',";
				$dial_string .= "origination_caller_id_number='" . escapeshellarg($fax_caller_id_number)    . "',";
				$dial_string .= "fax_ident='"                    . escapeshellarg($fax_caller_id_number)    . "',";
				$dial_string .= "fax_header='"                   . escapeshellarg($fax_caller_id_name)      . "',";
				$dial_string .= "fax_file='"                     . escapeshellarg($fax_file)                . "',";
						$dial_string .= "mailto_address='"     . escapeshellarg($mail_to_address)   . "',";
						$dial_string .= "mailfrom_address='"   . escapeshellarg($mail_from_address) . "',";
						$dial_string .= "fax_uri="             . escapeshellarg($fax_uri)  . ",";
						$dial_string = "{" . $dial_string . "}" . escapeshellarg($fax_uri)." &txfax('".escapeshellarg($fax_file)."')";