Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Vulnerability Research

File Path Injection: What It Is, How It Works & How to Fix It

File path injection (CWE-22) lets attackers read, overwrite, or delete arbitrary files on your server. Learn how path traversal attacks work and how to prevent them in your code.

Offensive360 Security Research Team — min read
file path injection path traversal CWE-22 directory traversal file inclusion SAST web security application security

File path injection — also called path traversal or directory traversal — is a vulnerability class (CWE-22) where an attacker manipulates a file path parameter to access files and directories outside the intended scope. In severe cases, it allows reading sensitive system files, overwriting configuration files, or achieving remote code execution.

File path injection consistently appears in OWASP Top 10 lists under A01:2021 – Broken Access Control and is one of the most common findings in enterprise code audits. This guide explains exactly how the vulnerability works, what it looks like in code, and how to fix it.


What Is File Path Injection?

File path injection occurs when user-controlled input is used to construct a file system path — and the application fails to validate or restrict that input before accessing the file.

The classic attack uses the ../ sequence (dot-dot-slash) to traverse up the directory tree:

https://example.com/download?file=../../etc/passwd

If the application naively appends this parameter to a base path and reads the resulting file, the attacker gets the contents of /etc/passwd — and by extension, can probe the entire file system.


How File Path Injection Works

Basic Path Traversal

Consider this vulnerable Python code:

import os

BASE_DIR = "/var/app/uploads/"

def download_file(filename):
    path = BASE_DIR + filename
    with open(path, "rb") as f:
        return f.read()

If a user supplies filename = "../../etc/shadow", the resolved path becomes /var/app/uploads/../../etc/shadow/etc/shadow. The attacker can read the system’s password hashes.

Encoded and Obfuscated Traversal

Attackers encode the traversal sequence to bypass naive string-matching defenses:

EncodingPayload
URL encoding%2e%2e%2f = ../
Double URL encoding%252e%252e%252f
Unicode / UTF-8%c0%ae%c0%ae/
Mixed separator..\ (Windows)
Null byte../../../etc/passwd%00.jpg

A robust fix cannot rely on blocking ../ — it must validate the resolved canonical path.

File Inclusion via Path Injection

In PHP applications, path injection can escalate to Local File Inclusion (LFI), where the attacker forces the application to execute an arbitrary file as PHP code:

// Vulnerable
$page = $_GET['page'];
include("/var/www/html/pages/" . $page . ".php");

Payload: page=../../var/log/apache2/access.log%00 — if the attacker can inject PHP code into the access log (via a crafted User-Agent), this becomes Remote Code Execution.


What an Attacker Can Achieve

Depending on the vulnerability and system configuration, file path injection enables:

  • Sensitive file read/etc/passwd, /etc/shadow, SSH private keys, application config files with database credentials, .env files
  • Source code disclosure — Reading application source files to find additional vulnerabilities
  • Log poisoning → RCE — Writing attacker-controlled content into log files and then including them
  • Config file overwrite — If the application writes to a user-supplied path, attackers can overwrite cron jobs, authorized_keys, web server configs
  • Web shell upload — Writing a PHP/ASP/JSP file to a web-accessible directory

Real-World Impact

File path injection vulnerabilities have contributed to significant breaches:

  • Accellion FTA (2021) — Path traversal in file transfer software led to data theft at dozens of organizations including Morgan Stanley, Kroger, and the Reserve Bank of New Zealand.
  • Pulse Secure VPN (CVE-2019-11510) — Unauthenticated path traversal allowed reading arbitrary files including VPN session files and plaintext credentials.
  • Apache HTTP Server (CVE-2021-41773) — Path traversal in the URL normalization layer allowed remote file read and code execution on Apache 2.4.49.

How to Detect File Path Injection in Code

Manual Code Review

Look for these patterns in code review:

  • File I/O functions that accept user-controlled input: open(), fopen(), FileStream(), include(), require()
  • String concatenation used to build file paths from user input
  • Lack of path canonicalization before file access
  • Missing allowlist of permitted filenames or directories

Static Code Analysis (SAST)

A static code analysis tool with taint analysis will automatically trace user-controlled data from HTTP request parameters (the source) through string concatenation operations to file system calls (the sink), flagging the injection wherever the path is not properly sanitized.

