Vulnerability: app/operator_panel/exec.php in the Operator Panel module in FusionPBX 4.4.3 suffers from a command injection vulnerability due to a lack of input validation that allows authenticated non-administrative attackers to execute commands on the host. This can further lead to remote code execution when combined with an XSS vulnerability also present in the FusionPBX Operator Panel module.
Vulnerability ID: CVE-2019-11409
CWE: CWE-78
Severity score: 6.4
Project: https://github.com/fusionpbx/fusionpbx
Published: 2019-06-17
File: app/operator_panel/exec.php
Commit message: Update exec.php
Commit author: FusionPBX

Vulnerable version (before fix):
 	//setup the event socket connection 		$fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']);  	if (stristr($action, 'user_status') == true) { 		$user_status = $data; 		switch ($user_status) { 		case "Available": 			$user_status = "Available"; 			//update the user state 			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 			$response = event_socket_request($fp, $cmd); 			break; 		case "Available_On_Demand": 			$user_status = "Available (On Demand)"; 			//update the user state 			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 			$response = event_socket_request($fp, $cmd); 			break; 		case "Logged_Out": 			$user_status = "Logged Out"; 			//update the user state 			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 			$response = event_socket_request($fp, $cmd); 			break; 		case "On_Break": 			$user_status = "On Break"; 			//update the user state 			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 			$response = event_socket_request($fp, $cmd); 			break; 		case "Do_Not_Disturb": 			$user_status = "Do Not Disturb"; 			//update the user state 			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 			$response = event_socket_request($fp, $cmd); 			break; 		default: 			$user_status = ""; 		} 	}  	//fs cmd 	if (strlen($switch_cmd) > 0) {  		//set the status so they are compatible with mod_callcenter 			$switch_cmd = str_replace("Available_On_Demand", "'Available (On Demand)'", $switch_cmd); 			$switch_cmd = str_replace("Logged_Out", "'Logged Out'", $switch_cmd); 			$switch_cmd = str_replace("On_Break", "'On Break'", $switch_cmd); 			$switch_cmd = str_replace("Do_Not_Disturb", "'Logged Out'", $switch_cmd);  		/* 		//if ($action == "energy") { 			//conference 3001-example.org energy 103 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 			$result_array = explode("=",$switch_result); 			$tmp_value = $result_array[1]; 			//if ($direction == "up") { $tmp_value = $tmp_value + 100; } 			//if ($direction == "down") { $tmp_value = $tmp_value - 100; } 			//echo "energy $tmp_value<br />\n"; 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value); 		//} 		if ($action == "volume_in") { 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 			$result_array = explode("=",$switch_result); 			$tmp_value = $result_array[1]; 			if ($direction == "up") { $tmp_value = $tmp_value + 1; } 			if ($direction == "down") { $tmp_value = $tmp_value - 1; } 			//echo "volume $tmp_value<br />\n"; 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value); 		} 		if ($action == "volume_out") { 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 			$result_array = explode("=",$switch_result); 			$tmp_value = $result_array[1]; 			if ($direction == "up") { $tmp_value = $tmp_value + 1; } 			if ($direction == "down") { $tmp_value = $tmp_value - 1; } 			//echo "volume $tmp_value<br />\n"; 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value); 		} 		*/  		$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 		if ($action == "record") { 			if (trim($_GET["action2"]) == "stop") { 				$x=0; 				while (true) { 					if ($x > 0) { 						$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"]."_".$x.".wav"; 					} 					else { 						$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav"; 					} 					if (!file_exists($dest_file)) { 						rename($_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav", $dest_file); 						break; 					} 					$x++; 				} 			} 		} 	} }  ?>\ No newline at end of file

