Modular programming is a cornerstone of modern software development. By separating logic into distinct files—such as database hendlers, configuration templates, or UI components—developers can maintain cleaner and more efficient codebases. Functions like include or import allow these separate modules to be merged at runtime. While this practice promotes reusability, it introduces significant security risks if the logic governing which file to include is exposed to user manipulation.
Mechanism of File Inclusion
In many languages, inclusion happens during the pre-processing or execution stage. For instance, in C++, a single #include <iostream> directive can expand a few lines of code into thousands after the pre-processor replaces the directive with the actual content of the header file. In web scripting languages like PHP, this process is dynamic, meaning the application can decide which file to load based on logic executed at runtime.
Root Cause of the Vulnerability
A File Inclusion vulnerability arises when an application uses unvalidated user input to construct a path for a file inclusion function. If a developer uses a variable from a URL parameter to determine a file path, an attacker can modify that parameter to point to sensitive system files or even remote malicious scripts.
The core requirements for this exploit are:
- The web application must perform dynamic file inclusion.
- the input path must be controllable by the client.
PHP File Inclusion Functions
PHP provides several functions for including files, each with different behavior regarding failure and redundancy:
Server Configuration Impact
Two primary settings in php.ini dictate the scope of file inclusion vulnerabilities:
- allow_url_fopen: When enabled (On), it allows PHP functions to retrieve data from remote locations via FTP or HTTP.
- allow_url_include: When enabled (On), it allows the
includeandrequirefunctions to load files from remote URLs. This is the primary toggle that enables Remote File Inclusion (RFI).
Exploitation Techniques
1. Local File Inclusion (LFI)
LFI occurs when an attacker accesses files already present on the server's filesystem. This is often used to read sensitive configuration files or system data.
// Vulnerable Code Example
$template = $_GET['layout'];
include("/var/www/html/templates/" . $template);
An attacker might request: index.php?layout=../../../../etc/passwd to read the system's user list.
2. Null Byte Injection (Legacy)
In older versions of PHP (prior to 5.3.4), attackers could bypass forced file extensions using the null byte character (%00). If the code was written as follows:
include($_GET['page'] . ".php");
An attacker could provide page=config.txt%00. The system would treat the string as terminated at the null byte, effectively ignoring the .php extension and loading config.txt.
3. Utilizing PHP Wrappers
PHP provides various I/O wrappers that can be abused during file inclusion:
- file://: Used to access the local filesystem.
Example:?path=file:///etc/hosts - php://filter: Allows an attacker to read the source code of PHP files by encoding them (e.g., Base64) before they are executed.
Example:?path=php://filter/convert.base64-encode/resource=config.php
4. Log Poisoning
If an attacker cannot upload a file, they may "poison" a server log file. By making a request containing PHP code (e.g., <?php phpinfo(); ?>) to a non-existent page, that code is written into the server's access logs. The attacker then uses the LFI vulnerability to include the log file, triggering the execution of the injected code.
5. Image-Based Web Shells
Attackers often hide PHP code inside the metadata of an image file. Since the include function executes any PHP code found within a file regardless of its extension, including a "poisoned" image will trigger the web shell.