CVE-2022-23612 (published 2022-02-22T23:15Z)

OpenMRS is a patient-based medical record system focusing on giving providers a free customizable electronic medical record system. Affected versions are subject to arbitrary file exfiltration due to failure to sanitize request when satisfying GET requests for `/images` & `/initfilter/scripts`. This can allow an attacker to access any file on a system running OpenMRS that is accessible to the user id OpenMRS is running under. Affected implementations should update to the latest patch version of OpenMRS Core for the minor version they use. These are: 2.1.5, 2.2.1, 2.3.5, 2.4.5 and 2.5.3. As a general rule, this vulnerability is already mitigated by Tomcat's URL normalization in Tomcat 7.0.28+. Users on older versions of Tomcat should consider upgrading their Tomcat instance as well as their OpenMRS instance.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/openmrs/openmrs-core
commit_details: repo_url: https://github.com/openmrs/openmrs-core
author: Ian
author_date: 2022-01-28 15:58:59-05:00
msg: Fix bug
repository: repo_url: https://github.com/openmrs/openmrs-core
repo_name: openmrs/openmrs-core
description: OpenMRS API and web application code
repo_language: Java
owner: openmrs
file_changes: filename: StartupFilter.java
change_type: ModificationType.MODIFY
diff: @@ -18,6 +18,8 @@ import java.io.InputStreamReader;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -116,30 +118,27 @@ public abstract class StartupFilter implements Filter {
 			// (the "/initfilter" part is needed so that the openmrs_static_context-servlet.xml file doesn't
 			//  get instantiated early, before the locale messages are all set up)
 			if (servletPath.startsWith("/images") || servletPath.startsWith("/initfilter/scripts")) {
-				servletPath = servletPath.replaceFirst("/initfilter", "/WEB-INF/view"); // strip out the /initfilter part
+				// strip out the /initfilter part
+				servletPath = servletPath.replaceFirst("/initfilter", "/WEB-INF/view");
 				// writes the actual image file path to the response
-				File file = new File(filterConfig.getServletContext().getRealPath(servletPath));
+				Path filePath = Paths.get(filterConfig.getServletContext().getRealPath(servletPath)).normalize();
+				Path fullFilePath = filePath;
 				if (httpRequest.getPathInfo() != null) {
-					file = new File(file, httpRequest.getPathInfo());
+					fullFilePath = fullFilePath.resolve(httpRequest.getPathInfo());
+					if (!(fullFilePath.normalize().startsWith(filePath))) {
+						log.warn("Detected attempted directory traversal in request for {}", httpRequest.getPathInfo());
+						return;
+					}
 				}
 				
-				InputStream imageFileInputStream = null;
-				try {
-					imageFileInputStream = new FileInputStream(file);
+				try (InputStream imageFileInputStream = new FileInputStream(fullFilePath.normalize().toFile())) {
 					OpenmrsUtil.copyFile(imageFileInputStream, httpResponse.getOutputStream());
 				}
 				catch (FileNotFoundException e) {
-					log.error("Unable to find file: " + file.getAbsolutePath());
+					log.error("Unable to find file: {}", filePath, e);
 				}
-				finally {
-					if (imageFileInputStream != null) {
-						try {
-							imageFileInputStream.close();
-						}
-						catch (IOException io) {
-							log.warn("Couldn't close imageFileInputStream: " + io);
-						}
-					}
+				catch (IOException e) {
+					log.warn("An error occurred while handling file {}", filePath, e);
 				}
 			} else if (servletPath.startsWith("/scripts")) {
 				log.error(
@@ -193,7 +192,7 @@ public abstract class StartupFilter implements Filter {
 				velocityEngine.init(props);
 			}
 			catch (Exception e) {
-				log.error("velocity init failed, because: " + e);
+				log.error("velocity init failed, because: {}", e, e);
 			}
 		}
 	}

programming_language: Java