CVE-2021-41242: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: OpenOlat is a web-basedlearning management system. A path traversal vulnerability exists in OpenOlat prior to versions 15.5.12 and 16.0.5. By providing a filename that contains a relative path as a parameter in some REST methods, it is possible to create directory structures and write files anywhere on the target system. The attack could be used to write files anywhere in the web root folder or outside, depending on the configuration of the system and the properly configured permission of the application server user. The attack requires an OpenOlat user account, an enabled REST API and the rights on a business object to call the vulnerable REST calls. The problem is fixed in version 15.5.12 and 16.0.5. There is a workaround available. The vulnerability requires the REST module to be enabled. Disabling the REST module or limiting the REST module via some firewall or web-server access rules to be accessed only be trusted systems will mitigate the risk.
CWE 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.
CVSS v3 base score: 8.1
Published: 2021-12-10T23:15Z
Repository: https://github.com/OpenOLAT/OpenOLAT
Language: Java
Files changed: src/main/java/org/olat/core/util/vfs/LocalFolderImpl.java
Commit date: 2021-11-02 10:04:49 +0100
Commit message: OO-5819: clean starting / for backward compatibility
Diff: diff --git a/src/main/java/org/olat/core/util/vfs/LocalFolderImpl.java b/src/main/java/org/olat/core/util/vfs/LocalFolderImpl.java
index 11e5869cf10..dee64b8f158 100644
--- a/src/main/java/org/olat/core/util/vfs/LocalFolderImpl.java
+++ b/src/main/java/org/olat/core/util/vfs/LocalFolderImpl.java
@@ -371,10 +371,11 @@ public class LocalFolderImpl extends LocalImpl implements VFSContainer {
 		return "LFolder [base="+getBasefile()+"] ";
 	}
 
 	@Override
 	public VFSContainer createChildContainer(String name) {
+		name = cleanFilename(name); // backward compatibility
 		File fNewFile = new File(getBasefile(), name);
 		if(!isInPath(name)) {
 			log.warn("Could not create a new container::{} in container::{} - file out of parent directory", name, getBasefile().getAbsolutePath());
 			return null;
 		}
@@ -386,10 +387,11 @@ public class LocalFolderImpl extends LocalImpl implements VFSContainer {
 		return locFI;
 	}
 
 	@Override
 	public VFSLeaf createChildLeaf(String name) {
+		name = cleanFilename(name); // backward compatibility
 		File fNewFile = new File(getBasefile(), name);
 		try {
 			if(!isInPath(name)) {
 				log.warn("Could not create a new leaf::{} in container::{} - file out of parent directory", name, getBasefile().getAbsolutePath());
 				return null;
@@ -405,10 +407,24 @@ public class LocalFolderImpl extends LocalImpl implements VFSContainer {
 			log.error("Error while creating child leaf::{} in container::{}", name, getBasefile().getAbsolutePath(), e);
 			return null;
 		}
 		return new LocalFileImpl(fNewFile, this);
 	}
+	
+	/**
+	 * It was allowed to have a filename starting with /, but
+	 * it's not legal.
+	 * 
+	 * @param name The name
+	 * @return Name without starting /
+	 */
+	private String cleanFilename(String name) {
+		if(name != null && name.startsWith("/")) {
+			name = name.substring(1, name.length());
+		}
+		return name;
+	}
 
 	@Override
 	public VFSItemFilter getDefaultItemFilter() {
 		return defaultFilter;
 	}

Fixed code: 		name = cleanFilename(name); // backward compatibility
		name = cleanFilename(name); // backward compatibility
	
	/**
	 * It was allowed to have a filename starting with /, but
	 * it's not legal.
	 * 
	 * @param name The name
	 * @return Name without starting /
	 */
	private String cleanFilename(String name) {
		if(name != null && name.startsWith("/")) {
			name = name.substring(1, name.length());
		}
		return name;
	}