Path traversal (also known as directory traversal) is a vulnerability that enables an attacker to read arbitrary files on the server that is running an application.

This might include:

  • Application code and data: Source code, configuration files, SQLite databases.
  • Credentials for back-end systems: Passwords, API keys, database connection strings.
  • Sensitive operating system files: /etc/passwd, C:\Windows\win.ini.

In some critical cases, an attacker might be able to write to arbitrary files on the server, modifying application data or behavior, which can ultimately lead to full server compromise.


The Core Vulnerability

Imagine an application that loads an image using a URL parameter:

<img src="/loadImage?filename=218.png">

Under the hood, the application might append this parameter to a base directory: /var/www/images/ + 218.png = /var/www/images/218.png

If the application implements no defenses, an attacker can manipulate the filename parameter using ../ (dot-dot-slash) sequences to escape the base directory:

https://insecure-website.com/loadImage?filename=../../../etc/passwd

The sequence ../ steps up one level in the directory structure. Three consecutive ../ sequences step up from /var/www/images/ to the filesystem root (/), effectively reading /etc/passwd.

Note on Windows Environments: Both ../ and ..\ are valid directory traversal sequences on Windows. Example: https://insecure-website.com/loadImage?filename=..\..\..\windows\win.ini


Bypassing Common Defenses

Many applications attempt to sanitize user input to prevent path traversal, but these defenses can often be bypassed. Here are common obstacles and how to circumvent them:

1. Absolute Paths

If an application simply strips or blocks directory traversal sequences (../), you might be able to bypass it by using an absolute path directly from the filesystem root.

  • Payload: filename=/etc/passwd

2. Nested Traversal Sequences

If the application strips ../ non-recursively, you can nest the sequences. When the inner sequence is stripped, the outer characters form a new traversal sequence.

  • Payload: ....// or ....\/
  • Example: ....//....//....//etc/passwd becomes ../../../etc/passwd after stripping.

3. URL Encoding

Web application firewalls (WAFs) or the web server itself may block explicit ../ strings. You can sometimes bypass this sanitization by URL encoding, or double URL encoding, the characters.

  • Standard URL encoding: %2e%2e%2f
  • Double URL encoding: %252e%252e%252f
  • Non-standard Unicode encodings: ..%c0%af or ..%ef%bc%8f

4. Required Base Folder Bypass

An application may require the user-supplied filename to explicitly start with the expected base folder (e.g., /var/www/images). You can satisfy this requirement and then traverse out of it.

  • Payload: filename=/var/www/images/../../../etc/passwd

5. Null Byte Bypass (File Extension Validation)

An application may require the user-supplied filename to end with an expected file extension (e.g., .png). You can use a null byte (%00) to terminate the file path before the required extension, tricking the validation logic while making the filesystem API ignore everything after the null byte.

  • Payload: filename=../../../etc/passwd%00.png

Prevention & Mitigation

The most effective way to prevent path traversal vulnerabilities is to avoid passing user-supplied input to filesystem APIs altogether.

If you must pass user-supplied input to filesystem APIs, use a robust, two-layered defense strategy:

  1. Validate User Input (Allow-listing): Compare the user input with a strict allow-list of permitted values. If that isn’t possible, verify that the input contains only permitted content (e.g., strictly alphanumeric characters).
  2. Path Canonicalization: Append the input to the base directory and use a platform filesystem API to canonicalize the resulting path. Verify that the canonicalized path starts securely with the expected base directory.

Secure Implementation Example (Java)

// 1. Combine base directory with user input
File file = new File(BASE_DIRECTORY, userInput);

// 2. Canonicalize and verify the final path
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) {
    // Path is safe. Process file...
} else {
    // Path traversal attempt detected! Reject request.
}