CVE-2023-0316: Path Traversal: '\..\filename' (CWE-29)
value: Path Traversal: '\..\filename' in GitHub repository froxlor/froxlor prior to 2.0.0.
CWE description: The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize '\..\filename' (leading backslash dot dot) sequences that can resolve to a location that is outside of that directory.
CVSS v3 base score: 5.5
Published: 2023-01-16T01:15Z
Repository: https://github.com/froxlor/froxlor
Language: PHP
Files changed: lib/Froxlor/SImExporter.php
Commit date: 2022-12-30 14:39:32 +0100
Commit message: validate image data/filename when importing

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>

Diff: diff --git a/lib/Froxlor/SImExporter.php b/lib/Froxlor/SImExporter.php
index 3ee5b00c5..1e8df164d 100644
--- a/lib/Froxlor/SImExporter.php
+++ b/lib/Froxlor/SImExporter.php
@@ -155,11 +155,43 @@ class SImExporter
 						if (!chmod($path, 0775)) {
 							throw new Exception("Cannot write to img directory");
 						}
 					}
 
-					file_put_contents(Froxlor::getInstallDir() . '/' . explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0], base64_decode($value));
+					$img_data = base64_decode($value);
+					$img_filename = Froxlor::getInstallDir() . '/' . str_replace('../', '', explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0]);
+
+					file_put_contents($img_filename, $img_data);
+
+					if (function_exists('finfo_open')) {
+						$finfo = finfo_open(FILEINFO_MIME_TYPE);
+						$mimetype = finfo_file($finfo, $img_filename);
+						finfo_close($finfo);
+					} else {
+						$mimetype = mime_content_type($img_filename);
+					}
+					if (empty($mimetype)) {
+						$mimetype = 'application/octet-stream';
+					}
+					if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
+						@unlink($img_filename);
+						throw new Exception("Uploaded file is not a valid image");
+					}
+
+					$spl = explode('.', $img_filename);
+					$file_extension = strtolower(array_pop($spl));
+					unset($spl);
+
+					if (!in_array($file_extension, [
+						'jpeg',
+						'jpg',
+						'png',
+						'gif'
+					])) {
+						@unlink($img_filename);
+						throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
+					}
 					continue;
 				}
 
 				Settings::Set($index, $value);
 			}

Vulnerable code: 					file_put_contents(Froxlor::getInstallDir() . '/' . explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0], base64_decode($value));
Fixed code: 					$img_data = base64_decode($value);
					$img_filename = Froxlor::getInstallDir() . '/' . str_replace('../', '', explode('?', $_data[$index_split[0] . '.' . $index_split[1]], 2)[0]);

					file_put_contents($img_filename, $img_data);

					if (function_exists('finfo_open')) {
						$finfo = finfo_open(FILEINFO_MIME_TYPE);
						$mimetype = finfo_file($finfo, $img_filename);
						finfo_close($finfo);
					} else {
						$mimetype = mime_content_type($img_filename);
					}
					if (empty($mimetype)) {
						$mimetype = 'application/octet-stream';
					}
					if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
						@unlink($img_filename);
						throw new Exception("Uploaded file is not a valid image");
					}

					$spl = explode('.', $img_filename);
					$file_extension = strtolower(array_pop($spl));
					unset($spl);

					if (!in_array($file_extension, [
						'jpeg',
						'jpg',
						'png',
						'gif'
					])) {
						@unlink($img_filename);
						throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
					}