A type confusion vulnerability was found in the PHP_to_XMLRPC_worker() function of PHP's XMLRPC extension. A remote attacker could exploit this flaw to disclose server memory or crash a PHP application.
It was found that an attacker can control type and val via get_zval_xmlrpc_type() with a crafted object-type ZVAL. Z_STRVAL_P macro and the Z_STRLEN_P macro handles a non-string-type val, which is able to look up an arbitrary memory address. This results in leaking arbitrary memory blocks, crash application or other issues.
Vulnerable code:
static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int depth TSRMLS_DC)
{
XMLRPC_VALUE xReturn = NULL;
if (in_val) {
zval* val = NULL;
XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(in_val, &val);
if (val) {
switch (type) {
case xmlrpc_base64:
if (Z_TYPE_P(val) == IS_NULL) {
xReturn = XMLRPC_CreateValueEmpty();
XMLRPC_SetValueID(xReturn, key, 0);
} else {
xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
}
break;
case xmlrpc_datetime:
convert_to_string(val);
xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
break;
...
XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue) /* {{{ */
{
XMLRPC_VALUE_TYPE type = xmlrpc_none;
TSRMLS_FETCH();
if (value) {
switch (Z_TYPE_P(value)) {
...
case IS_OBJECT:
{
zval** attr;
type = xmlrpc_vector;
if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
if (Z_TYPE_PP(attr) == IS_STRING) {
type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
}
}
break;
}
}
/* if requested, return an unmolested (magic removed) copy of the value */
if (newvalue) {
zval** val;
if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
*newvalue = *val;
}
} else {
*newvalue = value;
}
}
Upstream patch:
http://git.php.net/?p=php-src.git;a=commit;h=f3c1863aa2721343245b63ac7bd68cfdc3dd41f3
Upstream bug (contains reproducer):
https://bugs.php.net/bug.php?id=70728
It was found that an attacker can control type and val via get_zval_xmlrpc_type() with a crafted object-type ZVAL. Z_STRVAL_P macro and the Z_STRLEN_P macro handles a non-string-type val, which is able to look up an arbitrary memory address. This results in leaking arbitrary memory blocks, crash application or other issues. Vulnerable code: static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int depth TSRMLS_DC) { XMLRPC_VALUE xReturn = NULL; if (in_val) { zval* val = NULL; XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(in_val, &val); if (val) { switch (type) { case xmlrpc_base64: if (Z_TYPE_P(val) == IS_NULL) { xReturn = XMLRPC_CreateValueEmpty(); XMLRPC_SetValueID(xReturn, key, 0); } else { xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val)); } break; case xmlrpc_datetime: convert_to_string(val); xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val)); break; ... XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue) /* {{{ */ { XMLRPC_VALUE_TYPE type = xmlrpc_none; TSRMLS_FETCH(); if (value) { switch (Z_TYPE_P(value)) { ... case IS_OBJECT: { zval** attr; type = xmlrpc_vector; if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) { if (Z_TYPE_PP(attr) == IS_STRING) { type = xmlrpc_str_as_type(Z_STRVAL_PP(attr)); } } break; } } /* if requested, return an unmolested (magic removed) copy of the value */ if (newvalue) { zval** val; if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) { if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) { *newvalue = *val; } } else { *newvalue = value; } } Upstream patch: http://git.php.net/?p=php-src.git;a=commit;h=f3c1863aa2721343245b63ac7bd68cfdc3dd41f3 Upstream bug (contains reproducer): https://bugs.php.net/bug.php?id=70728