This file: ftp://ftp.cert.org/pub/tech_tips/cgi_metacharacters

Last revised November 13, 1997
Version 1.3
---------------------------------------------------------------
    http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html

    The following book contains useful information including sections on
    secure programming techniques.

    _Practical Unix & Internet Security_, Simson Garfinkel and
    Gene Spafford, 2nd edition, O'Reilly and Associates, 1996.

    Please note that the CERT/CC and AUSCERT do not endorse the URL that
    appears above. If you have any problem with the sites, please contact
    the site administrator.

    Another resource that sites can consider is the CGI.pm module. Details
    about this module are available from:

    http://www.genome.wi.mit.edu/ftp/pub/software/WWW/cgi_docs.html

    This module provides mechanisms for creating forms and other web-based
    applications. Be aware, however, that it does not absolve the programmer
    from the safe-coding responsibilities discussed above.



    Copyright 1997 Carnegie Mellon University. Conditions for use, disclaimers,
    and sponsorship information can be found in
    http://www.cert.org/legal_stuff.html and ftp://info.cert.org/pub/legal_stuff .
    If you do not have FTP or web access, send mail to cert@cert.org with
    "copyright" in the subject line.

    CERT is registered in the U.S. Patent and Trademark Office.




      Author: Rain Forest Puppy, http://www.wiretrip.net
      Оригинал статьи лежит на сайте Team Void.Ru
      © Copyright Rain Forest Puppy
      © Copyright Team Void, перевод
      ---------------------------------------------------------------



        Phrack #55. Автор статьи - .Rain.forest.puppy., с чьего любезного разрешения Team Void и представляет Вам этот материал. Сильно рекомендуется ознакомиться с данным материалом всем писателям на перле - ведь из-за мелкой ошибки в скрипте, который вы пишете по заказу клиента, он может стать жертвой злых хакеров и предъявить Вам иск ;)





          ВСТУПЛЕНИЕ



        Я считаю, что неплохо было бы написать некое подобие вступления. Итак, по большей части я занимался написанием и анализом различныый скриптов, и пытался выяснить - каким образом можно использовать небольшие ошибки в сових целях ? Мм.. эта статья как раз рассказывает об этом.







          ЯДОВИТЫЙ БАЙТ 0x00



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







        Как вы видите ниже, требоваось открыть указанный файл, rfp.db. Я использовал http-запрос, чтобы передать скрипту значение rfp, пришил к нему расширение и затем открыл файл. На Перле рабочий скрипт выглядит примерно так:







        # parse $user_input

        $database="$user_input.db";

        open(FILE "<$database");








        Отлично. Я передал 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 не будет распознана фильтром, как не соответсвующая правилам - и будет передана скрипту далее. Теперь, чтобы использовать это имя файла в перле, требуется что-то вроде



        $file="/usr/tmp/.\\./.\\./etc/passwd";

        $file=s/\.\.//g;

        system("ls -l $file");



        Но всЈ вышеописанное работает лишь на системных вызовах и вызовах, заключенных в кавычки (''). Опция -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| - мы получим листинг директории. Теперь подумайте, что мы имеем конструкцию вроде



        $filename="/safe/dir/to/read/$FORM"

        open(FILE, $filename)



        Мы должны точно указать директорию, где находится ls - посему мы устанавливаем $FROM в "../../../../bin/ls|", что даЈт нам листинг директории.







        Итак - теперь у нас ситуация, немного более сложная



        $filename="/safe/dir/to/read/$FORM"

        if(!(-e $filename)) die("I don't think so!")

        open(FILE, $filename)



        Требуется одурачить опцию -e. Проблема в том, что "-е" прекратит работу фонкции, как только удостоверится, что файл, который надо открыть, заканчивается пайпом (|). Итак - нам надо сделать пайп невидимым для -е, но добиться того, чтобы перл по прежнему знал о его сущуствовании. Для этого мы будем использовать тот самый нулевой байт. Итак - мы открываем что-то вроде ls%00| , -е оканчивает обработку данный на нулевом байте, и команда ls исполняется!




        Итак, код



        $filename="/bin/ls /etc\0|"

        if(!(-e $filename)) exit;

        open(FILE, $filename)



        не выдаст нам содержимое папки /etc. Это происходит потому, что -e видит, что /bin/ls /etc не существует. Правильный вариант будет выглядеть так:



        $filename="/bin/ls\0 /etc|"

        if(!(-e $filename)) exit;

        open(FILE, $filename)


        Это сработает, но мы получим листинг текущей папки - (простое ls) - /etc не воспринимается как аргумент.





        Я так же хотел сделать замечание всем программистам - если вы ленивые программисты на перле - дважды перепроверьте ваши программы на подобные вещи. Так же помните об управлении потоками даных. Есть символы < и > - которые не позволяют, к приемру, перенаправить анные в работающий ls, но вполне нормально осуществляют запись в открытый файл. Вкратце -



        $bug="ls|"

        open(FILE, $bug)

        open(FILE, "$bug")



        сработает. Но

        <br /> <br /> open(FILE, "&lt;$bug")<br /> <br /> open(FILE, ">$bug")<br /> <br /> open(FILE, ">>$bug")<br /> <br />

        не сработает и всЈ будет в порядке. Если вам надо только читать чтолибо из файла, просто открывайте <$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, и опять никакой поверки на подозрительные символы. Затем



        $output = $basedir . $contents{'file'};

        open(RESULTS, ">>$output");



        Используя стандартный выход за пределы папки, куда нас посадили (/../../) , нам даже не придЈтся использовать нефильтрацию 0x00. Но что бы мы не открыли - файл открывается для дописывания, и поэтому мы должны иметь права на запись в тот файл, который пытаемся открыть. По этой же причине не сработает и pipe (|) bug.





          ОКОНЧАНИЕ



        Итак, на сегодня обзор недокументированных возможностей различных скриптов, и рассказы о том, как их сделать более безопасными, окочнены. Спасибо всем за внимание!






        .rain.worest.puppy. [ADM/Wiretrip] rfp@wiretrip.net