CVE-2023-46448: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Reflected Cross-Site Scripting (XSS) vulnerability in dmpop Mejiro Commit Versions Prior To 3096393 allows attackers to run arbitrary code via crafted string in metadata of uploaded images.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2023-11-01T22:15Z
Repository: https://github.com/dmpop/mejiro
Language: PHP
Files changed: index.php
Commit date: 2023-10-15 10:35:50 +0200
Commit message: Fix reflected XSS vulnerability

Diff: diff --git a/index.php b/index.php
index bbc5ed6..71e0edb 100644
--- a/index.php
+++ b/index.php
@@ -95,12 +95,12 @@ set_time_limit(600);
 					// If the longitude is west, make it negative
 					$GPSLatitudeRef	 == 's' ? $lat *= -1 : '';
 					$GPSLongitudeRef == 'w' ? $lon *= -1 : '';
 
 					return array(
-						'lat' => $lat,
-						'lon' => $lon
+						'lat' => htmlentities($lat),
+						'lon' => htmlentities($lon)
 					);
 				}
 			}
 			return false;
 		}
@@ -362,29 +362,29 @@ set_time_limit(600);
 				$description = @file_get_contents($photo_dir . $file_path['filename'] . '.txt');
 			}
 			$gps = read_gps_location($file);
 
 			// Get aperture, exposure, iso, and datetime from EXIF
-			$aperture = (is_null($exif['COMPUTED']['ApertureFNumber']) ? null : $exif['COMPUTED']['ApertureFNumber']);
-			$exposure = (is_null($exif['EXIF']['ExposureTime']) ? null : $exif['EXIF']['ExposureTime']);
+			$aperture = htmlentities((is_null($exif['COMPUTED']['ApertureFNumber']) ? null : $exif['COMPUTED']['ApertureFNumber']));
+			$exposure = htmlentities((is_null($exif['EXIF']['ExposureTime']) ? null : $exif['EXIF']['ExposureTime']));
 			// Normalize exposure
 			// https://stackoverflow.com/questions/3049998/parsing-exifs-exposuretime-using-php
 			if (!is_null($exposure)) {
 				$parts = explode("/", $exposure);
 				if (($parts[1] % $parts[0]) == 0 || $parts[1] == 1000000) {
-					$exposure = ' &bull; 1/' . round($parts[1] / $parts[0], 0);
+					$exposure = htmlentities(' &bull; 1/' . round($parts[1] / $parts[0], 0));
 				} else {
 					if ($parts[1] == 1) {
-						$exposure = ' &bull; ' . $parts[0];
+						$exposure = htmlentities(' &bull; ' . $parts[0]);
 					} else {
-						$exposure = ' &bull; ' . $parts[0] . '/' . $parts[1];
+						$exposure = htmlentities(' &bull; ' . $parts[0] . '/' . $parts[1]);
 					}
 				}
 			}
-			$iso = (is_null($exif['EXIF']['ISOSpeedRatings']) ? null : " &bull; " . $exif['EXIF']['ISOSpeedRatings']);
-			$datetime = $exif['EXIF']['DateTimeOriginal'] ?? null;
-			$comment = $exif['COMMENT']['0'] ?? null;
+			$iso = htmlentities((is_null($exif['EXIF']['ISOSpeedRatings']) ? null : " &bull; " . $exif['EXIF']['ISOSpeedRatings']));
+			$datetime = htmlentities($exif['EXIF']['DateTimeOriginal']) ?? null;
+			$comment = htmlentities($exif['COMMENT']['0']) ?? null;
 
 			// Concatenate $exif_info
 			if (!is_null($aperture) || !is_null($exposure) || !is_null($iso) || !is_null($datetime)) {
 				$exif_info = '<img style="margin-right: .5rem;" src="svg/camera.svg" alt="' . L::img_exif . '" title="' . L::img_exif . '"/>' . $aperture . $exposure . $iso . '<img style="margin-left: .5rem; margin-right: .5rem;" src="svg/calendar.svg" alt="' . L::img_date . '" title="' . L::img_date . '"/>' .  $datetime;
 			}

Vulnerable code: 						'lat' => $lat,
						'lon' => $lon
			$aperture = (is_null($exif['COMPUTED']['ApertureFNumber']) ? null : $exif['COMPUTED']['ApertureFNumber']);
			$exposure = (is_null($exif['EXIF']['ExposureTime']) ? null : $exif['EXIF']['ExposureTime']);
					$exposure = ' &bull; 1/' . round($parts[1] / $parts[0], 0);
						$exposure = ' &bull; ' . $parts[0];
						$exposure = ' &bull; ' . $parts[0] . '/' . $parts[1];
			$iso = (is_null($exif['EXIF']['ISOSpeedRatings']) ? null : " &bull; " . $exif['EXIF']['ISOSpeedRatings']);
			$datetime = $exif['EXIF']['DateTimeOriginal'] ?? null;
			$comment = $exif['COMMENT']['0'] ?? null;
Fixed code: 						'lat' => htmlentities($lat),
						'lon' => htmlentities($lon)
			$aperture = htmlentities((is_null($exif['COMPUTED']['ApertureFNumber']) ? null : $exif['COMPUTED']['ApertureFNumber']));
			$exposure = htmlentities((is_null($exif['EXIF']['ExposureTime']) ? null : $exif['EXIF']['ExposureTime']));
					$exposure = htmlentities(' &bull; 1/' . round($parts[1] / $parts[0], 0));
						$exposure = htmlentities(' &bull; ' . $parts[0]);
						$exposure = htmlentities(' &bull; ' . $parts[0] . '/' . $parts[1]);
			$iso = htmlentities((is_null($exif['EXIF']['ISOSpeedRatings']) ? null : " &bull; " . $exif['EXIF']['ISOSpeedRatings']));
			$datetime = htmlentities($exif['EXIF']['DateTimeOriginal']) ?? null;
			$comment = htmlentities($exif['COMMENT']['0']) ?? null;