PHP Globals And Unregistered Variables.

One of the biggest problems with PHP is certainly register_globals. Once GLOBALS are registered, you are in for a ton of trouble. Today I want to discuss unregistered variables that can be set through register_globals in another method of which I am sure that 99,9% of all PHP developers don't know about. Some PHP developers know about overwriting the $GLOBALS scope, and in order to mitigate global overriding developers create a ridiculous protection mechanism which as I will explain, will fail to work. Please pay attention because I will show you a very subtle trick and difference here which one would assume everyone knows about, but to my amazement do not.

This is common practice by PHP developers to mitigate overwritten GLOBALS:

if ($_REQUEST['GLOBALS']) {
die("GLOBALS Overwrite attack attempt");
}

I've even seen this:

if ( isset( $_REQUEST['GLOBALS'] ) || isset( $_FILES['GLOBALS'] )
|| isset( $_SERVER['GLOBALS'] ) || isset( $_COOKIE['GLOBALS'] )
|| isset( $_ENV['GLOBALS'] ) ) {
die( 'GLOBALS overwrite attempt' );
}

Well, once you have register_globals the above protection mechanisms all fail. Here is why: What many PHP developers do not understand, is that PHP treats all request variables as being initialized as a variable. It expects that the requested variable is set, and if it isn't set, PHP will register it for you if register_globals is being used. This means that you can overwrite GLOBALS by just referencing a query string variable that has the same name as the GLOBAL that is echoed back. Ever wondered why it is called a GLOBAL? exactly.

Here is my PHP mantra:

? == $ or: ?foo == $foo

See a problem yet? I didn't use GLOBALS['foo']=bar as normally seen in exploits, because I don't have too. PHP concerned, the request variable foo is being set and registered. The moment that variable isn't set in your source code, we can overwrite that variable with our own.

?foo=bar

$GLOBALS['foo'] registered as $foo from ?foo=
$_GET['foo'] registered as $foo from ?foo=
$_POST['foo'] registered as $foo if posted.
$_REQUEST['foo'] registered as $foo from ?foo=

To understand this problem, look at the example below:

<?php

?foo=bar

// if the query string contains foo, it will be outputted below:

echo $GLOBALS['foo'];

?>

This will not work, because foo is initialized and registered, which supersede the query string:

<?php

$foo = 'bar';

// will echo bar.

echo $GLOBALS['foo'];

?>

What does it all mean? well, it means that once you still have register_globals turned on and forgot to initialize or register a variable, we can overwrite it. The only way to mitigate this is, is to turn register_globals off because you can't protect this from happening with your self baked functions, because as we just saw the global scope registers the variables if they aren't set, no matter if you use GET, POST, REQUEST or GLOBALS. So what is the impact of this? By doing a Google code query on the use of GLOBALS protection[1], it shows us that at least 100.000 snippets of code from widespread software packages are vulnerable despite their GLOBALS protection.

[1] http://www.google.com/codesearch?q=GLOBALS+Overwrite
source: OWASP News