Skip to main content

CWE 73: EXTERNAL CONTROL OF FILE NAME OR PATH

Flaw

CWE 73: External Control of File Name or Path is a type of security flaw in which users can access resources from restricted locations on a file system. It is commonly called Path Traversal. If an attackers perform a Path Traversal attack successfully, they could potentially view sensitive files or other confidential information. This threat can arise when applications allow a user to specify the filename used in a file system operation.

For example:

<h1>Path Traversal Lab</h1>
    <h4>Welcome to our free library. Please select any book from the shelf. Take care to not overreach...</h4>
        <form>
            <select name="bookChoice">
                <option value="Anna Karenina.epub">Anna Karenina (Leo Tolstoy)</option>
                <option value="Leaves of Grass.epub">Leaves of Grass (Walt Whitman)</option>
                <option value="Pygmalion.epub">Pygmalion (Bernard Shaw)</option>
                <option value="Siddhartha.epub">Siddhartha (Hermann Hesse)</option>
                <option value="The Picture of Dorian Gray.epub">The Picture of Dorian Gray (Oscar Wilde)</option>
            </select>

            <button type="button" formAction="/getSearchResults">Download Book</button>
        </form>

The above form is handled by the following Java code, which sends the selected file to the browser:

public ResponseEntity getSearchResults(String bookChoice) throws IOException {
    String partialPath = "X:\\app\\src\\main\\resources\\books\\";
    String fullPath = partialPath + bookChoice;
    getLocalFile(fullPath);
    ...
}

The page displayed in the browser offers a drop-down list to select a book name. If we select "Anna Karenina (Leo Tolstoy)"; the value of bookChoice would then be:

Anna Karenina.epub

Which means that fullPath -- the full name of the file being returned -- would be:

 X:\app\src\main\resources\books\Anna Karenina.epub

And the correct epub file will be returned.

But because the bookChoice value is not being validated, a malicious user could ask for other files on the system. Having the choices be <select> options doesn't help here, because a malicious user can easily make their own POST request using a tool, or intercept and modify the request as it leaves the browser, or do other things to bypass the browser's restrictions.

Consider that an attacker figures out that you're running a Java Spring application, and supplies the following for bookChoice:

..\..\config\application.yml

This would result in fullPath being:

X:\app\src\main\resources\books\..\..\config\application.yml

Each .. here means "in the parent of the current directory", or "go one level up".The application will look two directories above the one where it normally would. This action breaks them out from the starting folder, goes one folder down to the config directory, and instructs the application to retrieve the application.yml file, which contains information that could help the attacker. Automated tools exist to allow attackers to use this technique to search for and find many common sensitive data files with very little effort; any file the application can access, the attacker can obtain.

To summarize: if an attacker is allowed to specify all or part of the filename, it may be possible to gain unauthorized access to files on the server, including those outside the Webroot, that would normally be inaccessible to end users. The level of exposure depends on the effectiveness of input validation routines, if any.

Fix

No matter how we choose to address this issue, there is no ideal fix within client-side code. It is trivial for users to circumvent client-side validation, and for this reason, it is never guaranteed that what the server receives is trustworthy. Therefore, you must address the issue within server-side code.

There are three basic patterns to fix Path Traversal flaws, all of which are various ways to validate the input coming from the client. From best solution to worst, they are:

  1. Indirect references
  2. Pattern whitelisting
  3. Pattern blacklisting

Indirect references

In this example, the set of acceptable filenames is already known; the page intends to have only five possible options from a drop-down box. If the corresponding filenames on the server are also known, there is no need for the client to submit them. Instead, they can send a numeric identifier (or UUID, or any similar identifier) to indicate their choice; the server code can then map these to a filename. This way, it doesn't matter what value the user sends: only approved values can return a file, and all other input will be rejected.

A simple implementation of this pattern follows.

-public ResponseEntity getSearchResults(String bookChoice) throws IOException {
+public ResponseEntity getSearchResults(int bookChoiceNumber) throws IOException {
     String partialPath = "X:\\app\\src\\main\\resources\\books\\";
-    String fullPath = partialPath + bookChoice;
+    Map books = new HashMap();
+
+    books.put(1, "Anna Karenina.txt");
+    books.put(2, "Leaves of Grass.txt");
+    books.put(3, "Pygmalion.txt");
+    books.put(4, "Siddhartha.txt");
+    books.put(5, "The Picture of Dorian Gray.txt");
+
+    if(books.containsKey(bookChoiceNumber)) {
+        String fullPath = partialPath + books.get(bookChoiceNumber);
+    }
+
     getLocalFile(fullPath);
     ...
 }
view fixed code only

NB: we hard-coded the values in this example for clarity. In a live application, instead of hard-coding the items in a HashMap, you would probably look up the value from a key-value store like a database, properties file, or similar source.

Pattern whitelisting

If you are unable to make an indirect reference, you can instead create a pattern or list of known good characters (a whitelist) in a valid filename, and ensure that any submitted data matches that list.

In our example, valid file names only consist of "word characters", spaces, dots, dashes, and underscores; and they also all end in '.epub'. This pattern doesn't include things like \, so we can be confident that if a bookChoice matches the pattern, it is likely to be safe. Be careful! An overly-broad pattern will provide you with no protection!

An implementation of this pattern might look like this:

public ResponseEntity getSearchResults(String bookChoice) throws IOException {
     String partialPath = "X:\\app\\src\\main\\resources\\books\\";
-    String fullPath = partialPath + bookChoice;
-    getLocalFile(fullPath);
+    /* Only allow file names that consist of alphanumeric, -, _, space, and .
+       and which end with .epub */
+    Pattern whitelist = Pattern.compile("^[\w-_. ]+\.epub$")
+
+    if (whitelist.matcher(bookChoice).matches()) {
+        // filename matched! Go ahead and send file
+        String fullPath = partialPath + bookChoice;
+        getLocalFile(fullPath);
+        ...
+    }
+    else {
+        // didn't match whitelist, throw Exception
+        throw new InvalidBookFileNameException(bookChoice);
+    }
     ...
 }
view fixed code only

Pattern blacklisting

If you don't have clear rules about filename patterns, you might have to resort to making a list of characters you know you wish to disallow (this is called a blacklist).

For example, you could choose to disallow any bookChoice that includes .. or \, which would make it difficult to obtain files outside of the directory specified by partialPath.

The following is an example of one way to do that.

public ResponseEntity getSearchResults(String bookChoice) throws IOException {
     String partialPath = "X:\\app\\src\\main\\resources\\books\\";
+    Pattern blacklist = Pattern.compile("\.\.|\\|/");  // matches .. or \ or /
+
+    if (blacklist.matcher(bookChoice).matches()) {
+        // if there are invalid characters, throw an Exception
+        throw new InvalidBookFileNameException(bookChoice);
+    }
+
     String fullPath = partialPath + bookChoice;
     getLocalFile(fullPath);
     ...
view fixed code only

References

CWE ↪WASC ↪