CVE-2021-41152 (published 2021-10-18T21:15Z)

OpenOlat is a web-based e-learning platform for teaching, learning, assessment and communication, an LMS, a learning management system. In affected versions by manipulating the HTTP request an attacker can modify the path of a requested file download in the folder component to point to anywhere on the target system. The attack could be used to read any file accessible 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 or the enabled guest user feature together with the usage of the folder component in a course. The attack does not allow writing of arbitrary files, it allows only reading of files and also only ready of files that the attacker knows the exact path which is very unlikely at least for OpenOlat data files. The problem is fixed in version 15.5.8 and 16.0.1 It is advised to upgrade to version 16.0.x. There are no known workarounds to fix this problem, an upgrade is necessary.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:P/I:N/A:N)
CVSS v3 base score: 7.7, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/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/OpenOLAT/OpenOLAT
commit_details: repo_url: https://github.com/OpenOLAT/OpenOLAT
author: srosse
author_date: 2021-09-06 19:02:57+02:00
msg: OO-5696: validate file selections against current container
repository: repo_url: https://github.com/OpenOLAT/OpenOLAT
repo_name: OpenOLAT/OpenOLAT
description: Learning Management System OpenOlat
repo_language: Java
owner: OpenOLAT
file_changes: filename: FileSelection.java
change_type: ModificationType.MODIFY
diff: @@ -27,24 +27,29 @@
 package org.olat.core.commons.modules.bc;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.util.FileUtils;
 import org.olat.core.util.StringHelper;
