CVE-2022-28055 (PHP, published 2022-05-04T03:15Z)
Description: value: Fusionpbx v4.4 and below contains a command injection vulnerability via the download email logs function.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-78 \u2014 Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
Repository: https://github.com/fusionpbx/fusionpbx
Files changed:
app/email_logs/email_logs.php
app/email_logs/resources/classes/email_logs.php
Commit message: Remove email_logs download. (#6331)

* Remove email_logs download.

This feature has a security risk that is being eliminated by removing the download feature.

* Update email_logs.php

Vulnerable code:
			case 'download':
				if (permission_exists('email_log_download')) {
					$obj = new email_logs;
					$obj->download($emails);
					message::add($text['message-download_failed'],'negative',7000); //download failed, set message
				}
				break;
	if (permission_exists('email_log_download') && $result) {
		echo button::create(['type'=>'button','label'=>$text['button-download'],'icon'=>$_SESSION['theme']['button_icon_download'],'onclick'=>"list_action_set('download'); list_form_submit('form_list');"]);
	}
	if (permission_exists('email_log_download') || permission_exists('email_log_resend') || permission_exists('email_log_delete')) {
			if (permission_exists('email_log_download') || permission_exists('email_log_resend') || permission_exists('email_log_delete')) {
			if (permission_exists('email_log_download')) {
				echo button::create(['type'=>'button','title'=>$text['button-download'],'icon'=>$_SESSION['theme']['button_icon_download'],'onclick'=>"list_self_check('checkbox_".$x."'); list_action_set('download'); list_form_submit('form_list')"]);
			}
		/**
		 * download records
		 */
		public function download($records) {
			if (permission_exists($this->permission_prefix.'download')) {

				//add multi-lingual support
					$language = new text;
					$text = $language->get();

				//validate the token
					$token = new token;
					if (!$token->validate('/app/email_logs/email_logs.php')) {
						message::add($text['message-invalid_token'],'negative');
						header('Location: '.$this->list_page);
						exit;
					}

				//download multiple records (eventually zip individual emails together)
					if (is_array($records) && @sizeof($records) != 0) {

						//retrieve checked records
							foreach($records as $x => $record) {
								if ($record['checked'] == 'true' && is_uuid($record['uuid'])) {
									$uuids[] = $record['uuid'];
								}
							}

						//download emails
							if (is_array($uuids) && @sizeof($uuids) != 0) {
								foreach ($uuids as $x => $uuid) {

									//get email details
										$sql = "select call_uuid, sent_date, type, email from v_email_logs ";
										$sql .= "where email_log_uuid = :email_log_uuid ";
										$parameters['email_log_uuid'] = $uuid;
										$database = new database;
										$row = $database->select($sql, $parameters, 'row');
										if (is_array($row) && @sizeof($row) != 0 && is_uuid($row['call_uuid'])) {

											//santize filename components
												$sent_date = str_replace('-','', $row['sent_date']);
												$sent_date = str_replace(':','', $sent_date);
												$sent_date = str_replace(' ','_', $sent_date);
												$type = strtolower($row['type']);
												$email_filename = $sent_date.'_'.$type.'_'.$row['call_uuid'].'.eml';

											//single email
												if (@sizeof($uuids) == 1) {

													//set headers
														header("Content-Type: message/rfc822");
														header('Content-Disposition: attachment; filename="'.$email_filename.'"');
														header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
														header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
														header("Content-Length: ".strlen($row['email']));

													//output content
														echo $row['email'];
														exit;
												}

											//multiple emails
												else {
													if (is_dir($_SESSION['server']['temp']['dir'])) {

														if (file_put_contents($_SESSION['server']['temp']['dir'].'/'.$email_filename, $row['email'])) {
															$email_files[] = $_SESSION['server']['temp']['dir'].'/'.$email_filename;
														}
													}
												}

										}
										unset($sql, $parameters, $row);
								}

								//download compressed file
								if (@sizeof($email_files) != 0) {

									//define compressed file name
										$compressed_filename = 'emails_'.date('Ymd_His').'.zip';

									//compress email files
										$command = 'zip -mj '.$_SESSION['server']['temp']['dir'].'/'.$compressed_filename.' '.implode(' ', $email_files).' 2>&1';
										exec($command, $response, $restore_errlevel);
										unset($command);

									//push download
										if (file_exists($_SESSION['server']['temp']['dir'].'/'.$compressed_filename)) {

											//open file
												session_cache_limiter('public');
												$fd = fopen($_SESSION['server']['temp']['dir'].'/'.$compressed_filename, 'rb');

											//set headers
												header("Content-Type: application/zip");
												header('Content-Disposition: attachment; filename="'.$compressed_filename.'"');
												header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
												header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
												header("Content-Length: ".filesize($_SESSION['server']['temp']['dir'].'/'.$compressed_filename));

											//output file content
												ob_clean();
												fpassthru($fd);
												fclose($fd);

											//remove compressed file
												@unlink($_SESSION['server']['temp']['dir'].'/'.$compressed_filename);
												exit;

										}

								}

							}

					}

			}

		} //method

?>

Fixed code:
	if (permission_exists('email_log_resend') || permission_exists('email_log_delete')) {
			if (permission_exists('email_log_resend') || permission_exists('email_log_delete')) {
?>