CVE-2024-25641 (published 2024-05-14T15:05Z)

Cacti provides an operational monitoring and fault management framework. Prior to version 1.2.27, an arbitrary file write vulnerability, exploitable through the "Package Import" feature, allows authenticated users having the "Import Templates" permission to execute arbitrary PHP code on the web server. The vulnerability is located within the `import_package()` function defined into the `/lib/import.php` script. The function blindly trusts the filename and file content provided within the XML data, and writes such files into the Cacti base path (or even outside, since path traversal sequences are not filtered). This can be exploited to write or overwrite arbitrary files on the web server, leading to execution of arbitrary PHP code or other security impacts. Version 1.2.27 contains a patch for this issue.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/Cacti/cacti
commit_details: repo_url: https://github.com/Cacti/cacti
author: Petr Macek
author_date: 2024-04-07 16:25:58+02:00
msg: Merge pull request from GHSA-7cmj-g5qc-pj88
repository: repo_url: https://github.com/Cacti/cacti
repo_name: Cacti/cacti
description: Cacti ™
repo_language: PHP
owner: Cacti
file_changes: filename: import.php
change_type: ModificationType.MODIFY
diff: @@ -523,14 +523,22 @@ function import_package($xmlfile, $profile_id = 1, $remove_orphans = false, $rep
 		$fdata = base64_decode($f['data'], true);
 		$name  = $f['name'];
 
+		/* The xml file without path is the main template of the package. It is processed below using $debug_data */
 		if (strpos($name, 'scripts/') !== false || strpos($name, 'resource/') !== false) {
+
 			$filename = CACTI_PATH_BASE . "/$name";
 
 			if (!$preview) {
 				if (!cacti_sizeof($import_files) || in_array($name, $import_files, true)) {
-					cacti_log('Writing file: ' . $filename, false, 'IMPORT', POLLER_VERBOSITY_MEDIUM);
+					cacti_log('Checking filepath: ' . $filename, false, 'IMPORT', POLLER_VERBOSITY_MEDIUM);
+
+					if (!preg_match('/^(scripts|resource)[a-zA-Z0-9_\-\/]*$/', dirname($name))) {
+						cacti_log('FATAL: Incorrect path: ' . $filename, true, 'IMPORT', POLLER_VERBOSITY_LOW);
+
+						$filestatus[$filename] = __('incorrect path, file not saved');
+					} elseif ((is_writeable(dirname($filename)) && !file_exists($filename)) || is_writable($filename)) {
+						cacti_log('Writing file: ' . $filename, false, 'IMPORT', POLLER_VERBOSITY_MEDIUM);
 
-					if ((is_writeable(dirname($filename)) && !file_exists($filename)) || is_writable($filename)) {
 						$file = fopen($filename, 'wb');
 
 						if (is_resource($file)) {
@@ -562,7 +570,9 @@ function import_package($xmlfile, $profile_id = 1, $remove_orphans = false, $rep
 					$existing = md5_file($filename);
 				}
 
-				if (is_writeable(dirname($filename))) {
+				if (!preg_match('/^(scripts|resource)[a-zA-Z0-9_\-\/]*$/', dirname($name))) {
+					$filestatus[$filename] = 'incorrect path';
+				} elseif (is_writeable(dirname($filename))) {
 					if (file_exists($filename) && is_writable($filename)) {
 						if ($new == $existing) {
 							$filestatus[$filename] = 'writable, identical';
@@ -589,6 +599,7 @@ function import_package($xmlfile, $profile_id = 1, $remove_orphans = false, $rep
 				} else {
 					$filestatus[$filename] = 'not writable, new';
 				}
+
 			}
 		} else {
 			if (!$preview) {

programming_language: PHP
filename: package_import.php
change_type: ModificationType.MODIFY
diff: @@ -558,6 +558,8 @@ function import_display_package_data($templates, $files, $package_name, $xmlfile
 							$nstatus .= ($nstatus != '' ? ', ':'') . __('Writable');
 						} elseif ($s == 'new') {
 							$nstatus .= ($nstatus != '' ? ', ':'') . __('New');
+						} elseif ($s == 'incorrect path') {
+							$nstatus .= ($nstatus != '' ? ', ':'') . __('Incorrect path, will not be imported');
 						} else {
 							$nstatus .= ($nstatus != '' ? ', ':'') . __('Unknown');
 						}

programming_language: PHP