+import org.olat.core.util.vfs.VFSContainer;
+import org.olat.core.util.vfs.VFSItem;
 
 public class FileSelection {
 
 	/** HTML form identifier */
 	public static final String FORM_ID = "paths";
 	
-	private List<String> files = new ArrayList<>();
-	private String currentContainerRelPath;
+	private final List<String> files = new ArrayList<>();
+	private final String currentContainerRelPath;
+	private final VFSContainer currentContainer;
 	
-	public FileSelection(UserRequest ureq, String currentContainerRelPath) {
+	public FileSelection(UserRequest ureq, VFSContainer currentContainer, String currentContainerRelPath) {
 		if (currentContainerRelPath.equals("/")) currentContainerRelPath = "";
 		this.currentContainerRelPath = currentContainerRelPath;
+		this.currentContainer = currentContainer;
 		parse(ureq);
 	}
 
@@ -76,8 +81,20 @@ public class FileSelection {
 	 */
 	private void parse(UserRequest ureq) {
 		String[] sFiles = ureq.getHttpReq().getParameterValues(FORM_ID);
-		if (sFiles == null || sFiles.length == 0) return;
-		files = Arrays.asList(sFiles);
+		if (sFiles == null || sFiles.length == 0) {
+			return;
+		}
+		List<VFSItem> items = currentContainer.getItems();
+		if(items != null && !items.isEmpty()) {
+			Set<String> itemNames =  items.stream()
+					.map(VFSItem::getName)
+					.collect(Collectors.toSet());
+			for(String sFile:sFiles) {
+				if(itemNames.contains(sFile)) {
+					files.add(sFile);
+				}
+			}
+		}
 	}
 
 	/**

programming_language: Java
filename: CmdDelete.java
change_type: ModificationType.MODIFY
diff: @@ -74,7 +74,7 @@ public class CmdDelete extends BasicController implements FolderCommand {
 	public Controller execute(FolderComponent fc, UserRequest ureq, WindowControl wContr, Translator trans) {
 		this.translator = trans;
 		this.folderComponent = fc;
-		this.fileSelection = new FileSelection(ureq, fc.getCurrentContainerPath());
+		this.fileSelection = new FileSelection(ureq, fc.getCurrentContainer(), fc.getCurrentContainerPath());
 
 		VFSContainer currentContainer = folderComponent.getCurrentContainer();
 		List<String> lockedFiles = hasLockedFiles(currentContainer, fileSelection);

programming_language: Java
filename: CmdDownloadZip.java
change_type: ModificationType.MODIFY
diff: @@ -70,7 +70,7 @@ public class CmdDownloadZip implements FolderCommand {
 			return null;
 		}
 	
-		FileSelection selection = new FileSelection(ureq, folderComponent.getCurrentContainerPath());
+		FileSelection selection = new FileSelection(ureq, folderComponent.getCurrentContainer(), folderComponent.getCurrentContainerPath());
 		status = FolderCommandHelper.sanityCheck3(wControl, folderComponent, selection);
 		if(status == FolderCommandStatus.STATUS_FAILED) {
 			return null;
@@ -78,7 +78,7 @@ public class CmdDownloadZip implements FolderCommand {
 		
 		if(selection.getFiles().isEmpty()) {
 			status = FolderCommandStatus.STATUS_FAILED;
-			wControl.setWarning(trans.translate("warning.file.selection.empty"));
+			wControl.setWarning(trans.translate("warning.file.selection.empty22"));
 			return null;
 		}
 		

programming_language: Java
filename: CmdMoveCopy.java
change_type: ModificationType.MODIFY
diff: @@ -89,7 +89,7 @@ public class CmdMoveCopy extends DefaultController implements FolderCommand {
 	public Controller execute(FolderComponent fc, UserRequest ureq, WindowControl windowControl, Translator trans) {
 		this.folderComponent = fc;
 		this.translator = trans;
-		this.fileSelection = new FileSelection(ureq, fc.getCurrentContainerPath());
+		this.fileSelection = new FileSelection(ureq, fc.getCurrentContainer(), fc.getCurrentContainerPath());
 				
 		VelocityContainer main = new VelocityContainer("mc", VELOCITY_ROOT + "/movecopy.html", translator, this);
 		main.contextPut("fileselection", fileSelection);

programming_language: Java
filename: CmdUnzip.java
change_type: ModificationType.MODIFY
diff: @@ -77,7 +77,7 @@ public class CmdUnzip extends BasicController implements FolderCommand {
 	@Override
 	public Controller execute(FolderComponent folderComponent, UserRequest ureq, WindowControl wContr, Translator trans) {
 		this.translator = trans;
-		FileSelection selection = new FileSelection(ureq, folderComponent.getCurrentContainerPath());
+		FileSelection selection = new FileSelection(ureq, folderComponent.getCurrentContainer(), folderComponent.getCurrentContainerPath());
 		VFSContainer currentContainer = folderComponent.getCurrentContainer();
 		if (currentContainer.canWrite() != VFSConstants.YES)
 			throw new AssertException("Cannot unzip to folder. Writing denied.");

programming_language: Java
filename: CmdZip.java
change_type: ModificationType.MODIFY
diff: @@ -89,7 +89,7 @@ public class CmdZip extends FormBasicController implements FolderCommand {
 			return null;
 		}
 	
-		selection = new FileSelection(ureq, folderComponent.getCurrentContainerPath());
+		selection = new FileSelection(ureq, folderComponent.getCurrentContainer(), folderComponent.getCurrentContainerPath());
 		status = FolderCommandHelper.sanityCheck3(wControl, folderComponent, selection);
 		if(status == FolderCommandStatus.STATUS_FAILED) {
 			return null;

programming_language: Java
filename: SendDocumentsByEMailController.java
change_type: ModificationType.MODIFY
diff: @@ -189,7 +189,7 @@ public class SendDocumentsByEMailController extends FormBasicController implemen
 		if (status == FolderCommandStatus.STATUS_FAILED) {
 			return null;
 		}
-		FileSelection selection = new FileSelection(ureq, folderComponent.getCurrentContainerPath());
+		FileSelection selection = new FileSelection(ureq, folderComponent.getCurrentContainer(), folderComponent.getCurrentContainerPath());
 		status = FolderCommandHelper.sanityCheck3(wControl, folderComponent, selection);
 		if (status == FolderCommandStatus.STATUS_FAILED) {
 			return null;

programming_language: Java