Запрошенная Вами страница является устаревшей. В связи с недавней
реструктуризацией проекта
вы могли попасть на эту страницу со
стороннего сервера по ссылке, утратившей актуальность. Рекомендуем Вам перейти к заглавной странице нашего сервера и поискать требуемый вами материал в архиве проекта
СТАТЬЯ - 11 / 11 / 99
УЧИМСЯ ПИСАТЬ БЕЗОПАСНЫЕ CGI-СКРИПТЫ
Данный текст - статья из журнала Phrack #55. Автор статьи - .Rain.forest.puppy., с чьего любезного разрешения Team Void и представляет Вам этот материал. Сильно рекомендуется ознакомиться с данным материалом всем писателям на перле - ведь из-за мелкой ошибки в скрипте, который вы пишете по заказу клиента, он может стать жертвой злых хакеров и предъявить Вам иск ;)
ВСТУПЛЕНИЕ
Я считаю, что неплохо было бы написать некое подобие вступления. Итак, по большей части я занимался написанием и анализом различныый скриптов, и пытался выяснить - каким образом можно использовать небольшие ошибки в сових целях ? Мм.. эта статья как раз рассказывает об этом.
ЯДОВИТЫЙ БАЙТ 0x00
Когда строка "root" не равна "root", но, одновременно, равна ? (вы смущены ? я имею в виду разные языки программирования). Однажды я решил выяснить, что же на самом деле позволяет перл - и могу ли я использовать в своих целях ошибки компилятора ? Итак, я начал передавать совершенно странные данные различным системным вызовам. Ничего осбенного не произошло, за исключением одной вещи...
Как вы видите ниже, требоваось открыть указанный файл, rfp.db. Я использовал http-запрос, чтобы передать скрипту значение rfp, пришил к нему расширение и затем открыл файл. На Перле рабочий скрипт выглядит примерно так:
Отлично. Я передал user_input=rfp, скрипт пытается открыть файл rfp.db. Довольно просто (про пути типа /../../../../ мы поговорим позже).
Затем я попытался передать скрипту значение user_input=rfp%00. Перл создал $database="rfp\0.db и попытался это открыть. В результате был открыть файл rfp (или был бы открыт, если бы он существовал). Куда же делось расширение ? Это интересная часть.
Как вы видите, перл допускает нулевые байты в строковых переменных как данные. В Си же нулевой байт - признак конца строки. Итак, "root" != "root\0". Но все системные вызовы программированы в Си, где ноль есть признак конца строки. А результат - перл передаёт системным вызовам параметр rfp\0.db, но все вызываемые библиотеки и функции перестают обрабатывать данные, как только доходят до нуля.
Что же мы получим, если попытаемся написать скрипт, который позволяет администратору менять пароли у различных пользователей КРОМЕ рута ? Код будет примерно таков:
$user=$ARGV[1] #
if ($user ne "root"){
# осуществить дальнейшие операции }
Итак, если теперь некто, пользующийся этим скриптом скажет - поменять паpоль у пользователя root - то ничего не произойдёт. Если же он поменяется поменять пароль у пользователя root\0 = то тест будет пройден, но все вызовы, которые делает часть скрипта, меняющая пароль, будут считать что операция выполняется над пользователем root.
Но в общем - это не есть очень опасная уязвимость - но не стоит забывать об этом. К примеру, существует куча скриптов, которые добавляют расширение .html к полученным данным. К примеру, простейший каталогизатор может добавлять .html к номеру, который вводит пользователь, т.е. page.cgi?page=1 покажет мне 1.html. Отчасти безопасно, т.к. любой окрываемый файл должен иметь расширение .html. Но - если мы пошлём скрипту параметр page.cgi?page=page.cgi%00 (%00 == '\0' escaped), то скрипт попросту покажет своё содержимое. А такой эксплоит
$file="/etc/passwd\0.txt.whatever.we.want";
die("hahaha! Caught you!) if($file eq "/etc/passwd");
if (-e $file){
open (FILE, ">$file");}
откроет файл /etc/passwd для чтения. Решение проблемы - очень порстое - просто отфильтруйте все нулевые символы : $insecure_data=~s/\0//g;
Не забудьте так же отфильтровать остальные символ, перенаправляющие потоки данных. Список их (как сказано на официальном сайте W3C.ORG) -
&;`'\"|*?~<>^()[]{}$\n\r
Так же не стоит забывать про бэкслеш (\). Представьте себе, если вдруг пользователь передаст вашему скрипту данные user data `rm -rf /`. После того, как они будут вычищены процедурой очистки от нежелательных символов, это будет что-то вроде user data \\`rm -rf / \\`. Итак, что что находится в кавычках - останется невычищенным и запустит `rm -rf / \`. Итак - следует так же вычищать и двойные бэкслеши (это так же приведёт к невозможности осуществить просмотр директории по пути вида /../../../. (s/\.\.//g;).
Итак, /usr/tmp/../../etc/passwd превратится в /usr/tmp///etc/passwd, что не сработает. Но - теперь займёмся бэкслешем. Строка /usr/tmp/.\./.\./etc/passwd не будет распознана фильтром, как не соответсвующая правилам - и будет передана скрипту далее. Теперь, чтобы использовать это имя файла в перле, требуется что-то вроде
Но всё вышеописанное работает лишь на системных вызовах и вызовах, заключенных в кавычки (''). Опция -e не позволит открытым (non-piped) функциям работать.
$file="/usr/tmp/.\\./.\\./etc/passwd";
open(FILE, "<$file") or die("No such file");
завершит работу с сообщением NO SUCH FILE.
Решение таких проблем простое - избавляйтесь от бэкслешей.
ПАЙПЫ (|)
В перле добавление пайпа к имени открываемого файла запустит его, а не откроет. Итак,
open(FILE, "/bin/ls") даст вам кучу исполняемого кода, тогда как open(FILE, "/bin/ls|") исполнит ls и даст вам желаемый результат. фильтр s/(\|)/\\$1/g предотвратит это (перл будет завершать работу с сообщением - unexpacted end of file) - так как sh ожидает, что следующая строка будет начинаться с \.
Итак, теперь можно попытаться использовать это в комплексе с тем, о чём мы говорили ранее. Предположим, у нас в скрипте есть строка open(FILE, "$FORM"). Установив $FORM в ls| - мы получим листинг директории. Теперь подумайте, что мы имеем конструкцию вроде
Мы должны точно указать директорию, где находится ls - посему мы устанавливаем $FROM в "../../../../bin/ls|", что даёт нам листинг директории.
Итак - теперь у нас ситуация, немного более сложная
Требуется одурачить опцию -e. Проблема в том, что "-е" прекратит работу фонкции, как только удостоверится, что файл, который надо открыть, заканчивается пайпом (|). Итак - нам надо сделать пайп невидимым для -е, но добиться того, чтобы перл по прежнему знал о его сущуствовании. Для этого мы будем использовать тот самый нулевой байт. Итак - мы открываем что-то вроде ls%00| , -е оканчивает обработку данный на нулевом байте, и команда ls исполняется!
Итак, код
Это сработает, но мы получим листинг текущей папки - (простое ls) - /etc не воспринимается как аргумент.
Я так же хотел сделать замечание всем программистам - если вы ленивые программисты на перле - дважды перепроверьте ваши программы на подобные вещи. Так же помните об управлении потоками даных. Есть символы < и > - которые не позволяют, к приемру, перенаправить анные в работающий ls, но вполне нормально осуществляют запись в открытый файл. Вкратце -
$bug="ls|"
open(FILE, $bug)
open(FILE, "$bug")
сработает. Но
open(FILE, "<$bug")
open(FILE, ">$bug")
open(FILE, ">>$bug")
не сработает и всё будет в порядке. Если вам надо только читать чтолибо из файла, просто открывайте <$file, а не $file.
Известный рассадник дырявых скриптов www.freecode.com изобилует примерами небезопасных скриптов. Не берите оттуда ничего. Рассмотрим несколько примеров:
Скрипт-менеджер рекламных объявлений
Это первый скрипт с www.freecode.com.
# First version 1.1
# Dan Bloomquist dan@lakeweb.net
Автор передаёт все параметры, с которыми был вызван скрипт в %DATA. Он не очищает '..', не очишает и нулевые байты. Итак, поглядим код...
#This sets the real paths to the html and lock files.
#It is done here, after the POST data is read.
#of the classified page.
$pageurl= $realpath . $DATA{ 'adPath' } . ".html";
$lockfile= $realpath . $DATA{ 'adPath' } . ".lock";
Используя 'adPath=/../../../../../etc/passwd%00' - мы можем указать $pageurl на файл с паролями. Посмотрим на $lockfile. Мы не можем использовать пайп для наших нужд - так как расширение (.html) добавляется в последнюю очередь.
#Read in the classified page
open( FILE,"$pageurl" ) || die "can't open to read
$pageurl: $!\n";
@lines= ;
close( FILE );
Тут в $pageurl заносится имя файла, коорый затем открывается. К счастью для автора, он немедленно окрывает $pageurl на запись. Итак, мы должны иметь права на запись в то, что, вообще-то пытаемся открыть на чтение. Это ограничивает наши возможности использования данной уязвимости, но это есть живой пример того, как сам того не зная, автор сильно связал нам руки в раскалывании серверов через его скрипт.
Весьма интересно открывается и мейлер.
#Send your mail out.
#
open( MAIL, "|$mailprog $DATA{ 'adEmail' }" )
|| die "can't open sendmail: $adEmail: $!\n";
Ох.. это мы видели уже сто раз. Не проверяются ни символы перенаправления потока данных, ни пайпы.. мы можем выполнить любую команду через это.
Так же я нашёл простенький логгер данных, которые пользователь вводит в веб-форму.
# flexform.cgi
# Written by Leif M. Wright
# leif@conservatives.net
Итак, входные данные передаются в %CONTENTS, и опять никакой поверки на подозрительные символы. Затем
Используя стандартный выход за пределы папки, куда нас посадили (/../../) , нам даже не придётся использовать нефильтрацию 0x00. Но что бы мы не открыли - файл открывается для дописывания, и поэтому мы должны иметь права на запись в тот файл, который пытаемся открыть. По этой же причине не сработает и pipe (|) bug.
ОКОНЧАНИЕ
Итак, на сегодня обзор недокументированных возможностей различных скриптов, и рассказы о том, как их сделать более безопасными, окочнены. Спасибо всем за внимание!
.rain.worest.puppy. [ADM/Wiretrip] rfp@wiretrip.net - duke not foundУважаемый пользователь !
Запрошенная Вами страница является устаревшей. В связи с недавней
реструктуризацией проекта
вы могли попасть на эту страницу со
стороннего сервера по ссылке, утратившей актуальность. Рекомендуем Вам перейти к заглавной странице нашего сервера и поискать требуемый вами материал в архиве проекта