Title: PHP parse_str() arbitrary variable overwrite Vendor: http://www.php.net/ Advisory: http://www.acid-root.new.fr/advisories/14070612.txt Author: DarkFig < gmdarkfig (at) gmail (dot) com > Written on: 2007/06/12 Released on: 2007/06/12 Risk level: Medium / High [I].BACKGROUND [Quote from php.net] PHP is a popular open-source programming language used primarily for developing server-side applications and dynamic web content, and more recently, other software. The name is a recursive acronym for "PHP: Hypertext Preprocessor". This is actually a retronym; see history of PHP.[/quote] While I was coding the new version of phpsploitclass, I was reading the manual of parse_url(), then I saw the parse_str() function. I decided to see how it works. During some test that I did, I discovered a vulnerability which can be exploited to overwrite some variables. [II].MANUAL void parse_str ( string $str [, array &$arr] ) Parses str as if it were the query string passed via a URL and sets variables in the current scope. If the second parameter arr is present, variables are stored in this variable as array elements instead. Note: Support for the optional second parameter was added in PHP 4.0.3. Note: To get the current QUERY_STRING, you may use the variable $_SERVER['QUERY_STRING']. Also, you may want to read the section on variables from outside of PHP. Note: The magic_quotes_gpc setting affects the output of this function, as parse_str() uses the same mechanism that PHP uses to populate the $_GET, $_POST, etc. variables. [III].SOURCE CODE --- ./ext/standard/string.c --- 4025. /* 4025. {{{ proto void parse_str(string encoded_string [, array result]) 4026. Parses GET/POST/COOKIE data and sets global variables 4026. */ 4027. PHP_FUNCTION(parse_str) 4028. { 4029. zval **arg; 4030. zval **arrayArg; 4031. zval *sarg; 4032. char *res = NULL; 4033. int argCount; 4034. 4035. argCount = ZEND_NUM_ARGS(); 4036. if (argCount < 1 || 4036. argCount > 2 || 4036. zend_get_parameters_ex(argCount,&arg,&arrayArg) == FAILURE) 4036. { ####. /* Not enough or too many args */ 4037. WRONG_PARAM_COUNT; 4038. } 4039. 4040. convert_to_string_ex(arg); 4041. sarg = *arg; 4042. if (Z_STRVAL_P(sarg) && *Z_STRVAL_P(sarg)) { 4043. res = estrndup(Z_STRVAL_P(sarg), Z_STRLEN_P(sarg)); 4043. ####. /* Allocate Z_STRLEN_P(sarg)+1 bytes of memory and copy ####. Z_STRLEN_P(sarg) bytes from Z_STRVAL_P(sarg) to the ####. newly allocated block */ 4044. } 4045. ####. /* parse_str(argv1) */ 4046. if (argCount == 1) 4046. { 4047. zval tmp; 4048. Z_ARRVAL(tmp) = EG(active_symbol_table); 4049. ####. /* The problem is here, there is no conditions before setting ####. the variable. If a variable already exists, it will overwrite it */ 4049. 4050. sapi_module.treat_data(PARSE_STRING, res, &tmp TSRMLS_CC); 4051. } ####. /* parse_str(argv1,argv2) */ 4051. else 4051. { 4052. /* Clear out the array that was passed in. */ 4053. zval_dtor(*arrayArg); 4054. array_init(*arrayArg); 4055. 4056. sapi_module.treat_data(PARSE_STRING, res, *arrayArg TSRMLS_CC); 4057. } 4058. } [IV].EXPLANATIONS As you can see in the manual, the user who want to use this function is not prevented against overwriting. That's why I think that overwriting is not wanted, they forgot to check it. Simple proof of concept: init parse_str($_SERVER['QUERY_STRING']); # [1] => new print_r($array); # ) # ?array=new ############ # Array $array = array('init'); # ( parse_str($_SERVER['QUERY_STRING'],$array); # [array] => new print_r($array); # ) ?> This type of vulnerability can open a door to many vulnerabilities, that's why it's difficult to define the risk level. Unlike extract() there is no option such as EXTR_SKIP which will define what to do if there is a collision. So if you want to secure your code, don't use this function. I didn't contacted the php team but maybe they will release a fix for this vulnerability. [V].GREETZ benjilenoob, ddx39, lorenzo, romano, shaka, sparah.