Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Уязвимости (https://forum.antichat.xyz/forumdisplay.php?f=74)
-   -   PHP parse_str() arbitrary variable overwrite (https://forum.antichat.xyz/showthread.php?t=48504)

blackybr 07.09.2007 08:57

PHP parse_str() arbitrary variable overwrite
 
Удаленная перезапись переменной в ф-ии parse_str() в PHP.

Оригинал http://www.acid-root.new.fr/advisories/14070612.txt

[I]. Вступление

Цитата:

PHP.Net: PHP Это популярный язык программирования с открытым исходным кодом, который в первую очередь используют для разработки серверных приложений, создания динамического вэб-контэнта и другого софта.
Название PHP:Гипертекстовый процессор является рекурсивным акронимом. Более того это ретроним (ретроним - это обозначение, состоящее из существительного и дополнения к нему, имеющего тоже самое значение что и сущ). Смотрите историю пхп.

[II]. Руководство.

Функция void parse_str ( string $str [, array &$arr] ) парсит строку str, которая должна иметь формат типа URL и присваивает значения переменным в текущем контексте, если не передан второй аргумент arr. В последнем случае значения будкт сохранены в этой переменной как элементы массива.

Замечание: Поддержка необязательного второго аргумента была добавлена в PHP 4.0.3.

Замечание: Для получения текущей строки запроса (QUERY_STRING) может быть использована переменная $_SERVER['QUERY_STRING']. Также ознакомьтесь с разделом "Переменные вне PHP".

Замечание: Опция magic_quotes_gpc влияет на выходящее значение ф-ии, т.к. parse_str() использует тот же механизм, что пхп использует для заполнения $_GET, $_POST.

[III]. Исходный код.

Код:

--- ./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]. Пояснения

Как вы уже увидели в мануале, пользователь, использующий эту ф-ию не защищен от перезаписи переменных. Девелоперы просто забыли проверить этот момент. Простой PoC:
PHP код:

<?php
 
 
# ?var=new
 ###########
 
$var   'init';                     #
 
parse_str($_SERVER['QUERY_STRING']); #
 
print $var;                          # new

 # ?array[]=new                       # Array
 ##############                       # (
 
$array = array('init');              #    [0] => 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);                            # )

 
?>


Этот тип уязвимости открывает дверь к многим другим уязвимостям, поэтому в данном случае достаточно сложно обозначить уровень риска. В отлчиие от extract(), тут нет опции на подобии EXTR_SKIP, которая отвечает за защиту от перезаписи переменной (если переменная с таким именем существует, будет сохранено её прежнее значение). Поэтому если вы хотите чтобы ваш код был безопасным - не используйте эту ф-ию. Я не связывался с пхп-тимой, но возможно они зарелизят фикс на эту уязвимость.

Избранные мысли из [V]. Комменты

1. Даже если register_globals отключено, уязвимость есть как минимум на пхп 4.4.4

2. Hardened-PHP и PHP с сухосин патчем тоже уязвимы.

3. При тесте уязвимости она имело место на
Цитата:

Apache/2.0.59 (FreeBSD) DAV/2 PHP/4.4.7 with Suhosin-Patch
и
Apache/2.2.4 (Darwin) PHP/5.2.3

Helios 07.09.2007 16:33

Сколько двигов перерыл, но что-то эту функцию видел в применении всего несколько раз. Так что особой "опасности" и чего-то сверхестественного тут не вижу. Просто нужно думать, когда используешь. // и не только тут)

Xex 11.09.2007 03:06

2blackybr:маладец, спасибо, только одно замечание - если дословный перевод получается бредом, то можно смело высказать мысль своими словами.

RE:"Замечание: Опция magic_quotes_gpc влияет на выходящее значение ф-ии, т.к. parse_str() использует тот же механизм, что пхп использует для заполнения $_GET, $_POST."



2Helios:
RE:"Сколько двигов перерыл, но что-то эту функцию видел в применении всего несколько раз. Так что особой "опасности" и чего-то сверхестественного тут не вижу." - оттого что ты не нашел применение данной уязвимости, опасность и актуальность данной баги не уменьшается


Время: 15:56