Weakness reference
CWE-23

Relative Path Traversal

Relative path traversal occurs when an application constructs file paths using user-supplied input without properly validating or sanitizing relative path…

01Summary

Relative path traversal occurs when an application constructs file paths using user-supplied input without properly validating or sanitizing relative path sequences like ../. An attacker can use these sequences to navigate outside the intended directory and access, modify, or delete files elsewhere on the system. This weakness is particularly dangerous in web applications, file managers, and any system that dynamically builds file paths based on user input.

02How It Happens

The vulnerability arises when an application concatenates user input directly into a file path without checking for directory traversal sequences. For example, if a web application accepts a filename parameter and constructs a path like uploads/ + user_input, an attacker can supply ../../etc/passwd to escape the uploads directory. The application may also fail to canonicalize paths (convert them to their absolute, normalized form) before validation, allowing bypass of simple string-matching checks. Relative paths are particularly problematic because they're context-dependent — the same input behaves differently depending on the current working directory.

03Real-World Impact

Successful exploitation can lead to unauthorized file access, allowing attackers to read sensitive configuration files, source code, or private data. In write scenarios, attackers may overwrite critical application files, inject malicious code, or corrupt system files. On shared hosting or multi-tenant systems, an attacker might access files belonging to other users or applications. The severity depends on the application's file permissions and what files exist in parent directories, but the potential for data breach or system compromise is significant.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import os

def serve_file(filename):
    base_dir = "/var/www/uploads"
    file_path = os.path.join(base_dir, filename)
    with open(file_path, 'r') as f:
        return f.read()

# Called with user input from a web request
user_file = request.args.get('file')
content = serve_file(user_file)

Why it's vulnerable:
The code concatenates user input directly into the file path without validating for ../ sequences. An attacker can supply ../../etc/passwd to read files outside the intended directory.

Fixed pattern
import os

def serve_file(filename):
    base_dir = "/var/www/uploads"
    file_path = os.path.abspath(os.path.join(base_dir, filename))
    
    # Verify the resolved path is still within the base directory
    if not file_path.startswith(os.path.abspath(base_dir)):
        raise ValueError("Path traversal attempt detected")
    
    with open(file_path, 'r') as f:
        return f.read()
Vulnerable pattern
<?php
$base_dir = "/var/www/uploads";
$filename = $_GET['file'];
$file_path = $base_dir . "/" . $filename;

if (file_exists($file_path)) {
    echo file_get_contents($file_path);
}
?>

Why it's vulnerable:
User input from $_GET is directly appended to the base directory path. An attacker can supply ../../etc/passwd to traverse outside the uploads folder.

Fixed pattern
<?php
$base_dir = "/var/www/uploads";
$filename = $_GET['file'];
$file_path = realpath($base_dir . "/" . $filename);

// Verify the resolved path is within the base directory
if ($file_path === false || strpos($file_path, realpath($base_dir)) !== 0) {
    die("Access denied");
}

if (file_exists($file_path)) {
    echo file_get_contents($file_path);
}
?>

05Prevention Checklist

Canonicalize all file paths
using functions like os.path.abspath() (Python) or realpath() (PHP) before any file operation, then verify the resolved path stays within the intended directory.
Allowlist acceptable filenames
— if possible, maintain a whitelist of permitted files or filename patterns rather than trying to blacklist dangerous sequences.
Reject paths containing ../ or ..\\
as a defense-in-depth measure, even after canonicalization.
Use basename() or equivalent
to strip directory components from user input if only the filename is needed, not the full path.
Run the application with minimal file system permissions
— use a dedicated user account with access only to necessary directories, limiting damage if traversal succeeds.
Log and alert on path traversal attempts
— monitor for ../ sequences in file-related parameters and flag them as suspicious activity.

06Signs You May Already Be Affected

Check your web server and application logs for requests containing ../, ..%2f, or similar encoded traversal sequences in file parameters. Review file access logs for unexpected reads of sensitive files (e.g., configuration files, /etc/passwd equivalents) from your application's process. If you find unauthorized files in your uploads or working directories, or if configuration files show unexpected modification times, investigate whether path traversal was used to plant or modify them.

07Related Recent Vulnerabilities