Internet Bug Bounty · Report #73247
php_stream_url_wrap_http_ex() type-confusion vulnerability
How it was found
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:
- the variable still exists
- 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