File inclusion
Directory map
- Intro to file inclusions — LFI basics, templating, impact, examples (PHP, Node, Java, .NET), read vs execute
- Local file inclusion (LFI) — exploitation basics, traversal, prefixes, extensions, second-order
- LFI — basic bypasses — non-recursive filters, encoding, approved paths, appended extension (legacy truncation/null byte)
- PHP filter wrappers (
php://filter) — Base64 source disclosure, fuzzing PHP endpoints, appended extensions - PHP wrappers for LFI → RCE —
data://,php://input,expect://,allow_url_include, readingphp.ini - Remote file inclusion (RFI) — LFI vs RFI, verifying URL inclusion, RCE via HTTP/FTP/SMB, SSRF overlap
- LFI and file uploads — polyglot images, discovering upload paths, PHP
zip:///phar://chains, legacy phpinfo edge case - Log poisoning (LFI → RCE) — PHP session files, Apache/Nginx access logs,
/prochints, SSH/FTP/mail logs, permissions and safety
Summary
File inclusion flaws happen when HTTP parameters or path segments choose a file to load or render, and that choice is not safely constrained. Local File Inclusion (LFI) lets attackers read (or sometimes execute) arbitrary server files—common around templating patterns such as index.php?page=about where the page fragment maps to an include path. Impacts include source disclosure, secret leakage (keys, credentials, configs), and under some primitives remote code execution. Examples span PHP (include / require / file_get_contents and friends), Node (fs.readFile, path.join breakout, res.render with interpolated paths), Java/JSP (jsp:include, c:import), and .NET (Response.WriteFile, @Html.Partial, SSI includes). A key review axis is read-only vs execute and local vs remote URL support: execution-capable includes (e.g. PHP include, some c:import / SSI cases) widen blast radius to RCE, while read-only sinks still enable credential and logic leakage. Defensive themes are allowlisting, canonical path checks, and never wiring raw request data into filesystem or template resolution.
For exploitation mechanics, see local-file-inclusion.md: swapping parameters for system files (e.g. /etc/passwd), ../ path traversal when a directory is prepended, /-led payloads when a filename prefix is concatenated, the effect of appended extensions, second-order LFI via stored fields (e.g. usernames used in download paths), and the lab distinction between reading PHP source vs executing it.
For filter and normalizer bypasses (single-pass ../ stripping, URL/double encoding, regex-approved path prefixes, and legacy PHP tricks for forced extensions), see lfi-basic-bypasses.md.
For PHP php://filter streams—using read=convert.base64-encode so included .php files leak as Base64 instead of executing, fuzzing *.php with non-200 responses in scope, and chaining reads from index.php or ffuf hits—see php-filters-lfi.md.
For PHP stream wrappers that drive RCE through an execution-capable include-style sink: confirm allow_url_include (and related settings) by Base64-reading php.ini under /etc/php/X.Y/apache2/ or .../fpm/; then try data://text/plain;base64,... for inline PHP, php://input with a POST body when the parameter accepts POST, and expect://command when the expect extension is installed and working. Trivial pivots (configs, ~/.ssh/id_rsa) remain parallel options. Phar/zip + upload chains are noted as follow-ons—see php-wrappers-lfi-rce.md.
Remote File Inclusion (RFI) applies when the sink accepts remote URLs (http://, ftp://, or on Windows UNC/SMB paths). Benefits: RCE by hosting a script the server includes and executes (when the primitive executes), and SSRF-style probing of localhost / internal ports (e.g. http://127.0.0.1:80/...). RFI usually implies LFI; the converse fails when URL wrappers are disallowed, the attacker cannot supply a full scheme, or the stack disables remote inclusion (default on many servers). Verify with a real URL fetch (often starting with loopback HTTP); php.ini alone is not always definitive. Delivery channels: HTTP (python3 -m http.server), FTP (pyftpdlib, ftp://—useful if HTTP is filtered), SMB (impacket-smbserver, \\host\share\file.php on Windows without allow_url_include). Avoid recursive self-includes. Details: remote-file-inclusion.md.
LFI plus file uploads chains a benign-looking upload (often profile images) with an execution-capable include. The upload need only store attacker bytes (e.g. polyglot GIF: magic bytes GIF8 plus embedded <?php ... ?>); normal image delivery does not execute code, but including that path via LFI does. Discover paths from img src, app URLs, or fuzz common upload directories; adjust with ../ when the sink prepends a base path. PHP fallbacks use zip:// (zip containing shell.php, # → %23 in the URL) or phar:// (generated with phar.readonly=0); wrappers may be off or blocked, and zip-as-image can fail content checks—polyglot + include remains the most broadly reliable. A rare legacy combo is LFI + file_uploads + old PHP + exposed phpinfo() for temp upload paths. Details: lfi-file-uploads.md.
Log poisoning writes PHP into a file the server already appends to (session store or HTTP/service logs), then includes that path through LFI so the sink executes it. PHP session poisoning targets sess_<PHPSESSID> under /var/lib/php/sessions/ (Linux) or C:\Windows\Temp\ (Windows): confirm a controllable session key (e.g. page driven by ?language=), URL-encode a small system($_GET['cmd']) payload into that key, re-include the session file with &cmd=, and expect session churn after inclusion (re-poison or pivot to a durable shell). Access-log poisoning sets User-Agent to PHP (Burp or curl -H @file); include access.log from /var/log/apache2/ or /var/log/nginx/ (defaults vary; misconfigurations may expose logs to www-data). Large logs are slow and risky in prod. /proc/self/environ and /proc/self/fd/N may mirror User-Agent when logs are unreadable. Broader pattern: SSH username, FTP user, or mail lines in sshd / vsftpd / mail logs if LFI can read them. Details: log-poisoning-lfi.md.