Skip to main content
File inclusion bugs happen when an application lets user input control which file gets pulled into the page, usually through something like ?language=en.php meant to pick a language file. Walk that path backward with ../ and you’re reading files outside the intended directory. Depending on what the server lets you reach, that ranges from reading /etc/passwd all the way to full remote code execution.

Local File Inclusion (LFI)

Including a file that already exists on the server, outside the intended directory. Starts as information disclosure, often escalates to RCE via wrappers, logs, or uploads.

Remote File Inclusion (RFI)

Including a file from a server you control instead. Immediate RCE if it works, but requires allow_url_include enabled in PHP, off by default since PHP 5.2.

Basic LFI

The simplest case, the parameter takes a path directly with no traversal needed at all.
Path traversal climbs out of the intended directory using ../ repeated enough times to reach the filesystem root.
Some apps prepend a fixed prefix to whatever you supply, in which case a leading / sometimes gets you back to root anyway since the app concatenates paths naively.
If the app restricts input to an “approved” subdirectory, traversing from inside that directory and back out still reaches arbitrary paths.

Bypassing Traversal Filters

Doubling the traversal sequence works against a naive filter that removes ../ only a single pass instead of recursively, since removing the inner ../ leaves a new valid one behind.
URL-encoding the slashes and dots slips past a filter doing a plain string match on ../ or /etc/passwd.
Double URL-encoding defeats a filter that decodes once before checking, but the app itself decodes twice.
UTF-8 overlong encoding of / can slip past filters on IIS/some WAFs that normalize encoding incompletely.
Path truncation (obsolete on PHP 5.3+, included for legacy/CTF context): PHP’s old MAXPATHLEN handling meant a sufficiently long path with trailing ./ sequences got silently truncated before the appended extension, dropping it entirely.
Null byte (obsolete on PHP 5.3.4+): a trailing %00 used to terminate the string in C-level string handling, cutting off anything appended after it, including a forced extension.
On a modern PHP version neither trick works, look instead for a wrapper that ignores the extension entirely (see PHP Wrappers below), or a legitimate extension your target path can end with.

PHP Wrappers for RCE

PHP wrappers are built-in stream handlers (php://, data://, zip://, expect://, and others) that PHP’s file functions can read through instead of a plain path, normally used for things like reading stdin or compressing output. When a parameter controls what gets passed to include() or require(), pointing it at one of these wrappers instead of a real file path lets you feed PHP arbitrary content to execute or decode, turning a file-read bug into code execution. PHP’s stream wrappers can turn a plain LFI into full code execution, each with different requirements.
Reads a file’s contents through a filter chain rather than including it, convert.base64-encode is the standard way to safely exfiltrate PHP source without it actually executing (and crashing on syntax it can’t parse as HTML).
A chain of convert.iconv conversions can mutate arbitrary input into valid PHP code without needing any file upload or even a valid file path at all (php://temp works as the resource). Tools like php_filter_chain_generator build the chain for you, this is the one wrapper technique that needs neither allow_url_include nor an existing file on disk.

Remote File Inclusion (RFI)

If allow_url_include is on, the parameter will fetch and execute a file from anywhere you point it, including a server you control. Any of these protocols work the same way, host the shell, point the vulnerable parameter at it.

LFI + Upload Chains

When RFI is off and the wrappers above don’t apply, combining an upload feature with LFI gets you the same result: get a malicious file onto the disk through any upload field, then include it through the LFI. A GIF magic-byte header in front of the PHP payload is often enough to pass a basic image-type check, and the server still parses the rest of the file as PHP when included.

Log and Session Poisoning

When there’s no upload feature at all, poisoning something the server already writes to disk (a log file, a session file) achieves the same effect without needing any upload functionality.

PHP Session Poisoning

The session cookie’s value is the session filename on disk, if you can control any value that gets written into your own session data, including that session file executes it.

Web Server Log Poisoning

1

Identify the log file path

Common Apache logs:
Common Nginx logs:
2

Poison the log via User-Agent

Every request’s User-Agent header gets written verbatim into the access log, including it as PHP.
3

Include the log file to trigger execution

On older Apache setups running PHP as a module (not PHP-FPM), /proc/self/environ holds the process’s environment variables, including the raw HTTP_USER_AGENT value from the request that spawned it. Poisoning the User-Agent the same way as log poisoning, then including this file instead of a log, gets the same result without needing to locate the actual log path.
This technique is well known enough that most modern systems either run PHP-FPM (no /proc/self/environ equivalent exposing the request) or restrict read access to the /proc filesystem, treat it as a fallback worth trying rather than a reliable primary path.

Automated Fuzzing

FFUF against a curated LFI payload wordlist covers most common traversal depths and wrapper variants in one pass, -fs filters out the baseline “not found” response size so only real hits show.
fimap automates the full LFI/RFI discovery-and-exploitation chain rather than just fuzzing a wordlist:
Nuclei has dedicated LFI templates if you want it folded into a broader automated sweep (see Automated Web Scanners):
dotdotpwn is an older, dedicated path-traversal fuzzer, still useful for generating encoding-variant payloads automatically:

Quick Reference Tools

Hackviser: LFI/RFI

Reference used for this page.

HackTricks: File Inclusion

Deeper dive into wrappers, filter chains, and edge cases.

SecLists: LFI Wordlists

Wordlists for automated file inclusion fuzzing.

php_filter_chain_generator

Automates building a convert.iconv filter chain for RCE without upload.

fimap

Automated LFI/RFI detection and exploitation.
Log poisoning and session poisoning both write attacker-controlled content into files the server relies on, confirm scope before using them, and be aware some of these techniques (especially /proc/self/environ) can affect server stability on older or fragile targets.