Offensive360 detects file path injection across all major languages — including interprocedural cases where the user input is passed through multiple function calls before reaching the file system operation. The findings include the exact file, line, data flow trace, and recommended fix.


How to Fix File Path Injection

Fix 1: Canonicalize and Validate the Resolved Path

The most reliable fix is to resolve the canonical absolute path of the requested file and verify it starts with the expected base directory:

Python:

import os

BASE_DIR = os.path.realpath("/var/app/uploads/")

def download_file(filename):
    requested = os.path.realpath(os.path.join(BASE_DIR, filename))
    if not requested.startswith(BASE_DIR + os.sep):
        raise ValueError("Access denied: path traversal detected")
    with open(requested, "rb") as f:
        return f.read()

Java:

import java.nio.file.*;

Path baseDir = Path.of("/var/app/uploads/").toRealPath();

public byte[] downloadFile(String filename) throws IOException {
    Path requested = baseDir.resolve(filename).normalize().toRealPath();
    if (!requested.startsWith(baseDir)) {
        throw new SecurityException("Path traversal detected");
    }
    return Files.readAllBytes(requested);
}

C#:

string baseDir = Path.GetFullPath("/var/app/uploads/");

public byte[] DownloadFile(string filename)
{
    string requested = Path.GetFullPath(Path.Combine(baseDir, filename));
    if (!requested.StartsWith(baseDir, StringComparison.OrdinalIgnoreCase))
        throw new UnauthorizedAccessException("Path traversal detected");
    return File.ReadAllBytes(requested);
}

Fix 2: Use an Allowlist of Permitted Files

If the set of accessible files is known and bounded, reject any filename not on the allowlist:

ALLOWED_FILES = {"report.pdf", "template.docx", "readme.txt"}

def download_file(filename):
    if filename not in ALLOWED_FILES:
        raise ValueError("File not permitted")
    path = os.path.join("/var/app/uploads/", filename)
    with open(path, "rb") as f:
        return f.read()

This is the most secure approach when applicable — it completely eliminates the attack surface.

Fix 3: Use File IDs Instead of Names

Instead of accepting a filename from the user, accept an opaque identifier (UUID, numeric ID) and map it server-side to the actual file path:

FILE_MAP = {
    "a3f1c2d4": "/var/app/uploads/report.pdf",
    "b7e8f901": "/var/app/uploads/template.docx",
}

def download_file(file_id):
    path = FILE_MAP.get(file_id)
    if not path:
        raise ValueError("Invalid file ID")
    with open(path, "rb") as f:
        return f.read()

There is no user-controlled path component at all — path traversal is architecturally impossible.

What NOT to Rely On

These defenses are insufficient on their own:

  • Blocking ../ — Easily bypassed with URL encoding, Unicode, or Windows separators
  • Stripping ../ — Recursive stripping can be bypassed: ....// becomes ../ after one strip
  • Extension validation — Null byte injection (file.jpg%00) can bypass extension checks in some languages
  • Relative path checks — Without canonicalization, relative path manipulation still works

Checklist: Preventing File Path Injection

Use this checklist when reviewing code that handles user-supplied file paths:

  • All file paths constructed from user input are canonicalized with realpath() / toRealPath() / Path.GetFullPath() before use
  • The resolved path is validated to start with the expected base directory (with trailing separator)
  • An allowlist of permitted filenames or file IDs is used where feasible
  • User input is never passed directly to include(), require(), or similar dynamic loading functions
  • Upload directories are outside the web root (or have execute permissions disabled)
  • SAST scanning is part of the CI/CD pipeline to catch new instances automatically

Detecting File Path Injection with Offensive360

Offensive360 SAST performs deep taint analysis to detect file path injection across 60+ languages including Java, C#, Python, PHP, JavaScript/Node.js, Ruby, Go, and more. Findings include:

  • The exact source (user-controlled input entry point)
  • The complete data flow trace through your code
  • The vulnerable sink (file system call)
  • The severity and CWE classification (CWE-22)
  • A recommended fix with code examples

Scans run against your source code — on-premise, in your CI/CD pipeline, or as a one-time assessment. Try a one-time code scan for $500 or request a demo.

Offensive360 Security Research Team

Application Security Research

Find vulnerabilities before attackers do

Run Offensive360 SAST and DAST against your applications and get a full vulnerability report in minutes.