CVE-2022-24830 (Java, published 2022-05-14T00:15Z)
Description: value: OpenClinica is an open source software for Electronic Data Capture (EDC) and Clinical Data Management (CDM). OpenClinica prior to version 3.16 is vulnerable to path traversal in multiple endpoints, leading to arbitrary file read/write, and potential remote code execution. There are no known workarounds. This issue has been patched and users are recommended to upgrade.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-22 \u2014 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
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.
Repository: https://github.com/OpenClinica/OpenClinica
Files changed:
web/src/main/java/org/akaza/openclinica/control/admin/DownloadVersionSpreadSheetServlet.java
web/src/main/java/org/akaza/openclinica/control/submit/DownloadAttachedFileServlet.java
web/src/main/java/org/akaza/openclinica/controller/BatchCRFMigrationController.java
web/src/main/java/org/akaza/openclinica/controller/openrosa/OpenRosaSubmissionController.java
Commit message: OC-17139: code  changes after code review


Vulnerable code:
       
        	if (oldExcelFile.exists() && oldExcelFile.length() > 0) {
                 if (!excelFile.exists() || excelFile.length() <= 0) {
                     // if the old name exists and the new name does not...
                     excelFile = oldExcelFile;
                     excelFileName = oldExcelFileName;
                 }
             }                  
        
        if(fileName != null && fileName.indexOf("..") > -1) {
        	throw new RuntimeException("Traversal attempt - absolute path not allowed " + fileName);
        }
        
            int parentStudyId = currentStudy.getParentStudyId();
            File temp = new File(testName);
            String logFileName = getFilePath() + File.separator + FilenameUtils.getName(fileName);
            File fileToDownload = new File(logFileName);
            inputStream = new FileInputStream(fileToDownload);
            response.setContentType("application/force-download");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            IOUtils.copy(inputStream, response.getOutputStream());
            response.flushBuffer();
import org.apache.commons.io.FilenameUtils;
    public static String getAttachedFilePath(String inputStudyOid) {
    	// Using a standard library to validate/Sanitize user inputs which will be used in path expression to prevent from path traversal
    	String studyOid =  FilenameUtils.getName(inputStudyOid);
            attachedFilePath = CoreResources.getField("filePath") + "attached_files" + File.separator + studyOid + File.separator;
        } else {
            attachedFilePath += studyOid + File.separator;
        return attachedFilePath;

Fixed code:
            
            String canonicalPath1= excelFile.getCanonicalPath();
            String canonicalPath2= oldExcelFile.getCanonicalPath();
            if (canonicalPath1.startsWith(dir) && canonicalPath2.startsWith(dir)) {
            	if (oldExcelFile.exists() && oldExcelFile.length() > 0) {
                    if (!excelFile.exists() || excelFile.length() <= 0) {
                        // if the old name exists and the new name does not...
                        excelFile = oldExcelFile;
                        excelFileName = oldExcelFileName;
                    }
                }     
            }else {
            	 addPageMessage(respage.getString("the_excel_is_not_available_on_server_contact"));
                 forwardPage(Page.CRF_LIST_SERVLET);
            }
        	             
              
            int parentStudyId = currentStudy.getParentStudyId();           
            
            String filePath = testPath + currentStudy.getOid() +File.separator;            
            File temp = new File(filePath,f.getName());            
            String canonicalPath= temp.getCanonicalPath();
            
            if (canonicalPath.startsWith(filePath)) {
            	;
            }else {
            	throw new RuntimeException("Traversal attempt - file path not allowed " + fileName);
            }
            
            String logFilePath = getFilePath() + File.separator;
            File fileToDownload = new File(logFilePath, fileName);                    
            String canonicalPath= fileToDownload.getCanonicalPath();
            
            if (canonicalPath.startsWith(logFilePath)) {
            	inputStream = new FileInputStream(fileToDownload);
                response.setContentType("application/force-download");
                response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
                IOUtils.copy(inputStream, response.getOutputStream());
                response.flushBuffer();
            }else {
            	throw new RuntimeException("Traversal attempt - file path not allowed " + fileName);
            }
            
    public static String getAttachedFilePath(String studyOid) throws Exception {    	
            attachedFilePath = CoreResources.getField("filePath") + "attached_files" + File.separator;
        }
        File tempFile =  new File(attachedFilePath,studyOid);
        String canonicalPath= tempFile.getCanonicalPath();
        
        if (canonicalPath.startsWith(attachedFilePath)) {
        	if (attachedFilePath == null || attachedFilePath.length() <= 0) {
                attachedFilePath = CoreResources.getField("filePath") + "attached_files" + File.separator + studyOid + File.separator;
            } else {
                attachedFilePath += studyOid + File.separator;
            }
            return attachedFilePath;
        }else {
        	throw new RuntimeException("Traversal attempt - file path not allowed " + studyOid);