java.spring.security.injection.tainted-file-path.tainted-file-path

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected user input controlling a file path. An attacker could control the location of this file, to include going backwards in the directory with '../'. To address this, ensure that user-controlled variables in file paths are sanitized. You may also consider using a utility method such as org.apache.commons.io.FilenameUtils.getName(...) to only retrieve the file name from the path.

Run Locally

Run in CI

Defintion

rules:
  - id: tainted-file-path
    languages:
      - java
    severity: ERROR
    message: Detected user input controlling a file path. An attacker could control
      the location of this file, to include going backwards in the directory
      with '../'. To address this, ensure that user-controlled variables in file
      paths are sanitized. You may also consider using a utility method such as
      org.apache.commons.io.FilenameUtils.getName(...) to only retrieve the file
      name from the path.
    options:
      interfile: true
    metadata:
      cwe:
        - "CWE-23: Relative Path Traversal"
      owasp:
        - A01:2021 - Broken Access Control
      references:
        - https://owasp.org/www-community/attacks/Path_Traversal
      category: security
      technology:
        - java
        - spring
      subcategory:
        - vuln
      impact: HIGH
      likelihood: MEDIUM
      confidence: HIGH
      interfile: true
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Path Traversal
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $METHODNAME(..., @$REQ(...) $TYPE $SOURCE,...) {
                    ...
                  }
              - pattern-inside: |
                  $METHODNAME(..., @$REQ $TYPE $SOURCE,...) {
                    ...
                  }
          - metavariable-regex:
              metavariable: $TYPE
              regex: ^(?!(Integer|Long|Float|Double|Char|Boolean|int|long|float|double|char|boolean))
          - metavariable-regex:
              metavariable: $REQ
              regex: (RequestBody|PathVariable|RequestParam|RequestHeader|CookieValue|ModelAttribute)
          - focus-metavariable: $SOURCE
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: new File(...)
              - pattern: new java.io.File(...)
              - pattern: new FileReader(...)
              - pattern: new java.io.FileReader(...)
              - pattern: new FileInputStream(...)
              - pattern: new java.io.FileInputStream(...)
              - pattern: (Paths $PATHS).get(...)
              - patterns:
                  - pattern: |
                      $CLASS.$FUNC(...)
                  - metavariable-regex:
                      metavariable: $FUNC
                      regex: ^(getResourceAsStream|getResource)$
              - patterns:
                  - pattern-either:
                      - pattern: new ClassPathResource($FILE, ...)
                      - pattern: ResourceUtils.getFile($FILE, ...)
                      - pattern: new FileOutputStream($FILE, ...)
                      - pattern: new java.io.FileOutputStream($FILE, ...)
                      - pattern: new StreamSource($FILE, ...)
                      - pattern: new javax.xml.transform.StreamSource($FILE, ...)
                      - pattern: FileUtils.openOutputStream($FILE, ...)
                  - focus-metavariable: $FILE
    pattern-sanitizers:
      - pattern: org.apache.commons.io.FilenameUtils.getName(...)

Examples

tainted-file-path.java

package org.sasanlabs.service.vulnerability.fileupload;

import static org.sasanlabs.service.vulnerability.fileupload.UnrestrictedFileUpload.CONTENT_DISPOSITION_STATIC_FILE_LOCATION;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.sasanlabs.internal.utility.FrameworkConstants;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

/**
 * Preflight is the request which is executed to download the uploaded file. This controller is made
 * specifically for content disposition based response. we could have created the similar endpoint
 * in {@code UnrestrictedFileUpload} but as framework appends the "Vulnerability name" hence created
 * a new endpoint.
 *
 * @author KSASAN preetkaran20@gmail.com
 */
@RestController
public class PreflightController {
    private UnrestrictedFileUpload unrestrictedFileUpload;

    public PreflightController(UnrestrictedFileUpload unrestrictedFileUpload) {
        this.unrestrictedFileUpload = unrestrictedFileUpload;
    }

    @RequestMapping(
            CONTENT_DISPOSITION_STATIC_FILE_LOCATION + FrameworkConstants.SLASH + "{fileName}")
    public ResponseEntity<byte[]> fetchFile(@PathVariable("fileName") String fileName)
            throws IOException {
        InputStream inputStream =
                // ruleid: tainted-file-path
                new FileInputStream(
                        unrestrictedFileUpload.getContentDispositionRoot().toFile()
                                + FrameworkConstants.SLASH
                                + fileName);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment");
        return new ResponseEntity<byte[]>(
                IOUtils.toByteArray(inputStream), httpHeaders, HttpStatus.OK);
    }

    public static void bad(@RequestParam String user)
    {
        Socket sock;
        BufferedReader filenameReader = new BufferedReader(
                new InputStreamReader(sock.getInputStream(), "UTF-8"));
        String filename = filenameReader.readLine();
        // ruleid: tainted-file-path
        BufferedReader fileReader = new BufferedReader(new FileReader("/home/" + user + "/" + filename));
        String fileLine = fileReader.readLine();
        while(fileLine != null) {
                sock.getOutputStream().write(fileLine.getBytes());
                fileLine = fileReader.readLine();
        }
    }

    public static void bad2(@RequestParam String filename)
    {
    	ApplicationContext appContext = 
    	   new ClassPathXmlApplicationContext(new String[] {"If-you-have-any.xml"});

        // ruleid: tainted-file-path
    	Resource resource = appContext.getResource("classpath:com/" + filename);
                
        try {
           InputStream is = resource.getInputStream();
           BufferedReader br = new BufferedReader(new InputStreamReader(is));
                
           String line;
           while ((line = br.readLine()) != null) {
              System.out.println(line);
           } 
           br.close();
                
        } catch(IOException e){
           e.printStackTrace();
        }
    }

    public static void ok(@RequestParam String filename)
    {
    	ApplicationContext appContext = 
    	   new ClassPathXmlApplicationContext(new String[] {"If-you-have-any.xml"});

        // ok: tainted-file-path
    	Resource resource = 
           appContext.getResource("classpath:com/" + org.apache.commons.io.FilenameUtils.getName(filename));
                
        try {
           InputStream is = resource.getInputStream();
           BufferedReader br = new BufferedReader(new InputStreamReader(is));
                
           String line;
           while ((line = br.readLine()) != null) {
              System.out.println(line);
           } 
           br.close();
                
        } catch(IOException e){
           e.printStackTrace();
        }
    }

    @Test
    public void whenResourceAsFile_thenReadSuccessful(@RequestParam String filename) throws IOException {
        // ruleid: tainted-file-path
        File resource = new ClassPathResource("data/employees.dat" + filename).getFile();
        String employees = new String(Files.readAllBytes(resource.toPath()));
        assertEquals("Joe Employee,Jan Employee,James T. Employee", employees);
    }
}