Local File Inclusion (LFI)
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and psalm as the tools, both specialist SAST tools. The code_pattern identifies `include($_GET[` and similar patterns, which default linters won't catch but semgrep/psalm rules will. It won't be caught by the compiler or a standard linter, and it won't necessarily surface at runtime until exploited.
Closest to 'simple parameterised fix' (e3). The quick_fix states: 'Never pass user input to include/require; use a whitelist map of allowed template names to file paths.' This is a targeted fix — replacing dynamic include calls with a whitelist lookup — confined to include/require call sites, typically within one or a few files. It's more than a one-line patch but doesn't span the whole codebase.
Closest to 'localised tax' (b3). The vulnerability applies to web and CLI PHP contexts but is scoped to include/require call sites that accept user input. Once fixed with a whitelist, the ongoing burden is low — future maintainers only need to update the whitelist when adding templates. It doesn't impose a persistent productivity tax across the entire codebase.
Closest to 'serious trap' (t7). The misconception field states explicitly: 'LFI only lets attackers read files — it cannot lead to code execution.' This is a well-documented but seriously dangerous misconception. Developers familiar with file-read vulnerabilities may dismiss LFI as low-severity, not realising it escalates to RCE via log poisoning, session file inclusion, or PHP wrappers — behaviour that contradicts the intuitive 'local file inclusion = read only' mental model.
Also Known As
TL;DR
Explanation
LFI is a specific form of path traversal where the attacker causes the application to include a local file using PHP's include, require, include_once, or require_once. If the attacker can write a file to the server (e.g. via file upload), they can then include it and achieve remote code execution. Even without write access, LFI can expose /etc/passwd, application config files, and session data.
How It's Exploited
# %00 null byte terminates the .php extension (older PHP)
# Reads /etc/passwd contents
Common Misconception
Why It Matters
Common Mistakes
- Using user-supplied language or template parameters directly in include() without a strict whitelist.
- Path traversal filtering that only strips ../ but misses URL-encoded or double-encoded variants.
- PHP wrappers like php://filter and php://input being usable even when URL include is disabled.
- Not using open_basedir to restrict which directories PHP can access at runtime.
Code Examples
$page = $_GET['page'];
include "pages/$page.php"; // LFI: ?page=../../../../etc/passwd%00
// Allowlist — only permit known page names
$allowed = ['home', 'about', 'contact', 'faq'];
$page = $_GET['page'] ?? 'home';
if (!in_array($page, $allowed, true)) {
$page = 'home';
}
include "pages/{$page}.php"; // only pages in the allowlist