Unregistering Globals?

1.2k views Asked by At

I'm having trouble understading this function. I know what register_globals is and how long it has been depreciated from PHP but I'm looking at this code and I'm like, what in the?...

<?php
    //Undo register_globals
    function unregister_globals() {
        if (ini_get(register_globals)) {
            $array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
            foreach ($array as $value) {
                foreach ($GLOBALS[$value] as $key => $var) {
                    if ($var === $GLOBALS[$key]) {
                        unset($GLOBALS[$key]);
                    }
                }
            }
        }
    }
?>

The part in which I'm not understanding is this...

foreach ($array as $value) {
    foreach ($GLOBALS[$value] as $key => $var) {
        if ($var === $GLOBALS[$key]) {
            unset($GLOBALS[$key]);
         }
     }
 }

This foreach loop is cycling through each value in $array we defined then the inner foreach loop is grabbing a super global array from GLOBALS whether it is _REQUEST, _SESSION, _SERVER, _ENV, _FILES, etc... Afterwords it seems like the condional is checking to see if $var is equal to a GLOBAL variable or what not. If so then we'll unset it.

But I'm still having a major difficulty wrapping my head around this one...

UPDATE Here is the snippet of code I'm experimenting with and debugging. What happens if register_globals is on, and a hacker comes barreling along, inserts ?auth=1 into the query string? Will auth be deleted from GLOBALS or will it be echoed out?

if( true ) {
    $globals = array(
        '_COOKIE',
        '_GET',
        '_POST',
        '_REQUEST',
        '_SERVE',
        '_SESSION'
    );

    foreach($globals as $global) {
        foreach($GLOBALS[$global] as $k => $v) {
            /* $GLOBALS['_GET'] on the first loop; */
            /* IF WE SAY, $GLOBALS['app_dir'], WE GET THE VALUE */

            if( $v == $GLOBALS[$k] ) {
                echo "K=> " . $k . "<br />";
                echo "V => " . $v . "<br />";
                echo "GLOB => " . $GLOBALS[$k] . "<br />";
            }
        }
    }

            echo $authorized; // a intentional non-defined variable

    //print_r($GLOBALS);
}

Thanks for your time.

3

There are 3 answers

9
Codist On BEST ANSWER

Now that I was able to see how this actually works, I was able to wrap my head around it. Essentially if users are using PHP 5.3.0 or below and 'register_globals' is enabled, hackers can query common variables names such as 'authorized', 'username', etc to feed in there own values if the variable was already defined by the developer.

Example:

if( is_authorized() ) {
    $auth = 1;
}

if( $auth ) {
    // do authorization code here!!!
} else {
    show_login();
}

You can see $auth is condiotionally defined. If a hacker comes along and tries something like, index.php?auth=1 or index.php?auth=true then PHP will register that as a global value, it'll be checked in the conditional, and our hacker will have access to the application.

I found that...

foreach ($array as $value) {
    foreach ($GLOBALS[$value] as $key => $var) {
        if ($var === $GLOBALS[$key]) {
            unset($GLOBALS[$key]);
        }
    }
}

... actually unsets these values and the hacker won't have access, however, there is also a bug as I previously thought. I have two globals in my framework called $app_dir and $sys_dir pointing to two important directories. If hackers come along and say something like index.php?app_dir=application&sys_dir=system then it'll actually unset those global variables from $GLOBALS. This calls for a potential danger inside of my framework for anyone using it on PHP 5.3.0 or below.

Basically how this works is the conditional checks to see if $var (our value, 'system') is equal to the same value as what's inside $GLOBAL['sys_dir']. Anyone know how to fix this?

10
user4035 On

Yes, this code looks weird:

foreach ($GLOBALS[$value] as $key => $var) {
        if ($var === $GLOBALS[$key]) {//<- ?
            unset($GLOBALS[$key]);
         }
     }

You can imitate what's going in, using a simple array, and make sure, that this if is absolutely useless:

<?php
$data = array(1,2,3);

foreach ($data as $key => $var) {
    var_dump($var);
    var_dump($data[$key]);
    if ($var === $data[$key]) {
        unset($data[$key]);
    }
}

output:

int(1)
int(1)
int(2)
int(2)
int(3)
int(3)

As you can see, the values are equal every time, and in the end $data will be empty.

Update

Here is the script, you can reproduce on your machine:

<?php
extract($_REQUEST);

var_dump($auth);
$array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
foreach ($array as $value) {
    if(isset($GLOBALS[$value]))
    {
        foreach ($GLOBALS[$value] as $key => $var) {
            unset($GLOBALS[$key]);
        }
    }
}
var_dump($auth);

While calling it like this: http://site/script.php?auth=1

It gives the following output for me:

string(1) "1"
<br />
<b>Notice</b>:  Undefined variable: auth on line 14
NULL

So, it seems, that auth variable was destroyed. But $GLOBALS array still contains a lot of data.

Update 2

I think, our error was here:

unset($GLOBALS[$key]);

Why are we unsetting the key of a foreign array. If you do it like this:

$array = array('_REQUEST', '_SESSION', '_SERVER', '_ENV', '_FILES');
foreach ($array as $value) {
    if(isset($GLOBALS[$value]))
    {
        foreach ($GLOBALS[$value] as $key => $var) {
            unset($GLOBALS[$value][$key]);
        }
    }
}

It will empty $_REQUEST, $_SESSION and all the other necessary arrays.

2
nihylum On

Globals ( especially $_GET, $_POST, $_COOKIE, $_ENV, ... and the $GLOBALS-Entries of this Superglobals ) are initially filled by PHP and should never be (over)written oder deleted by any application.

Any manipulation of Superglobal-Values drops the authenticity of the current request and works against the intentional behavior of PHP.

The nested foreach means that a given Array would be iterated ( outter foreach ) and each entry would be iterated too. The Array-Key is served as $key and the array-value as $var