The PHP Protocol, Filters and Local File Inclusion

PHP-logo.svg

Andrew wrote up some notes for our internal blog about an experience he had on a recent Capture the Flag (CTF) event. I thought they were interesting so we talked and decided to republish them here.

<Andrew>

I came across an interesting twist on exploiting a PHP local file inclusion vulnerability while participating in a CTF. LFIs are the little sister vulnerability to remote file inclusion (RFI), which is when an attacker can tell an application to execute arbitrary code from another file. The distinction with an LFI is it will only read and execute from files that are already on the server.

Developers usually create these vulns by allowing input from the client to decide the destination of an include() function call, which among other things is useful in templating out code. An example would be:

$page = $_GET[‘page’];

include(“app/schedule/” . $page . “.php”);

The developer’s intention is to pull in schedule code into another page, perhaps onto a sidebar. An attacker who notices this can specify their own page parameter to point to whatever file they want to be included. If an attacker can manage to upload code to the server, they can use this flaw to execute that code and probably compromise the machine. If not, they can still try to include files they’re not supposed to, such as /etc/passwd, but if the web server’s account is locked down, it might not have read-access to access all the interesting files an attacker might want.

If the system is locked down then it seems like the risk here is somewhat mitigated, but PHP 5 has a fun little feature that can keep LFI in the game. PHP offers a protocol for accessing streams called ‘php://’. This protocol can do such things as read from the standard out stream with ‘php://stdout’. Interestingly, it can also apply filters to the stream, such as a filter which compresses the data as it passes through the stream.

We can take advantage of PHP’s base64-encode converter to tell PHP to base64-encode the php file before including it. The input would appear as such:

&page=php://filter/convert.base64-encode/resource=main

This would get parsed into this include function call as:

include(“app/schedule/php://filter/convert.base64-encode/resource=main.php”);

PHP does not parse base64-encoded text, so the entire base64 blob gets outputted onto the page as text. At this point an attacker is one base64 decode away from having the PHP source code to the application.

While the machine hasn’t been rooted yet, having the PHP source code offers all sorts of new attack options. From personal experience, I’ve found PHP developers rapidly sober up at the prospect of their source code being leaked. Features such as these is the reason the optimal fix for LFIs in PHP is to avoid it entirely in design by never using client-controlled input inside of an include.

</Andrew>

Contact us to talk about ways you can build secure PHP applications and test the security of the PHP applications you have deployed.

–Dan

dan _at_ denimgroup.com

@danielcornell

About Dan Cornell

A globally recognized application security expert, Dan Cornell holds over 15 years of experience architecting, developing and securing web-based software systems. As the Chief Technology Officer and a Principal at Denim Group, Ltd., he leads the technology team to help Fortune 500 companies and government organizations integrate security throughout the development process. He is also the original creator of ThreadFix, Denim Group's industry leading application vulnerability management platform.
More Posts by Dan Cornell

One Response to “The PHP Protocol, Filters and Local File Inclusion”

  1. Weight Loss

    Everything is very open with a very clear clarification of the challenges. It was really informative. Your website is very helpful. Many thanks for sharing!

Leave a Reply

Your email address will not be published. Required fields are marked *