Thursday, July 29, 2010

Hardening PHP: How to securely include remote code (part 1)

First post of the series discussing various methods of including remote PHP code in your application - from security standpoint. In this post we discuss the history of remote code execution vulnerabilities in PHP apps and ways to prevent them. We finish off by presenting an unsecure method of including a remote code and describe what is the problem with that method.


PHP applications from the early days have often suffered from Remote Code Execution vulnerabilities. Some design flaws in the language itself (e.g. allowing including a file from remote URL) and PHP developers' lack of knowledge all contributed to the problem. Common source of it was seen in do-it-yourself CMSes, where you could find snippets like these:
$file = $_GET['page'];
include $file . '.php';
Of course, this obviously has an remote code execution vulnerability - if an attacker used ?page= he can run any code on the server. That is, if PHP is left with default setting of allow_url_fopen. However, changing allow_url_fopen to false prevents you from easily opening all remote files (e.g. images to rescale), not only code, so usually it is left at default value.


Luckily PHP 5.2 introduced new config setting - allow_url_include. It's default value prevents including file from a remote location (only local file protocol URIs are possible):
// PHP 5.2 - allow_url_include = "0"
// you can still open files by URL
echo file_get_contents(''); 
include '/path/to/file.php'; // this is ok
include ''; // this will fail
Thanks to this move, most common remote code execution flaw in PHP applications has been patched. By default, including (and executing) code from remote URI is denied. Sure, it's still possible, but it's usually due to the application authors introducing some funky features to allow for downloading remote code (e.g. for updating plugins etc).

But I need remote code!

It's 2010, the web is now 2.0 - we have distributed applications, cloud computing, ad servers, application components, plugins, social networks, mashups etc. Remote code distribution becomes a popular need of PHP applications. PHP nowadays is not only sitting on one server - the applications are deployed in server farms, in the cloud, distributed to your customer's, run on a desktop - and even in your Android phone!

By allowing remote code you can:
  • create an application with upgradable plugins
  • allow your applications to download it's modules over the wire
  • automatically install additional plugins based on e.g. client's license file
and tons of other things, but ....

In this distributed environment, security becomes crucial. How do I know that the code I've been given (or that I've downloaded) is to be trusted? How do I know it has not been tampered with? Let's first look at how not to do it:

Naive way - hardcoded location

Let's pretend we have a simple scenario:

Here we have 2 servers - the consumer ( and the producer ( Client downloads a PHP code from the server, saves it locally (beating allow_url_include protection) and includes it. The code is downloaded from a hardcoded location, so we should be safe, right? Not really. We blindly trust that the server identified by the domain name is hosting "our" code - but this is not always true:
The client has its DNS entries wrong - effectively downloading code from attackers' location. DNS rebinding attacks this is only one of the methods, one could use man-in-the-middle attack, use some rogue proxy etc.

Don't trust the hostname!

The problem is that we were trusting the server hostname and not the code itself. So if one could act as for our application, we'll blindly accept everything he has to offer.

To protect from these kind of attacks (and securely use remote code) we need to perform some sort of code integrity check before it's execution. If not, we have CWE-494: Download of Code Without Integrity Check vulnerability. How to do the check? Well - check out the second post of the series ;)

No comments: