Blog · E.33 · Technical

File inclusion (LFI & RFI) explained

When an app picks which file to load from user input, an attacker can make it load the wrong one - a config file, a log, or code the server will execute. That's file inclusion, and it comes in two flavours: LFI (include a file already on the server) and RFI (fetch and include an attacker's file). LFI looks like “just reading files” until it escalates to remote code execution. Here's how both work, how LFI turns into RCE, how it differs from directory traversal, and how to prevent it.

LFIRFIFile InclusionRCE EscalationAllowlist
File Inclusion: User Input Picks the File · LFI = Local File · RFI = Attacker's Remote File · LFI → Code Execution · Fix: Allowlist, Never a Client Path File Inclusion: User Input Picks the File · LFI = Local File · RFI = Attacker's Remote File · LFI → Code Execution · Fix: Allowlist, Never a Client Path
// TL;DR

File inclusion happens when an app decides which file to include or load from user-supplied input without restricting the choice. Two forms: LFI (local file inclusion) - manipulated into including a file already on the server (config, logs, source); and RFI (remote file inclusion) - tricked into fetching and including a file from an attacker-controlled location. RFI is more immediately dangerous (attacker fully controls the content) but depends on the platform allowing remote inclusion, which modern setups often disable by default. LFI is more common, and though it starts as reading local files, it frequently escalates to remote code execution - e.g. planting code in a writable file (a log or upload) then including it so the embedded code runs with the app's privileges. That RCE escalation is why LFI is high severity, not mere info-disclosure. Fix: never use raw input to select a file - map to an allowlist of known-safe values, disable remote inclusion, canonicalise paths, and keep writable files separate from executable code. Related: directory traversal.

// 01 What file inclusion is

A file inclusion vulnerability occurs when an application decides which file to include or load based on user-supplied input, without properly restricting that choice. Some applications build pages by dynamically including files - for example choosing a template or module from a value in the request. If an attacker can influence that value, they may make the application include a file it was never meant to: a sensitive local file or, in the worst case, a file containing code the server will execute. There are two forms - local file inclusion (LFI), where the included file is one already on the server, and remote file inclusion (RFI), where the application is tricked into fetching and including a file from an external location the attacker controls. Both stem from the same mistake: trusting user input to select a file.

// 02 LFI vs RFI & vs directory traversal

The difference between LFI and RFI is where the included file comes from. In LFI, the application is manipulated into including a file that already exists on the server - a config file, a log, another sensitive file - by supplying a path that points to it. In RFI, the application is tricked into fetching and including a file from a remote, attacker-controlled location, so the attacker supplies their own malicious file directly. RFI is generally more immediately dangerous because the attacker fully controls the included content - but it depends on the platform being configured to allow inclusion of remote resources, which many modern setups disable by default. LFI is more common today. And note the distinction from directory traversal: traversal is about reading a file's contents outside the intended directory; file inclusion loads the file into the application, meaning that if the file contains code, it runs. That difference - read vs execute - is what makes inclusion the more severe cousin.

// 03 How LFI escalates to code execution

LFI starts as the ability to read files on the server - already serious, exposing configuration, credentials and source code. But it frequently escalates to running code. A common route: the attacker first gets their own content onto the server in a way they can later include - for example by planting code inside a file the application writes to, such as a log file or an uploaded file - and then uses the inclusion flaw to include that file so its embedded code executes. Because the included file is treated as part of the application, any code within it may run with the application's privileges. This escalation from file read to remote code execution is what makes LFI far more than an information-disclosure issue - and why we treat it as high severity and chain it fully during testing, exactly as with unsafe file uploads that provide the plantable file.

// 04 How to prevent file inclusion

The core principle: never use raw user input to decide which file to include. Where the file to load genuinely depends on user choice, map the input to a fixed set of known-safe values on the server - an allowlist that translates a short identifier to a specific, predetermined file - so the client never supplies a path at all. Beyond that: disable the ability to include remote files unless genuinely required (closing the RFI route), validate and canonicalise any path so it can't escape the intended directory, and separate uploaded or user-writable files from executable code so they can never be included and run (closing the LFI-to-RCE route). As with directory traversal, allowlisting known-good values is far more reliable than filtering out dangerous ones, and keeping the platform configured securely closes the remote-inclusion door. We verify all of this in a web application penetration test, including the escalation to code execution rather than stopping at file read.

// 05 Frequently asked questions

What is a file inclusion vulnerability?

It occurs when an application decides which file to include or load based on user-supplied input, without restricting the choice. Some apps build pages by dynamically including files - choosing a template or module from a request value. If an attacker influences that value, they may make the app include a file it was never meant to: a sensitive local file, or code the server will execute. Two forms exist - local file inclusion (a file already on the server) and remote file inclusion (a file from an attacker-controlled external location). Both stem from trusting user input to select a file.

What's the difference between LFI and RFI?

Where the included file comes from. In LFI, the app is manipulated into including a file already on the server - a config file, log, or other sensitive file - via a path pointing to it. In RFI, the app is tricked into fetching and including a file from a remote, attacker-controlled location, so the attacker supplies their own malicious file directly. RFI is generally more immediately dangerous because the attacker fully controls the content, but depends on the platform allowing remote inclusion, which many modern setups disable by default. LFI is more common today and can escalate from file read to code execution.

How does LFI lead to code execution?

LFI starts as reading files on the server - already serious, exposing config, credentials and source. But it frequently escalates: the attacker first gets their own content onto the server in a way they can later include - for example planting code inside a file the app writes to, like a log or an uploaded file - then uses the inclusion flaw to include that file so its embedded code executes. Because the included file is treated as part of the application, code within it can run with the app's privileges. That escalation from file read to RCE is why LFI is high severity.

How do you prevent file inclusion?

Never use raw user input to decide which file to include. Where the file depends on user choice, map the input to a fixed set of known-safe values on the server - an allowlist translating a short identifier to a specific predetermined file - so the client never supplies a path. Disable remote file inclusion unless genuinely required, validate and canonicalise any path so it can't escape the intended directory, and separate uploaded or user-writable files from executable code so they can't be included and run. Allowlisting known-good values beats filtering out dangerous ones, and secure platform configuration closes the remote route.

// 06 Related reading

UG

Usama Gul

Founder & Penetration Testing Lead, CyberFortify

Chains file-inclusion findings to their real impact — escalating LFI from file read to remote code execution during testing rather than reporting it as mere information disclosure, and verifying allowlists and platform hardening close both routes.

Does a template parameter run attacker code?

We test every file-selecting feature for LFI and RFI — and chain LFI all the way to code execution rather than stopping at file read, so you see the real severity and the real fix.

Scope a web app test → Read: directory traversal →