📜 ⬆️ ⬇️

Code execution in PHP 5.3.9

One of the innovations of the release of PHP 5.3.9 was the introduction of a new configuration parameter max_input_vars, which sets the maximum possible number of input parameters in the request. The new directive is a measure against attacks Hash Collision DoS, which can lead to a denial of service when sending a large number of incoming parameters. However, in the code responsible for limiting the number of parameters in the request, an error was made that leads to remote code execution. The vulnerability was discovered by information security specialist Stefan Esser, known for publishing a number of serious vulnerabilities in PHP, as well as for developing a jailbreak for Apple iOS.

Vulnerability occurs when the number of incoming parameters exceeds the value of max_input_vars (default 1000).


Vulnerable php_register_variable_ex function (php_variables.c@194):
  1. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  2. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  3. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  4. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  5. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  6. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  7. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  8. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  9. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  10. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  11. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  12. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  13. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  14. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  15. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  16. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  17. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  18. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */
  19. PHPAPI void php_register_variable_ex ( char * var_name , zval * val , zval * track_vars_array TSRMLS_DC ) { /* ... */ if ( is_array ) { /* ... */ while ( 1 ) { /* ... */ if ( zend_hash_num_elements ( symtable1 ) <= PG ( max_input_vars ) ) { if ( zend_hash_num_elements ( symtable1 ) == PG ( max_input_vars ) ) { php_error_docref ( NULL TSRMLS_CC , E_WARNING , "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini." , PG ( max_input_vars ) ) ; } MAKE_STD_ZVAL ( gpc_element ) ; array_init ( gpc_element ) ; zend_symtable_update ( symtable1 , escaped_index , index_len + 1 , & gpc_element , sizeof ( zval * ) , ( void ** ) & gpc_element_p ) ; } } /* ... */ symtable1 = Z_ARRVAL_PP ( gpc_element_p ) ; /* ... */

The limitation of the number of parameters is implemented by a cycle with the condition if, and there is no else block that would stop the cycle. As a result, at the exit from the loop, the macro Z_ARRVAL_PP (line 18), which returns a reference to the updated hash table, will get gpc_element_p with the value of the last variable processed in the loop, which leads to the execution of the code.
')
Vulnerabilities are not susceptible to servers with the Suhosin patch extension installed, authored by Stefan Esser. It is noteworthy that on the eve of the linux-distribution Debian abandoned Suhosin patch, thereby exposing its users to a new threat. And the most interesting thing is that the vulnerability is found in the fix of another vulnerability.

Source: https://habr.com/ru/post/137493/


All Articles