Local File Inclusion (LFI)
Local File Inclusion (LFI) is a web vulnerability that lets an attacker trick an application into reading — and sometimes executing — files that already exist on the server. If a page decides which file to load based on user input and doesn't validate that input, an attacker can walk the filesystem to reach configuration files, credentials, source code, or system files like /etc/passwd. In the worst case, LFI is chained into full remote code execution. This guide explains how LFI works, the techniques attackers use to escalate it, how to test for it, and how to fix and prevent it.
What is Local File Inclusion (LFI)?
In plain terms: a website lets the visitor choose which file to display, but forgets to check that the choice is a legitimate one. An attacker abuses that trust to point the application at files it was never meant to serve.
Technically: LFI occurs when user-controlled input is used to build a file path that is then passed to a file-reading or file-inclusion function (for example PHP's include(), require(), file_get_contents(), or equivalents in other languages) without proper sanitisation. Because the path is attacker-influenced, it can be manipulated to reference arbitrary local files.
LFI is closely related to Remote File Inclusion (RFI) and path traversal. The difference: LFI includes files already on the server, while RFI pulls in a file from a remote URL. Both stem from the same root cause — trusting user input to build a path.
How Local File Inclusion Works
Consider a PHP page that loads content based on a query parameter:
// vulnerable code
$page = $_GET['page'];
include("/var/www/pages/" . $page . ".php");
The application expects values like ?page=about. But nothing stops an attacker from supplying a relative path that climbs out of the intended directory:
?page=../../../../../../etc/passwd%00
Each ../ moves one directory up (this is the path traversal technique). With enough of them the attacker reaches the filesystem root and reads any file the web-server user can access. Historically, a trailing null byte (%00) was used to cut off an appended .php extension; modern PHP is patched against this, but the traversal itself still works whenever input is unvalidated.
From file read to code execution
LFI is often dismissed as "just" information disclosure. In practice it is frequently escalated to remote code execution (RCE) using well-known techniques:
- PHP filter wrapper —
php://filter/convert.base64-encode/resource=index.phpreturns the base64-encoded source of a script, exposing secrets and logic without triggering execution. - Log poisoning — the attacker injects PHP into a value the server logs (such as the
User-Agentheader), then uses LFI to include the log file (/var/log/apache2/access.log). The injected code runs when the log is parsed as PHP. - Wrappers and data streams —
data://,php://input, and (on misconfigured servers)expect://can turn an include into direct code execution. - /proc and session files — including
/proc/self/environor PHP session files can inject attacker-controlled data into the execution path.
Because of these escalation paths, an LFI finding should be treated as potentially critical, not merely a low-severity leak.
How to Test for Local File Inclusion
Testing combines manual probing with automated scanning. Start by identifying every parameter that could influence a file path — page, file, template, lang, include, view, doc are common names.
Manual checks. For each candidate parameter, try payloads such as:
?file=../../../../etc/passwd— classic traversal to a readable system file.?file=....//....//etc/passwd— bypasses naive filters that strip a single../.?file=php://filter/convert.base64-encode/resource=index— reveals source code as base64.?file=/etc/passwd%00— legacy null-byte truncation, still worth testing on old stacks.
A response that contains system-file contents (for example lines starting with root:x:0:0:) confirms the vulnerability.
Automated scanning. Manually testing every parameter across a large application is slow and error-prone. An automated scanner walks the site, fuzzes each input with traversal and wrapper payloads, and reports confirmed inclusions.
Check your site for LFI and other flaws
Secably's free scanner fuzzes your parameters for file inclusion, path traversal, XSS, SQL injection and more — no signup needed.
Run a Free Website Scan →How to Fix Local File Inclusion
The reliable fix is to never let user input choose a path directly. Map user input to a fixed, server-side allowlist instead:
// safe: user input only selects a key, never a path
$pages = [
'about' => 'about.php',
'contact' => 'contact.php',
];
$key = $_GET['page'] ?? 'about';
$file = $pages[$key] ?? 'about.php'; // unknown keys fall back safely
include("/var/www/pages/" . $file);
If a dynamic path is truly unavoidable, defend in depth:
- Strip directory components with
basename()so only a filename remains. - Resolve the final path with
realpath()and verify it still lives inside the intended base directory before opening it. - Reject any input containing
../, null bytes, or wrapper prefixes such asphp://anddata://. - Disable
allow_url_includein PHP to block the RFI-style escalation.
Prevention Best Practices
- Allowlist, don't blocklist. Validate input against a known set of permitted values rather than trying to filter out bad ones.
- Avoid dynamic includes entirely where you can — a router or explicit mapping is safer than including a computed path.
- Least privilege. Run the web server as a low-privileged user so even a successful read exposes as little as possible.
- Harden the runtime. Disable unused PHP wrappers, set
open_basedir, and keep your framework and language patched. - Test continuously. Add file-inclusion checks to your CI and re-scan after every deploy, since new parameters reintroduce risk.
Impact and Severity
The impact of LFI ranges from disclosure of sensitive files — source code, database credentials, API keys, /etc/passwd — to complete server compromise when it is chained into remote code execution via log poisoning or PHP wrappers. Severity therefore depends on which files are reachable and whether the attacker can turn a read primitive into execution. Given how often LFI escalates, treat any confirmed instance as high priority.