Fixed version (after fix):
 	//setup the event socket connection 		$fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']);  	//get the status 		if (stristr($action, 'user_status') == true) { 			$user_status = $data; 			switch ($user_status) { 			case "Available": 				$user_status = "Available"; 				//update the user state 				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 				$response = event_socket_request($fp, $cmd); 				break; 			case "Available_On_Demand": 				$user_status = "Available (On Demand)"; 				//update the user state 				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 				$response = event_socket_request($fp, $cmd); 				break; 			case "Logged_Out": 				$user_status = "Logged Out"; 				//update the user state 				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 				$response = event_socket_request($fp, $cmd); 				break; 			case "On_Break": 				$user_status = "On Break"; 				//update the user state 				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 				$response = event_socket_request($fp, $cmd); 				break; 			case "Do_Not_Disturb": 				$user_status = "Do Not Disturb"; 				//update the user state 				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting"; 				$response = event_socket_request($fp, $cmd); 				break; 			default: 				$user_status = ""; 			} 		}  	//allow specific commands 		if (strlen($switch_cmd) > 0) { 			if (stristr($switch_cmd, 'originate') == true) {} 			elseif (stristr($switch_cmd, 'uuid_record') == true) {} 			elseif (stristr($switch_cmd, 'uuid_transfer') == true) {} 			elseif (stristr($switch_cmd, 'eavesdrop') == true) {} 			elseif (stristr($switch_cmd, 'uuid_kill') == true) {} 			else { 				$switch_cmd = ''; 			} 			if (stristr($switch_cmd, 'system') == true) { 				$switch_cmd = ''; 			} 		}  	//switch cmd 		if (strlen($switch_cmd) > 0) {  			//set the status so they are compatible with mod_callcenter 			$switch_cmd = str_replace("Available_On_Demand", "'Available (On Demand)'", $switch_cmd); 			$switch_cmd = str_replace("Logged_Out", "'Logged Out'", $switch_cmd); 			$switch_cmd = str_replace("On_Break", "'On Break'", $switch_cmd); 			$switch_cmd = str_replace("Do_Not_Disturb", "'Logged Out'", $switch_cmd);  			/* 			//if ($action == "energy") { 				//conference 3001-example.org energy 103 				$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 				$result_array = explode("=",$switch_result); 				$tmp_value = $result_array[1]; 				//if ($direction == "up") { $tmp_value = $tmp_value + 100; } 				//if ($direction == "down") { $tmp_value = $tmp_value - 100; } 				//echo "energy $tmp_value<br />\n"; 				$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value); 			//} 			if ($action == "volume_in") { 				$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 				$result_array = explode("=",$switch_result); 				$tmp_value = $result_array[1]; 				if ($direction == "up") { $tmp_value = $tmp_value + 1; } 				if ($direction == "down") { $tmp_value = $tmp_value - 1; } 				//echo "volume $tmp_value<br />\n"; 				$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value); 			} 			if ($action == "volume_out") { 				$switch_result = event_socket_request($fp, 'api '.$switch_cmd); 				$result_array = explode("=",$switch_result); 				$tmp_value = $result_array[1]; 				if ($direction == "up") { $tmp_value = $tmp_value + 1; } 				if ($direction == "down") { $tmp_value = $tmp_value - 1; } 				//echo "volume $tmp_value<br />\n"; 				$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value); 			} 			*/  			//run the command 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd);  			//record stop 			if ($action == "record") { 				if (trim($_GET["action2"]) == "stop") { 					$x=0; 					while (true) { 						if ($x > 0) { 							$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"]."_".$x.".wav"; 						} 						else { 							$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav"; 						} 						if (!file_exists($dest_file)) { 							rename($_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav", $dest_file); 							break; 						} 						$x++; 					} 				} 			} 		} } \ No newline at end of file ?>

Patch:
patch: @@ -81,104 +81,123 @@
 	//setup the event socket connection
 		$fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']);
 
