Semmle 1.21
Skip to end of metadata
Go to start of metadata

Name: Uncontrolled data used in path expression

Description: Accessing paths influenced by users can allow an attacker to access unexpected resources.

ID: cpp/path-injection

Kind: problem

Severity: warning

Precision: medium

Query: TaintedPath.ql
/**
 * @name Uncontrolled data used in path expression
 * @description Accessing paths influenced by users can allow an
 *              attacker to access unexpected resources.
 * @kind problem
 * @problem.severity warning
 * @precision medium
 * @id cpp/path-injection
 * @tags security
 *       external/cwe/cwe-022
 *       external/cwe/cwe-023
 *       external/cwe/cwe-036
 *       external/cwe/cwe-073
 */

import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking

/**
 * A function for opening a file.
 */
class FileFunction extends FunctionWithWrappers {
  FileFunction() {
    exists(string nme | this.hasGlobalName(nme) |
      nme = "fopen" or
      nme = "_fopen" or
      nme = "_wfopen" or
      nme = "open" or
      nme = "_open" or
      nme = "_wopen" or

      // create file function on windows
      nme.matches("CreateFile%")
    )
    or
    (
      // on any of the fstream classes, or filebuf
      exists(string nme | this.getDeclaringType().getSimpleName() = nme |
        nme = "basic_fstream" or
        nme = "basic_ifstream" or
        nme = "basic_ofstream" or
        nme = "basic_filebuf"
      )
      and
      // we look for either the open method or the constructor
      (this.getName() = "open" or this instanceof Constructor)
    )
  }

  // conveniently, all of these functions take the path as the first parameter!
  override predicate interestingArg(int arg) {
    arg = 0
  }
}

from FileFunction fileFunction,
     Expr taintedArg, Expr taintSource, string taintCause, string callChain
where
  fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
  tainted(taintSource, taintedArg) and
  isUserInput(taintSource, taintCause)
select
  taintedArg,
  "This argument to a file access function is derived from $@ and then passed to " + callChain,
  taintSource, "user input (" + taintCause + ")"

Accessing paths controlled by users can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

Paths that are naively constructed from data controlled by a user may contain unexpected special characters, such as "..". Such a path may potentially point to any directory on the filesystem.

Recommendation

Validate user input before using it to construct a filepath. Ideally, follow these rules:

  • Do not allow more than a single "." character.
  • Do not allow directory separators such as "/" or "\" (depending on the filesystem).
  • Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to ".../...//" the resulting string would still be "../".
  • Ideally use a whitelist of known good patterns.
Example

In this example, a username and file are read from the arguments to main and then used to access a file in the user's home directory. However, a malicious user could enter a filename which contains special characters. For example, the string "../../etc/passwd" will result in the code reading the file located at "/home/[user]/../../etc/passwd", which is the system's password file. This could potentially allow them to access all the system's passwords.

int main(int argc, char** argv) {
  char *userAndFile = argv[2];
  
  {
    char fileBuffer[FILENAME_MAX] = "/home/";
    char *fileName = fileBuffer;
    size_t len = strlen(fileName);
    strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);
    // BAD: a string from the user is used in a filename
    fopen(fileName, "wb+");
  }

  {
    char fileBuffer[FILENAME_MAX] = "/home/";
    char *fileName = fileBuffer;
    size_t len = strlen(fileName);
    // GOOD: use a fixed file
    char* fixed = "jim/file.txt";
    strncat(fileName+len, fixed, FILENAME_MAX-len-1);
    fopen(fileName, "wb+");
  }
}

References