Информативная ошибка. Манипуляция параметрами php

2010-10-10T00:00:00
ID RDOT:789
Type rdot
Reporter ficrowns
Modified 2010-10-10T00:00:00

Description

Взлом – это не русский язык, а скорее.. химия. Почему? Да просто потому, что именно способами броуновского смешивания реагентов можно получить синтетическую взрывчатку =) Да-да, именно так и есть.

Как правило, при атаке на какой-то хост или сайт любой хакер (речь идет не о злоумышленниках) пытается сначала проверить общеизвестные истины, однако, если администратор/пользователь хоста не является полным профаном, то простые истины не дают ожидаемого результата. Именно тогда и приходится прибегнуть к нестандартным решениям, которые и открывают нашему взору зияющее отверстие, размером с люк, в которое и слона можно протиснуть, если постараться. Посему, любой хакер в первую очередь должен быть превосходным аналитиком и уметь быстро соображать, но, собственно, речь не об этом.

Сегодня я хотел бы остановиться на одном весьма интересном способе получения информации о скриптах (читай, веб-приложениях). Способ является некой medium point между обычными способами сбора инфы и нестандартными. Должен заметить, что он довольно часто оказывается весьма действенным (проверенно при многочисленных тест-пинках инет ресурсов). Последнее время он приобретает популярность из-за того, что размножающиеся как кролики горе-админы не понимают разницы между дефолтными настройками и грамотными.

Итак, на чем же основан данный метод? Все очень просто: метод основан на изучении ответов сервера при превышении лимитов времени, при обработке запроса. Согласитесь, довольно несложно, учитывая, что ответы содержат абсолютный путь к скрипту, который протянул резину и не справился с выполнением задачи вовремя.

PHP
На какие же параметры стоит опираться при проведении подобной атаки? Есть несколько таких, которые присутствуют в дефолтных настройках php интерпретатора на сервере и остаются нетронутыми после настройки сервисов и запуска железки в эксплуатацию.

Самые главные – это:

Код:

* max_execution_time
* max_input_nesting_level
* max_input_time
* memory_limit max_execution_time
* max_input_nesting_level
* max_input_time
* memory_limit

Так же следует обратить внимание на эти:

Код:

* pcre.recursion_limit (PHP, версии 5.2.0 и выше)
* post_max_size (PHP, версии 4.0.3 и выше)
* upload_max_filesize
* max_file_uploads (PHP, версии 5.2.12 и выше)

В помощь вам урла <http://php.net/manual/en/ini.list.php> и возможно найдете еще что-нибудь полезное. Исходя из соображений универсальности можно сделать вывод, что лучше всего подходят опции memory_limit и max_execution_time. Если имеются настройки display_errors=On и reporting=E_ERROR – то они выбрасываются в тело ответа сервера.

Подготовка
Для проведения подобной атаки необходимо знать максимальную длину GET-запроса. Это можно сделать при помощи скрипта. Погуглив я нашел отличный вариантик подобной игрушки на ксакепе (автор Владимир D0znpp Воронцов):

PHP код:

function fuzz_max_uri_len($url){
$headers = array();
$data = array();
$left = 500; //Значение левого края искомого диапазона
$right = 64000; //Значение правого края искомого диапазона
$accur = 5; //Точность, с которой определяем значение
while (($right-$left) > $accur){
$cur = ($right+$left)/2;
$data['x'] = str_repeat("x",$cur);
list($h,$c,$t) = sendGetRequest($url, $headers, $data);
$s = intval(substr($h,9,3));
if ($s<400){
$left=$cur;
}
else{
$right=$cur;
}
echo "\n$cur\t$s";
}
return(($right+$left)/2);
}

Скрипт работает по методу дихотомии и определяем максимальную длину запроса, установленную веб-сервером.

Далее нам нужно выяснить значение параметра max_input_nesting_level, который по умолчанию равен 64. Он определяет максимальную размерность массива. Взгляните на строчку кода:

PHP код:

<?php echo $_GET[‘a’]; ?>

Если max_input_nesting_level=1, то в запросе передастся 'a', а на экране мы получим «ничего», т.к. в интерпретаторе возникнет ошибка Notice, говорящая о том, что переданная переменная необъективна. Если значение будет выставлено на 2, то на экране мы увидим “Array”. Как видите, это довольно простой алгоритм: увеличиваем значение, пока не исчезнет надпись “Array”. Вся соль в том, что при постепенном увеличении значения мы получаем ответ, а при превышении порогового размера ответа возникает ситуация, которая расценивается как аномальная и записывается в лог – вот вам готовый алгоритм скрипта, который поможет облегчить процесс анализа.

Прожорливость

Итак, наша задача, напомню, вызвать «Allowed memory size exhausted». Хотите посмеяться? Прошу:

PHP код:

<?php echo ‘OK’;?>

