HIGH path traversalflask

Path Traversal in Flask

How Path Traversal Manifests in Flask

Path traversal, also known as directory traversal, is an attack where an adversary manipulates file path inputs to access files and directories outside the intended web root or restricted areas. In Flask applications, this vulnerability commonly arises when user-supplied input is used directly to construct file system paths for reading or serving files.

A typical vulnerable pattern in Flask is a route that accepts a filename parameter and uses it with Python's built-in open() function or with Flask's send_file() without proper validation. For example:

from flask import Flask, request, send_file

app = Flask(__name__)

@app.route('/download')
def download():
    filename = request.args.get('file')
    # Vulnerable: user input directly used in file operation
    return send_file(f'/var/www/files/{filename}')

An attacker can exploit this by providing a filename like ../../../etc/passwd to read the system's password file. Even if the application uses send_from_directory, which internally uses safe_join to prevent .. sequences, it is only safe when the directory argument is fixed and trusted. If the directory itself is derived from user input, the protection is bypassed:

@app.route('/download')
def download():
    directory = request.args.get('dir')  # user-controlled
    filename = request.args.get('file')
    # Vulnerable if 'directory' contains '..'
    return send_from_directory(directory, filename)

Flask's safe_join (used by send_from_directory) resolves the path and checks that it starts with the given directory. However, if the directory is user-controlled, an attacker can set it to / and then use filename to access any file.

Additionally, path traversal can occur in other contexts, such as when loading templates or reading configuration files if user input influences the path.

The impact of a successful path traversal attack can be severe: an attacker may read sensitive files (e.g., application source code, environment variables with secrets, system files like /etc/passwd), leading to full system compromise. This vulnerability falls under OWASP API Top 10:2023's Broken Object Property Level Authorization (BOLA) because it allows unauthorized access to resources. It also relates to OWASP Top 10:2021's A01:2021-Broken Access Control.

Flask-Specific Detection

Detecting path traversal vulnerabilities in Flask APIs involves identifying endpoints that accept file-related parameters and testing them with traversal payloads. Manually, you would review code for any use of open, send_file, send_from_directory, or similar functions with user-supplied input. Look for routes that take parameters like file, filename, path, dir, etc.

Automated scanning can efficiently find these issues. middleBrick's API security scanner includes an Input Validation check that tests for path traversal. When you submit a Flask API endpoint to middleBrick, it sends a series of payloads including:

PayloadDescription
../Unix directory up-level
..\Windows directory up-level
....//Bypass naive filters that remove one ..
%2e%2e%2fURL-encoded ../
..%2fPartial encoding

The scanner then analyzes responses for signs of successful traversal, such as:

  • HTTP 200 responses containing known file content (e.g., root:x:0:0 for /etc/passwd)
  • Error messages that reveal file system structure
  • Differences in response length or content when using traversal vs. normal requests

middleBrick runs these tests in parallel along with 11 other security checks, returning a risk score (A-F) and detailed findings within 5-15 seconds. For example, if your Flask endpoint at https://api.example.com/download is vulnerable, middleBrick will report it under the Input Validation category with a severity rating and a proof-of-concept payload that worked.

You can scan your Flask API using middleBrick's web dashboard, the CLI tool (middlebrick scan <url>), or integrate it into your CI/CD pipeline with the GitHub Action. This allows you to catch path traversal issues early in development.

Flask-Specific Remediation

Remediating path traversal in Flask requires strict control over any file system access based on user input. The primary strategy is to avoid using user input directly in file paths. Instead, use a whitelist of allowed files or map user input to safe, predetermined paths.

The safest approach is to use Flask's send_from_directory with a fixed, absolute directory and a validated filename. However, even then, you must ensure the filename does not contain path traversal sequences. Here is a robust pattern:

from flask import Flask, request, send_from_directory, abort
import os

app = Flask(__name__)

# Define a safe base directory (absolute path)
SAFE_BASE_DIR = '/var/www/files'

@app.route('/download')
def download_file():
    filename = request.args.get('file')
    if not filename:
        abort(400, 'Missing file parameter')
    
    # Remove any path components and normalize
    # Option 1: Use os.path.basename to strip directory parts
    safe_filename = os.path.basename(filename)
    
    # Option 2: Additional validation: allow only alphanumeric, dash, underscore, dot
    # import re
    # if not re.match(r'^[\w\-\.]+$', safe_filename):
    #     abort(400, 'Invalid filename')
    
    # Construct the full path
    full_path = os.path.join(SAFE_BASE_DIR, safe_filename)
    # Resolve to absolute path and ensure it's within SAFE_BASE_DIR
    abs_path = os.path.abspath(full_path)
    if not abs_path.startswith(SAFE_BASE_DIR):
        abort(403)
    
    # Use send_from_directory with the safe base and the safe filename
    return send_from_directory(SAFE_BASE_DIR, safe_filename)

Alternatively, you can avoid file system paths altogether by using a mapping:

FILE_MAP = {
    'report1': '/var/www/files/report1.pdf',
    'report2': '/var/www/files/report2.pdf'
}

@app.route('/download')
def download():
    file_id = request.args.get('id')
    if file_id not in FILE_MAP:
        abort(404)
    return send_file(FILE_MAP[file_id])

If you must read files with open, apply the same validation:

with open(abs_path, 'rb') as f:
    content = f.read()

Always use absolute paths for the base directory and resolve the user-provided path with os.path.abspath before checking it starts with the base directory. This prevents bypasses via symlinks or complex traversal sequences.

Additionally, ensure your Flask application runs with the least privileges necessary, so that even if a traversal occurs, the attacker can only access files readable by the application user.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

What is path traversal and why is it dangerous in Flask applications?
Path traversal is an attack where an attacker uses `../` sequences to access files outside the intended directory. In Flask, this can occur when user input is used to construct file paths for reading or serving files. It can lead to exposure of sensitive data (source code, credentials, system files) and is considered a high-risk vulnerability under OWASP API Top 10:2023 (BOLA) and OWASP Top 10:2021 (Broken Access Control).
How can middleBrick help detect path traversal vulnerabilities in my Flask API?
middleBrick automatically scans your API endpoints for path traversal by sending payloads like `../../../etc/passwd` and analyzing responses for signs of successful exploitation. It reports any findings under the Input Validation category with severity, proof-of-concept, and remediation guidance. You can use the web dashboard, CLI, or GitHub Action to integrate scanning into your workflow.