2012. 8. 13. 21:44

Using php://filter for local file inclusion

I came across a website where the site was vulnerable to LFI (local file inclusion) however the inclusion was done using a require_once and the script appended a .php extension to the end of the file; furthermore it was not vulnerable to null byte injection which meant that if I did include a file that:

  1. The file would have to be valid PHP syntax
  2. I would not be able to see anything contained between <? ?> tags
  3. Anything I could include would be executed.
  4. The file would have to end in the PHP extension

I tried to see if I could include remote files by specifying a URL as the parameter, sadlyallow_url_include was turned off so that failed. When I specified a valid PHP page it simply returned the normal page as expected.

The solution that allowed me to view the source of any PHP file was to use the functionphp://filter/convert.base64_encode/resource which has been available since PHP 5.0.0

1http://www.example.com/index.php?m=php://filter/convert.base64-encode/resource=index

This forces PHP to base64 encode the file before it is used in the require statement. From this point its a matter of then decoding the base64 string to obtain the source code for the PHP files. Simple yet effective..


Once you’ve got the source code for one file you can inspect it for further vulnerabilities such as SQL injections and additional PHP files referenced via include or require.

  • delicious
  • digg
  • facebook
  • linkedin
  • reddit
  • stumble
  • tumblr
  • twitter
This entry was posted in PHP and tagged . Bookmark the permalink.

3 Responses to Using php://filter for local file inclusion

  1. JOhn says:

    That’s pretty slick ;)

    I have a feeling that this can be prevented by using basename();

    1<?php
    2if(isset($_GET['m'])){
    3    $file basename($_GET['m']);
    4    require_once '$file';
    5}

    What are your thoughts on that?

    • Phil says:

      If you just use basename the strings going to end up as “resource=index.php”, checking to see if the file exists (using file_exists) is probably a safer method as it will return false for any php://filter files. A quick preg_match couldn’t hurt either…

      1if (preg_match("/^[A-Z0-9]+$/i"$_GET['m'])) {
      2    if (file_exists($_GET['m'])) {
      3        require_once($_GET['m']);
      4    }
      5}
  2. Frost says:

    Why not just have a white list array, even the `$_GET['m']` could produce unwanted results, and better to not leave it up to that.

    1$whiteList array('index' => 'index.php''about' =>'about.php');
    2if (in_array($_GET['m'], $whiteList)) {
    3      require_once($whiteList[$_GET['m']]);
    4}else {
    5      require_once($whiteList['index']);
    6}

    This way, you can easily default it, you know the files that will be included and you leave nothing up to chance. And, if you wanted to, you could name the names of the actual files different to prevent direct access.

Posted by k1rha