-	if (stristr($action, 'user_status') == true) {
-		$user_status = $data;
-		switch ($user_status) {
-		case "Available":
-			$user_status = "Available";
-			//update the user state
-			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
-			$response = event_socket_request($fp, $cmd);
-			break;
-		case "Available_On_Demand":
-			$user_status = "Available (On Demand)";
-			//update the user state
-			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
-			$response = event_socket_request($fp, $cmd);
-			break;
-		case "Logged_Out":
-			$user_status = "Logged Out";
-			//update the user state
-			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
-			$response = event_socket_request($fp, $cmd);
-			break;
-		case "On_Break":
-			$user_status = "On Break";
-			//update the user state
-			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
-			$response = event_socket_request($fp, $cmd);
-			break;
-		case "Do_Not_Disturb":
-			$user_status = "Do Not Disturb";
-			//update the user state
-			$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
-			$response = event_socket_request($fp, $cmd);
-			break;
-		default:
-			$user_status = "";
+	//get the status
+		if (stristr($action, 'user_status') == true) {
+			$user_status = $data;
+			switch ($user_status) {
+			case "Available":
+				$user_status = "Available";
+				//update the user state
+				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
+				$response = event_socket_request($fp, $cmd);
+				break;
+			case "Available_On_Demand":
+				$user_status = "Available (On Demand)";
+				//update the user state
+				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
+				$response = event_socket_request($fp, $cmd);
+				break;
+			case "Logged_Out":
+				$user_status = "Logged Out";
+				//update the user state
+				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
+				$response = event_socket_request($fp, $cmd);
+				break;
+			case "On_Break":
+				$user_status = "On Break";
+				//update the user state
+				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
+				$response = event_socket_request($fp, $cmd);
+				break;
+			case "Do_Not_Disturb":
+				$user_status = "Do Not Disturb";
+				//update the user state
+				$cmd = "api callcenter_config agent set state ".$username."@".$_SESSION['domain_name']." Waiting";
+				$response = event_socket_request($fp, $cmd);
+				break;
+			default:
+				$user_status = "";
+			}
 		}
-	}
 
-	//fs cmd
-	if (strlen($switch_cmd) > 0) {
+	//allow specific commands
+		if (strlen($switch_cmd) > 0) {
+			if (stristr($switch_cmd, 'originate') == true) {}
+			elseif (stristr($switch_cmd, 'uuid_record') == true) {}
+			elseif (stristr($switch_cmd, 'uuid_transfer') == true) {}
+			elseif (stristr($switch_cmd, 'eavesdrop') == true) {}
+			elseif (stristr($switch_cmd, 'uuid_kill') == true) {}
+			else {
+				$switch_cmd = '';
+			}
+			if (stristr($switch_cmd, 'system') == true) {
+				$switch_cmd = '';
+			}
+		}
 
-		//set the status so they are compatible with mod_callcenter
+	//switch cmd
+		if (strlen($switch_cmd) > 0) {
+
+			//set the status so they are compatible with mod_callcenter
 			$switch_cmd = str_replace("Available_On_Demand", "'Available (On Demand)'", $switch_cmd);
 			$switch_cmd = str_replace("Logged_Out", "'Logged Out'", $switch_cmd);
 			$switch_cmd = str_replace("On_Break", "'On Break'", $switch_cmd);
 			$switch_cmd = str_replace("Do_Not_Disturb", "'Logged Out'", $switch_cmd);
 
-		/*
-		//if ($action == "energy") {
-			//conference 3001-example.org energy 103
-			$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
-			$result_array = explode("=",$switch_result);
-			$tmp_value = $result_array[1];
-			//if ($direction == "up") { $tmp_value = $tmp_value + 100; }
-			//if ($direction == "down") { $tmp_value = $tmp_value - 100; }
-			//echo "energy $tmp_value<br />\n";
-			$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value);
-		//}
-		if ($action == "volume_in") {
-			$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
-			$result_array = explode("=",$switch_result);
-			$tmp_value = $result_array[1];
-			if ($direction == "up") { $tmp_value = $tmp_value + 1; }
-			if ($direction == "down") { $tmp_value = $tmp_value - 1; }
-			//echo "volume $tmp_value<br />\n";
-			$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value);
-		}
-		if ($action == "volume_out") {
+			/*
+			//if ($action == "energy") {
+				//conference 3001-example.org energy 103
+				$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
+				$result_array = explode("=",$switch_result);
+				$tmp_value = $result_array[1];
+				//if ($direction == "up") { $tmp_value = $tmp_value + 100; }
+				//if ($direction == "down") { $tmp_value = $tmp_value - 100; }
+				//echo "energy $tmp_value<br />\n";
+				$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value);
+			//}
+			if ($action == "volume_in") {
+				$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
+				$result_array = explode("=",$switch_result);
+				$tmp_value = $result_array[1];
+				if ($direction == "up") { $tmp_value = $tmp_value + 1; }
+				if ($direction == "down") { $tmp_value = $tmp_value - 1; }
+				//echo "volume $tmp_value<br />\n";
+				$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value);
+			}
+			if ($action == "volume_out") {
+				$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
+				$result_array = explode("=",$switch_result);
+				$tmp_value = $result_array[1];
+				if ($direction == "up") { $tmp_value = $tmp_value + 1; }
+				if ($direction == "down") { $tmp_value = $tmp_value - 1; }
+				//echo "volume $tmp_value<br />\n";
+				$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value);
+			}
+			*/
+
+			//run the command
 			$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
-			$result_array = explode("=",$switch_result);
-			$tmp_value = $result_array[1];
-			if ($direction == "up") { $tmp_value = $tmp_value + 1; }
-			if ($direction == "down") { $tmp_value = $tmp_value - 1; }
-			//echo "volume $tmp_value<br />\n";
-			$switch_result = event_socket_request($fp, 'api '.$switch_cmd.' '.$tmp_value);
-		}
-		*/
-
-		$switch_result = event_socket_request($fp, 'api '.$switch_cmd);
-		if ($action == "record") {
-			if (trim($_GET["action2"]) == "stop") {
-				$x=0;
-				while (true) {
-					if ($x > 0) {
-						$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"]."_".$x.".wav";
-					}
-					else {
-						$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav";
-					}
-					if (!file_exists($dest_file)) {
-						rename($_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav", $dest_file);
-						break;
+
+			//record stop
+			if ($action == "record") {
+				if (trim($_GET["action2"]) == "stop") {
+					$x=0;
+					while (true) {
+						if ($x > 0) {
+							$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"]."_".$x.".wav";
+						}
+						else {
+							$dest_file = $_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav";
+						}
+						if (!file_exists($dest_file)) {
+							rename($_SESSION['switch']['recordings']['dir']."/archive/".date("Y")."/".date("M")."/".date("d")."/".$_GET["uuid"].".wav", $dest_file);
+							break;
+						}
+						$x++;
 					}
-					$x++;
 				}
 			}
 		}
-	}
 }
 
-?>
\ No newline at end of file
+?>
path: app/operator_panel/exec.php