...очень хочется надеяться, что полученной информацией Вы
воспользуетесь в благих целях. И не кинетесь сломя голову "ломать" все и
вся.
Сегодня скрипты на PERL можно встретить не только на крупном портале, но и на
домашней страничке. Однако мощь языка столь велика, что часто позволяет
злоумышленникам взломать Ваш сервер с помощью Ваших же скриптов. Происходит это
конечно не по вине самого языка, а по причине незнания web-мастером всех
особенностей PERL, и некорректным его использованием.
Предлагаю рассмотреть тект скрипта, который находится ниже:
#!/usr/bin/perl
$file = $ENV{'QUERY_STRING'};
open(FILE, "$file");
my @indata = <FILE>;
close(FILE);
print "Content-type: text/html\n\n";
foreach (@indata) { print $_; }
Работает скрипт следующим образом, берет имя файла из строки запроса и
выводит его на экран.
В чем ошибка? Все прекрасно пока пользователь пользуется ссылками с
Вашего сайта. Однако он может "ручками" изменить строку запроса и тогда увидит
на экране любой интересующий его документ с Вашего сайта. Конечно он должен
знать имя этого документа, но скрипт предоставляет саму возможность просмотра
документа.
К примеру. Пусть на сервере лежит документ secret.txt. На него
естественно нет ни одной ссылки со страниц сервера. Вызов Ваших страниц
организован следующим образом:
www.mysite.ru/script.pl?page1.htm
Так как ссылки на secret.txt с ваших страниц нет, то порядочный
пользователь его и не увидит. Но стоит "плохому юзеру" набрать вручную
www.mysite.ru/script.pl?secret.txt
Как он на экране увидит его
содержимое...
Первое, что приходит в голову, это изменить строку
open(FILE, "$file");
на
open(FILE, "$file.htm");
Что казалось бы позволяет открывать только файлы с расширением ".htm".
Точнее это расширение добавляется к каждой ссылке, и в строке запроса расширение
необходимо опустить. Выглядеть при этом они будут так:
www.mysite.ru/script.pl?page1
Параметр secret при этом превратится в secret.htm, а
secret.txt в secret.txt.htm. И ни один из них не соответствует
файлу secret.txt.
Но если "плохой юзер" наберет
www.mysite.ru/script.pl?secret.txt%00
То он опять увидит на
экране содержимое файла secret.txt...
Почему это происходит? Perl позволяет нулевые символы в качестве
данных содержащихся в переменной. В отличии от C NUL не является конечным
символом строки. Но лежащие ниже вызовы системы/ядра написаны на "С", который
распознает NUL как разделитель строки. Что получается в результате? Скрипт
получает secret.txt%00. Преобразует его в secret.txt%00.htm. Далее
Perl передает secret.txt\0.htm, но лежащие ниже библиотеки
останавливаются когда встречают первый NUL. То есть имя файла превратится в
secret.txt...
Решение. Нужно удалить нули в строке запроса. К примеру так:
#!/usr/bin/perl
$file = $ENV{'QUERY_STRING'};
$file =~ s/\0//g;
open(FILE, "$file.htm");
my @indata = <FILE>;
close(FILE);
print
"Content-type: text/html\n\n";
foreach (@indata) { print $_; }
Теперь если "плохой юзер" наберет
www.mysite.ru/script.pl?secret.txt%00
То скрипт получив
secret.txt%00. Преобразует его сначала в secret.txt, обрезав нуль.
Далее добавляет расширение, и имя файла превратиться в secret.txt.htm,
которое к нашей радости не соответствует secret.txt!
Ошибка вторая. Скрипт позволяет обращаться к файлам более высокого
уровня. Не секрет, что абсолютное большинство "плохих юзеров" интересует
содержимое /etc/passwd. Название говорит само за себя (для пользователей
UNIX). А что бы туда попасть обычно вполне достаточно, применительно к нашему
скрипту, набрать нижеследующее:
www.mysite.ru/script.pl?../../../../../etc/passwd
Дело в том, что каждая последовательность "../" выводит нас на один
уровень выше. И после нескольких таких обращений мы попадаем в корень диска. А
от туда прямая дорога в /etc/passwd.
Решение. Запретить обратный ход в директориях можно следующим образом:
#!/usr/bin/perl
$file = $ENV{'QUERY_STRING'};
$file
=~ s/\0//g;
$file =~ s/\.\.\///g;
open(FILE, "$file.htm");
my @indata = <FILE>;
close(FILE);
print "Content-type: text/html\n\n";
foreach (@indata) { print $_; }
В этом случае запрос
www.mysite.ru/script.pl?../../../../../etc/passwd
превратится в
www.mysite.ru/script.pl?etc/passwd
Что, согласитесь, не одно и тоже. И "плохой юзер" ничего не увидит.
Ошибка третья. Если файл открывается на чтение, то лучше делать это
принудительно, а именно:
open(FILE, "<$file.htm");
Если не указать знак меньше "<", то по умолчанию файл так же
откроется на чтение. Но, как вы наверное догадываетесь, это самое "по умолчанию"
иногда можно обойти. Вставив всего один символ "меньше" вы защитите и себя и
свой сервер от кучи неприятностей.
Рекомендую всем использующим PERL-скрипты проверить их на наличие
вышеописанных "дыр". Даже если у Вас нет никакой секретной информации. Как
минимум есть что скрывать вашему хостинг-провайдеру. Конкретно, имена
пользователей и пароли.
P.S. Статья не описывает все "дыры" PERL-скриптов. А указывает на
самые распространенные ошибки и дает общие рекомендации по их устранению.