Вот этот самый скрипт может поедать оперативку сервера мегабайтами. Для вывода размера используемой памяти используется параметр memory_get_usage(), берем и добавляем его в наш код. После вызываем скрипт не при помощи переменной, а через GET-запрос (это +1Кб памяти). Напомню, что мы должны прибрать к рукам как можно больше памяти, при этом надо не только не привысить максимальную длину GET, но и сделать его как можно короче. Передаем запрос с параметром 'a' (+ еще 500 байт), а после беремся за вышеописанный max_input_nesting_level – как только размерность массива превысит установленный лимит, потребление памяти будет случайной.

Чтобы промониторить объем потребляемой памяти для любого приложения нужно воспользоваться скриптом:

PHP код:

<?php echo “marker:”.memory_get_usage().”#”; ?>

И добавить его в auto_append_file в php.ini

Теперь воспользовавшись функцией представленной ниже можно добиться того, что она будет искать в ответе сервера соответсвующий маркер и получать значение потребляемой памяти:&lt;/p&gt;<br />

PHP код:

function findMarker($content){
$p1 = strpos($content, "ONsec E500 mem:");
if ($p1===false){
return 0;
}
else{
$p2=strpos($content,"#",$p1);
if ($p2===false){
return 0;
}
else{
$mem=substr($content,$p1+15,$p2-$p1-15);
}
}
return intval($mem);
}

Теперь ищем на сервере те скрипты, которые любят кушать память. На вскидку это довольно тяжело определить, но если обратить внимание на циклы с обработкой массивов, рекурсии и т.п. – получится.

Медлительность, как уязвимость

Стоит совершенно справедливо заметить, что веб-сервера – это не двк а посему, поедание памяти и время выполнение скрипта зависит от нагрузки на сервер в данный момент времени. Следовательно, все это весьма непостоянно, однако, ситуацию, в которой время выполнения скрипта увеличится можно создать искусственно. Есть и соответствующий класс уязвимостей в OWASP – dead_code. Он как раз и включает в себя уязвимости разработки, которые позволяют злоумышленнику провести взлом, вызвав ошибку «промедления», если так можно выразится.

Вариантов вызвать такое большое множество, но все зависит от конкретного сайта, т.к. большинство подобных ошибок возникает из-за сторонних скриптов, а не из-за движка-каркаса сайта. Тем не менее, покормив ресурс разного рода запросами и их вариациями можно понять, на что делать ставку в первую очередь.

Анализ

После того, как нужное количество ответов имеется у нас в наличии, можно приступать к разбору результатов. Все что нужно – это выделить из ответов пути и расположить ответы по уровням, в зависимости от длинны. На том же ксакепе я нашел отличный код, который решит проблемку (автор тот же):

PHP код:

function parseResults($dir){
if (is_dir($dir)) {
if ($dh = opendir($dir)) {
$i=0;
$results = array();
while (($file = readdir($dh)) !== false) {
$curFile = $dir.$file;
$fh = fopen($curFile, 'r');
$filedata = fread($fh, filesize($curFile));
$fsize = filesize($curFile);
$p1 = strpos($filedata,"Maximum execution time of ");
if ($p1 === false) {}
else{
$p2 = $p1+52;
$p3 = strpos($filedata,"</b>",$p2);
if ($p3 === false) {}
else{
$len = $p3-$p2;
$path = substr($filedata,$p2,$len);
$unique = true;
//Проверяем на уникальность
foreach($results as $key=>$value){
if ($value['path']==$path){
$unique=false;
break;
}
}
if ($unique){
$len = $p3-$p2;
$res = array( 'path'=>substr($filedata,$p2,$len),'len'=>$fsize);
$results[$i]=$res;
$i++;
}
}
}
fclose($fh);
}
closedir($dh);
$size=count($results)-1;
//Сортируем результаты по длине
for ($i = $size; $i>=0; $i--) {
for ($j = 0; $j<=($i-1); $j++)
if ($results[$j]['len']>$results[$j+1]['len']) {
$k = $results[$j];
$results[$j] = $results[$j+1];
$results[$j+1] = $k;
}
}
return $results;
}
}
}

На выходе, как утверждает автор скрипта, мы получим упорядоченный массив с длинами ответов и адресами/именами скриптов, в которых возникла ошибка.

Вот такой вот способ получения информации путем провокации ошибки. Это далеко не единственный вариант, как все известно. Однако, он мне нравится своей оригинальностью – интересно таким образом получать нужные сведения для тестирования ресурсов.

В конце статьи я хотел бы напомнить, что использование данной информации в злонамеренных целях повлечет за собой уголовную ответственность согласно соответствующей статьи УК. Если кто либо решиться совершить злодеяние, то ни автор статьи, ни редакция не несет никакой ответственность, т.к. данный материал опубликован как руководство для тестирования интернет сайтов на наличие уязвимостей.

SASecurity group
11.08.2010