WAF глазами хакеров

    Привет, Хабр! Сегодня мы поговорим об одном из современных механизмов защиты веб-приложений, а именно о WAF, Web Application Firewall. Мы расскажем, на чем основываются и как работают современные WAF, какие существуют способы обхода и bypass-техники, как их применять, а также почему ни в коем случае не стоит всецело полагаться на WAF. Мы представим свой взгляд пентестеров, которые никогда не принимали участие в разработке WAF и которые собирали информацию из открытых источников и на основе своего опыта, поэтому о некоторых тонкостях работы WAF мы можем даже и не подозревать.



    Содержание


    1. Введение
    2. Что представляет собой современный WAF
    3. Идентифицируем WAF
    4. WAF bypass cheatsheet
    5. Обходим WAF на практике
    6. Заключение

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

    Введение


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



    Итак, что должен уметь WAF, чтобы его внедрение было обосновано в реальном проекте? Основная его функция — детектирование и блокирование запросов, в которых, согласно анализу WAF, есть некоторые аномалии или прослеживается атакующий вектор. Такой анализ не должен затруднять взаимодействие легитимных пользователей с веб-приложением, в то же время должен точно и своевременно детектировать любые попытки атак. Для того, чтобы реализовать такую функциональность, разработчики WAF обычно применяют регулярные выражения, токенайзеры, поведенческий анализ, репутационный анализ, а также машинное обучение, и зачастую все эти технологии используются совместно. Кроме этого, WAF может еще предоставлять и другую функциональность: защита от DDoS, блокировка IP-адресов атакующих, отслеживание подозрительных IP-адресов, добавление security-заголовков (X-XSS-Protection, X-Frame-Options, etc…), добавление http-only флага к cookie, внедрение механизма HSTS, добавление функциональности CSRF-токенов. Также некоторые WAF имеют встраеваемый на сайт клиентский модуль, написанный на JavaScript.

    Разумеется, WAF создает ряд трудностей для работы хакеров и пентестеров. Обнаружение и эксплуатация уязвимостей становится более трудоемкой задачей, если, конечно, атакующим не известны эффективные 0day-способы обхода конкретного WAF. Использовать автоматизированные сканеры при анализе веб-приложений под защитой WAF почти бесполезно. WAF надежно защищает сайты как минимум от scriptkiddies. Однако, опытный специалист или хакер, не имея должной мотивации, может также решить не тратить много времени на поиск способов обхода. Отдельно отметим, что, чем сложнее и многофункциональнее веб-приложение, тем больше возможная область атаки, и тем проще будет найти способ обхода WAF.

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

    • Мы знаем конкретную уязвимость в веб-приложении и пробуем обойти WAF, чтобы эксплуатировать её
    • Конкретная уязвимость неизвестна, задача состоит в том, чтобы обнаружить ее, не взирая на WAF, а затем проэксплуатировать, обходя WAF.

    Но для начала остановимся подробнее на основных механизмах работы WAF и посмотрим, какие проблемы с этим существуют.

    Что представляет собой современный WAF


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



    Правила на основе регулярных выражений


    Большинство существующих WAF базируется на правилах, основанных на регулярных выражениях. Для их создания некоторое известное множество атак изучается разработчиком WAF, в результате определяются ключевые синтаксические конструкции, по наличию которых можно утверждать о проведении атаки. На основе полученных результатов пишутся регулярные выражения, способные находить такие конструкции. Кажется, что всё просто, однако такой подход имеет ряд недостатков. Зона применимости регулярного выражения ограничивается одним запросом, а чаще даже конкретным параметром запроса, что очевидно снижает эффективность таких правил и создает ряд «слепых зон» для подобного механизма. Во-вторых, синтаксис регулярных выражений, сложная логика текстовых протоколов, допускающая замену на эквивалентные конструкции и использование различных представлений символов, приводят к ошибкам при создании подобных правил. На данную тему есть отличное исследование от Владимира Иванова.

    Scorebuilding


    Данный механизм должен быть знаком всем, кто интересовался устройством межсетевых экранов и антивирусов. Сам по себе он не детектирует атаки, однако дополняет другие методы, делая их более точными и гибкими. Причина появления инструмента — в том, что присутствие в запросе некоторой «подозрительной» конструкции является недостаточным условием для выявления атаки или же, напротив, может приводить к большому числу false-positive ошибок. Данная проблема решается введением балльной системы. Например, каждое правило, основанное на регулярных выражениях, дополняется информацией о критичности его срабатывания; после выявления всех сработавших правил их критичность суммируется. В случае преодоления некоторого порогового значения атака детектируется, а запрос блокируется. Данный механизм, несмотря на свою простоту, хорошо зарекомендовал себя в задачах подобного класса и применяется повсеместно.

    Токенайзеры


    Этот подход к детектированию атак был представлен на Black Hat 2012 в виде C/C++ библиотеки libinjection, позволяющей быстро и точно выявлять атаки класса SQL injection. На данный момент, для библиотеки libinjection существуют порты под различные языки программирования, включая PHP, Lua, Python, etc… По сути, механизм сводится к поиску сигнатур, представленных в виде последовательности токенов. Некоторое количество сигнатур вносится во встроенный черный список и считается недопустимым или вредоносным. Иными словами, перед тем, как проанализировать какой-либо запрос, его сначала приводят к набору токенов. Токены подразделяются на различные типы, например, variable, string, regular operator, unknown, number, comment, union-like operator, function, comma, etc… Один из основных недостатков данного метода заключается в том, что существует возможность построить такую конструкцию, которая приведет к некорректному формированию токенов, следовательно, сигнатура запроса будет отлична от ожидаемой. Такие конструкции обычно называются token breaker, о них мы расскажем чуть позже.

    Анализ поведения


    Детектирование попыток эксплуатации уязвимостей в параметрах запроса — не единственная задача WAF. Важно выявлять и саму процедуру поиска уязвимости, которая может проявляться в попытках сканирования, брутфорса директорий, фаззинга параметров и прочих часто применяемых автоматизированными средствами методик обнаружения уязвимостей и соответственным образом реагировать на них. Более продвинутые WAF даже умеют строить цепочки запросов, «типичные» для нормального поведения пользователя и блокировать попытки отправки запросов в отличном от стандартного поведения порядке. Данный механизм не столько противодействует атакам, сколько затрудняет процесс нахождения уязвимости. Ограничение на количество запросов в минуту никак не скажется на типичном пользователе, но будет существенной помехой для сканера, работающего в несколько потоков.

    Репутационный анализ


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

    Машинное обучение


    Один из самых спорных моментов в WAF. Данный механизм сложнее всего описать, и тому есть множество причин. Для начала, стоит отметить, что само понятие «машинное обучение» крайне обширно и фактически включает в себя множество технологий и методик. Кроме того, “машинное обучение” считается лишь одним из классов методов ИИ. «Внедрение» машинного обучения или “использование ИИ” — очень популярный маркетинговый ход. Зачастую непонятно, какие методы и алгоритмы используются на практике, а иногда со стороны атакующего кажется, что машинное обучение в WAF — это лишь красивые слова. Среди тех игроков рынка, кто действительно смог подчинить себе всю мощь машинного обучения, мало кто готов делиться своим опытом. Всё это складывается в довольно печальную картину для «человека извне», желающего разобраться в вопросе. И всё же попробуем выделить хотя бы парочку здравых идей на основе доступной информации.

    Во-первых, машинное обучение полностью зависит от тех данных, на основе которых оно осуществлялось, и это зачастую является большой проблемой. Требуется наличие у компании-разработчика актуальной и полной коллекции существующих атак и их методов применения, что довольно непросто. По этой причине многие поставщики тщательно логируют результаты работы своих WAF и сотрудничают с другими компаниями, предлагающими IDS, SIEM системы для доступа к реальным примерам атак. Во-вторых, модель, обученная на «сферическом веб-приложении в вакууме» может оказаться попросту неэффективной при установке на реальное веб-приложение клиента. Для наилучшего эффекта считается правильным проводить дополнительное обучение модели на стадии внедрения WAF у клиента, что требует дополнительных расходов, времени, организационных трудностей, а также не гарантирует лучшего результата.

    Идентифицируем WAF


    Разработчики WAF по-разному подходят к оповещению пользователя о том, что WAF заблокировал запрос. Поэтому, анализируя ответ на наш атакующий запрос, мы можем понять, каким именно WAF защищено веб-приложение. Для этого часто используется термин WAF Fingerprint. Это может помочь в случае, если WAF по какой-либо причине не обновляется (обычно это относится к WAF с открытым исходным кодом). Разработчики проприетарных WAF заботятся о клиентах и реализуют механизм автообновления. Также, если мы смогли идентифицировать WAF и он оказался обновленным до последней версии, все равно сведения о конкретном WAF помогут нам немного узнать о специфике его работы.

    Перечислим основные места, по которым можно идентифицировать WAF:

    • Дополнительные куки
    • Дополнительные заголовки, добавляемые к любому ответу или запросу
    • Содержание ответа (в случае блокировки запроса)
    • Код ответа (в случае блокировки запроса)
    • IP-адрес (относится к Cloud WAF)
    • JS-модуль (Client-side WAF)

    Для наглядности приведем несколько примеров.

    PT AF
    Код ответа при блокировке: 403
    Может встраивать в страницу ответа клиентский модуль waf.js.
    Тело ответа при блокировке:

    <h1>Forbidden</h1>
    <pre>Request ID: 2017-07-31-13-59-56-72BCA33A11EC3784</pre>

    Дополнительный заголовок, который добавляет waf.js:

    X-RequestId: cbb8ff9a-4e91-48b4-8ce6-1beddc197a30

    Nemesida WAF
    Код ответа при блокировке: 403
    Тело ответа при блокировке:

    <p style="font-size: 16px; align: center;">
    Suspicious activity detected. Access to the site is blocked.
    If you think that is's an erroneous blocking, please email us at
    <a href="mailto:nwaf@pentestit.ru">nwaf@pentestit.ru</a> and
    specify your IP-address. </p>

    Wallarm
    Код ответа при блокировке: 403
    Дополнительный заголовок: nginx-wallarm

    Citrix NetScaler AppFirewall
    Дополнительные куки:

    ns_af=31+LrS3EeEOBbxBV7AWDFIEhrn8A000;
    ns_af_.target.br_%2F_wat=QVNQU0VTU0lP
    TklEQVFRU0RDU0Nf?6IgJizHRbTRNuNoOpbBOiKRET2gA

    Mod_Security ver. 2.9
    Код ответа при блокировке: 403
    Тело ответа при блокировке:

    <head>
    <title>403 Forbidden</title>
    </head><body>
    <h1>Forbidden</h1>
    <p>You don't have permission to access /form.php on this server.<br /></p>

    Mod_Security ver. <2.9
    Код ответа при блокировке: 406 или 501
    Тело ответа при блокировке:
    В теле ответа можно найти mod_security, Mod_Security или NOYB

    Varnish FireWall
    Добавляет в ответ заголовки вида:

    X-Varnish: 127936309 131303037.
    X-Varnish: 435491096
    Via: 1.1 varnish-v4

    Разработчики WAF сами решают, какой код ответа возвращать в случае блокировки запроса, бывают и специфические коды. К примеру, код 999 вернет Web_Knight WAF в случае блокировки запроса, а dotDefender вернет код 200 с пустым телом ответа или содержащим сообщение об ошибке.

    Также не стоит забывать, что WAF, как и любое приложение, развивается и видоизменяется. Поэтому всегда важно проверять актуальность известных вам “отпечатков”. Кроме того, разработчики могут сделать кастомную страницу ответа при блокировке с каким-либо другим содержанием.

    WAF bypass cheatsheet


    Общая идея нахождения способов обхода WAF — привести нужный нам запрос к виду, в котором он всё ещё понятен атакуемому веб-приложению, но при этом не понятен или кажется безобидным для WAF. Важно отметить, что один тип WAF должен уметь обслуживать большое количество различных типов серверов, включая экзотические, такие как Unicorn, Tornado, Weblogic, Lighttpd etc… Каждый сервер может по-разному воспринимать те или иные исключительные случаи парсинга HTTP-запроса, что должно быть учтено в WAF. Таким образом, атакующий может воспользоваться спецификой парсинга HTTP-запроса атакуемого сервера для того, чтобы найти способ обхода WAF.



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

    Добавление спецсимволов


    Различные спецсимволы могут нарушать логику работы WAF и при этом быть понятными самому серверу. Вариации спецсимволов тоже могут быть любыми: их можно преобразовать в urlencode (но большинство WAF уже давно с этим справляются) или в иные кодировки. Можно также вставлять спецсимволы в запрос без какой-либо кодировки, в raw-формате, что может оказаться довольно неожиданно для WAF. Например, \r\n\r\n в таком виде может восприниматься как конец HTTP-request body, а null byte может вообще нарушать логику работы регулярных выражений и парсеров различных форматов данных. Также могут быть полезны и другие спецсимволы из первых двух десятков символов ASCII-таблицы.

    Примеры полезных спецсимволов:

    • 0x00 — Null byte;
    • 0x0D — Carriage return;
    • 0x0A — Line feed;
    • 0x0B — Vertical Tab;
    • 0x09 — Horizontal tab;
    • 0x0C — New page

    При поиске bypass будет полезно вставлять спецсимволы в различные места в теле запроса, не ограничиваясь включением в значение параметра. Например, если запрос представлен в формате JSON, то можно вставлять null byte как в один из параметров, так и между параметрами, и в начале JSON, и в конце. То же самое относится и к остальным форматам тела POST-запроса. В общем, рекомендуем исследовать и развлекаться, искать места, которые может проверять или парсить WAF, и пробовать там разные спецсимволы.

    Например:

    {"id":1337,"string0x00":"test' or sleep(9)#"}
    {"id":1337,"string":"test'/*0x00*/ or sleep(9)#"}
    {"id":1337,"string"0x0A0x0D:"test' or sleep(9)#"}

    <a href="ja0x09vas0x0A0x0Dcript:alert(1)">clickme</a>
    <a 0x00 href="javascript:alert(1)">clickme</a>
    <svg/0x00/onload="alert(1)">

    id=1337/*0x0C*/1 UNION SELECT version(), user() -- 

    Для наглядности мы заменили спецсимволы на их hex представление.

    Замена пробельных символов


    В отдельную категорию стоит выделить замену пробелов на эквивалентные символы. Например, большинство синтаксисов предполагает разделение ключевых слов и операторов пробельными символами, однако строго не указывает, какой именно символ должен быть использован. Таким образом, вместо привычных 0x20 (Space) могут быть использованы 0x0B (Vertical Tab), 0x09 (Horizontal tab). Также в эту категорию стоит отнести замену пробельных символов на разделяющие конструкции, не несущие смысловой нагрузки. В SQL, например, это /**/ (многострочный SQL комментарий), #\r\n (однострочный SQL комментарий, завершающийся переносом строки), --\r\n (альтернативный однострочный SQL комментарий, завершающийся переносом строки). Вот несколько примеров:

    http://test.com/test?id=1%09union/**/select/**/1,2,3
    http://test.com/test?id=1%09union%23%0A%0Dselect%2D%2D%0A%0D1,2,3

    Также можно изменить выражение таким образом, чтобы, используя синтаксис языка, избавиться от пробелов. К примеру, в SQL можно воспользоваться скобками:

    UNION(SELECT(1),2,3,4,5,(6)FROM(Users)WHERE(login='admin'))

    А в JS использовать символ /:

    <style/onload=confirm(1)>

    Изменение кодировки


    Способ основывается на использовании различных кодировок таким образом, чтобы WAF не декодировал данные в определенных местах. Например, после замены одного символа на его url-code, WAF не сможет понять, что нужно декодировать данные и пропустит запрос, в то время как тот же параметр будет принят и успешно декодирован веб-приложением.

    Десятичное представление символа HTML: &#106 или &#0000106. WAF может знать о коротком представлении символов (как в первом варианте), но не знать о варианте с добавлением дополнительных нулей, общее количество символов не должно быть больше 7. Аналогично, шестнадцатеричное представление символа HTML: &#x6A или &#x000006A.

    Существует еще трюк с экранированием некоторых символов с помощью символа \, например:

    <svg/on\load=a\lert(1)>

    Однако, это зависит от того, как веб-приложение обрабатывает такие входные данные. То есть, последовательность символов \l будет воспринята как экранированный l и преобразована в один символ, WAF же может воспринять каждый символ по отдельности. Таким образом WAF не увидит ключевые слова. Используя такую технику, нельзя экранировать символы \n, \r, \t, поскольку они будут преобразованы в совершенно другие символы: перенос строки, возврат каретки и табуляцию.

    HTML-encode также можно использовать внутри свойств тегов, например:

    <a href="javascript&colon;alert(1)">clickme</a>
    <input/onmouseover="javascript&colon;confirm&lpar;1rpar;">

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

    Кроме HTML encode можно вставлять символы используя \u:

    <a href="javascript:\u0061lert(1)">Clickme</a>
    <svg onload=confir\u006d(1)>

    Еще отдельно коснемся вектора, связанного со вставкой спецсимволов. Разобьем payload с помощью HTML encode:

    <a href="ja&Tab;vas&#x0000A;cript:alert(1)">clickme</a>

    В этом случае можно подставить и иные разделяющие символы.

    Таким образом, рекомендуем комбинировать различные кодировки с другими способами, например, чтобы кодировать спецсимволы.

    Поиск нетипичных эквивалентных синтаксических конструкций


    Данный метод заключается в том, чтобы найти способ эксплуатации, который мог быть не учтен разработчиками WAF, либо вектор отсутствовал в обучающей выборке для машинного обучения. В качестве простых примеров можно привести некоторые функции javascript: this, top self, parent, frames, свойства тэгов: data-bind, ontoggle, onfilterchange, onbeforescriptexecute, onpointerover, srcdoc и операторы SQL: lpad, field, bit_count.

    Вот некоторые примеры:

    <script>window['alert'](0)</script>
    <script>parent['alert'](1)</script>
    <script>self['alert'](2)</script>

    SELECT if(LPAD(' ',4,version())='5.7',sleep(5),null);

    Также можно пользоваться бессимвольным представлением JavaScript-выражений:


    Очевидная проблема такого представления JS — большая длина получающихся payload.

    Отдельно отметим, что обход WAF с использованием данной техники зависит от конкретной атаки и эксплуатируемого стека технологий. В качестве примера можно привести ситуацию с нашумевшим эксплоитом ImageTragick.

    Большинство WAF для защиты от данной атаки внесли в черные списки ключевые слова url, capacity, label которые использовались в большинстве статей и PoC, описывающих данную уязвимость. Однако, вскоре было обнаружено, что помимо этих ключевых слов можно использовать и другие, например ephemeral, pango. В результате, WAF можно было обойти, используя в качестве вектора эти ключевые слова.

    HTTP Parameter Pollution (HPP) и HTTP Parameter Fragmentation (HPF)


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

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



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

    В качестве примера такой атаки можно привести SQL-инъекцию вида:

    http://test.com/url?a=1+select&b=1+from&c=base

    HPF и HPP очень похожие атаки, но если первый направлен на веб-приложение, то второе на среду, в которой оно работает. Применение этих техник одновременно еще больше увеличивает шансы обхода WAF.

    Unicode normalization


    У Unicode есть одна особенность — Unicode Normalization. Это было сделано для того, чтобы можно было сравнивать некоторые похожие по написанию Unicode-символы, например, символы 'ª' и 'ᵃ' имеют разные коды, однако более ничем особо не различаются, после нормализации они оба будут приведены к простому символу 'a' и будут считаться одинаковыми. Нормализация позволяет преобразовать некоторые сложные Unicode-символы к их более простым аналогам. Существует таблица, в которой указаны все возможные Unicode-символы с их возможной нормализацией. С ее помощью можно составлять различные payload и комбинировать их с другими методами. Однако, это работает далеко не во всех веб-приложениях.

    Например, в таблице, приведенной выше, можно обнаружить, что символы и преобразуются в символ <. Но нужно отметить, что важен этап, на котором происходит нормализация, потому что, если приложение использует HTML encoding после нормализации, то, скорее всего, полученный после нормализации символ < будет закодирован в &lt;. Однако, в ином случае, разработчики вполне могли не учесть этой особенности и не кодировать Unicode-символы. Таким образом, мы получаем незакодированные символы < и >, которые можно превратить в XSS. У WAF также могут быть проблемы с пониманием Unicode-символов, в нем может просто не найтись правил для таких трюков, а машинное обучение может также оказаться бессильно. При нахождении способа обхода WAF в веб-приложениях, в которых есть нормализация Unicode, можно также заменять не только символы < >, а изменять и другие символы из payload.

    Например:

    <img src﹦x onerror=alert︵1)>

    Недавно у Rockstar на HackerOne также находили данную проблему, но там не было WAF, а была только жесткая фильтрация пользовательского ввода:

    hackerone.com/reports/231444
    hackerone.com/reports/231389

    Токен-брейкеры


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

    SELECT-@1,version()

    где -@ — это и есть токен-брейкер.

    В открытом доступе есть некоторый cheat sheet, полученный фаззингом mysql и последующей проверкой запросов в libinjection, его можно найти здесь.

    Тема нахождения проблем в libinjection уже давно не нова, больше подробностей можно узнать здесь:

    Еще один фазер
    Статья раз
    Статья два

    Использование возможностей RFC


    В спецификациях, посвященных протоколу HTTP/1.1 и различным форматам запроса, например таким, как multipart/form-data, можно обнаружить некоторые интересные моменты, связанные с граничными случаями или хитростями при обработке заголовков и параметров. Разработчики WAF зачастую не учитывают такие моменты, из-за чего WAF может некорректно распарсить запрос и упустить часть данных, в которых может скрываться атакующий вектор. Больше всего проблем в WAF связано именно с обработкой multipart/form-data и специфичными значениями параметра boundary, который определяет границы параметров в таких запросах. Кроме того, разработчики серверов тоже могут ошибаться и не всегда полностью поддерживать спецификации, из-за чего в HTTP-парсере сервера можно найти недокументированные возможности.

    Параметр boundary в HTTP-запросе с multipart/form-data, как мы уже писали выше, отвечает за разграничение различных параметров в теле запроса. Согласно RFC, перед каждым новым POST параметром указывается ранее обозначенный boundary с префиксом содержащим “--”, таким образом сервер отличает различные параметры запроса.

    POST /vuln.php HTTP/1.1
    Host: test.com
    Connection: close
    Content-Type: multipart/form-data; boundary=1049989664
    Content-Length: 192
    
    --1049989664
    Content-Disposition: form-data; name="id"
    
    287356 
    --1049989664--

    Атака может заключаться в том, что сервер и WAF по-разному обрабатывают ситуацию, когда параметр boundary остается пустым. Исходя из RFC, в такой ситуации границей между параметрами будет являться последовательность символов “--”. Однако, в WAF может использоваться парсер, который не учитывает этой особенности, из-за чего WAF пропустит запрос, поскольку данные из параметров POST-запроса просто не попадут в анализатор. Веб-сервер в свою очередь может распарсить такую ситуацию без проблем и передать данные дальше на обработку.

    POST /vuln.php HTTP/1.1
    Host: test.com
    Connection: close
    Content-Type: multipart/form-data; boundary=
    Content-Length: 192
    
    --
    Content-Disposition: form-data; name="id"
    
    123' or sleep(20)# 
    ----

    Мы приведем еще несколько интересных примеров из доклада Bo0om’а на ZeroNights 2016 и объясним их:

    
    POST /vuln.php HTTP/1.1 
    Host: test.com
    Content-Type: multipart/form-data; boundary=FIRST;
    Content-Type: multipart/form-data; boundary=SECOND;
    Content-Type: multipart/form-data; boundary=THIRD;
    
    --THIRD 
    Content-Disposition: form-data; name=param 
    
    UNION SELECT version()
    --THIRD--
    

    В данной атаке мы пытаемся определить какой из параметров boundary будет принят WAF, а какой веб-сервером. Соответственно, если веб-сервер и WAF будут принимать различные параметры boundary, то возможно провести атаку, указав такую итоговую границу, которую не увидит WAF. Такая атака чем-то похожа на HPP.

    
    POST /vuln.php HTTP/1.1 
    Host: test.com
    Content-Type: multipart/form-data; xxxboundaryxxx=FIRST; boundary=SECOND;
    
    --FIRST 
    Content-Disposition: form-data; name=param 
    
    UNION SELECT version()
    --FIRST--
    

    Такая атака рассчитана на другое возможное различие в парсинге HTTP-запроса между WAF и веб-сервером. Различие должно заключаться в следующем, парсер на стороне веб-сервера ищет первое вхождение 'boundary', а затем ищет знак '=' и только после этого определяет значение boundary, парсер WAF в свою очередь ищет только вхождение строки 'boundary=' и затем определяет значение boundary. Если эти условия выполнены, то получив такой запрос WAF не сможет найти указанный boundary и, следовательно, не сможет найти и проанализировать параметр. Веб-сервер же получит запрос и обработает параметр. Эта атака может работать и наоборот, когда парсер веб-сервера ищет вхождение 'boundary=', а парсер WAF ищет только 'boundary', в таком случае нужно лишь изменить реальный boundary с FIRST на SECOND.

    
    POST /somepage.php HTTP/1.1 
    Host: test.com
    Content-Type: multipart/form-data; boundary=Test0x00othertext; 
    
    --Test 
    Content-Disposition: form-data; name=param 
    Attack
    --Test--
    

    Эта атака связана в том числе и с добавлением спецсимволов. В параметр boundary добавлен null byte в расчете на то, что веб-сервер обрежет параметр boundary до null byte, а WAF воспримет его целиком. В таком случае, WAF опять не сможет проанализировать параметр, поскольку не найдет его границ.

    Обход машинного обучения


    Суть обхода очевидна — составить такую атаку, которая удовлетворяла бы параметрам обученной статистической модели. Но это очень сильно зависит от того, как и на какой выборке был обучен WAF. Иногда лазейку найти можно, а иногда обход не представляется возможным в принципе. Обычно, при внедрении у клиента, WAF с машинным обучением нуждается в некотором дополнительном обучении на основе запросов, поступающих в веб-приложение клиента. И проблема для пентестеров тут может заключаться в следующем: параметры, которые имеют одинаковый вид или не сильно меняются от запроса к запросу, будет невозможно как-либо тестировать, поскольку любой шаг в сторону от обычного вида параметра может уже восприниматься как аномалия. Поясним на примере. Пусть у нас есть условный запрос к http://api.test.com/getuser?id=123, параметр id всегда является числовым, и в обучающей выборке он также всегда оставался числовым. Если модуль машинного обучения обнаружит в этом параметре что-либо кроме цифр, то он скорее всего решит, что это аномалия. А в другом случае, предположим, что WAF обучался классифицировать POST-запрос к http://api.test.com/setMarkDown с POST-параметрами, в которых присутствует маркдаун. Разумеется, в маркдауне могут присутствовать и кавычки, и спецсимволы, да и что вообще угодно. В таком случае, модуль машинного обучения будет значительно проще обойти, поскольку WAF будет толерантен к кавычкам и спецсимволам.

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

    В целом, нужно учитывать специфику тестируемого запроса и параметров в нём, предполагать возможные варианты значений параметров, к которым WAF может толерантен, и затем уже отталкиваться от них.

    Когда WAF не поможет?


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

    Существующие приложения


    Для автоматизации поиска способов обхода WAF существуют некоторые инструменты, написанные энтузиастами в этой области.

    Вот наиболее интересные из них, на которые стоит обратить внимание:

    lightbulb-framework — это целый фреймворк для тестирования веб-приложений, защищенных WAF. Он написан на Python и дополнительно портирован как плагин для Burp Suite. Главной его особенностью являются два алгоритма:

    • GOFA — алгоритм активного обучения, который позволяет анализировать фильтрацию и санитизацию параметров в веб-приложении.
    • SFADiff — алгоритм дифференциального тестирования черного ящика на основе обучения с помощью символьных конечных автоматов (SFA). Он позволяет находить различия в работе веб-приложений, что, в свою очередь, помогает идентифицировать WAF и найти способ обхода.

    Bypass WAF — плагин для Burp Suite, который позволяет без лишних сложностей настроить автоматическое изменение элементов в теле запроса по различным правилам и изменениям кодировки, в том числе автоматизирует атаку HPP.

    WAFW00F — программа для идентификации WAF, написанная на Python. Имеет достаточно неплохую базу WAF и поддерживается по сей день. Однако, результат может быть все равно неточным, поскольку различные WAF обновляются гораздо быстрее, чем обновляется сам проект.

    Обходим WAF на практике




    Перейдем к реальным случаям из нашей практики. Мы проводили аудит некоторого интернет-магазина, сайт которого находился под защитой PT AF. Из-за WAF сложно было обнаружить уязвимое место, от которого можно было бы отталкиваться и искать способы обхода. Но вскоре было обнаружено нестандартное поведение со стороны веб-приложения, которое WAF не фильтровал. Аномалия была найдена в функциональности поиска по истории купленных товаров и заключалась в следующем:

    Запрос передавался в формате JSON и выглядел примерно вот так:

    {"request":{"Count":10,"Offset":0,"ItemName":"Phone"}}

    При подстановке в параметр ItemName значений Phone’ и Phone’+’, было обнаружено, что сервер для этих двух случаев возвращал различные ответы. Ответ на запрос с Phone’ был пустым, а ответ на запрос с Phone’+’ содержал в себе такие же данные о товарах со словом Phone в названии, как если бы параметр ItemName имел бы просто значение Phone. Такое поведение знакомо многим хакерам и пентестерам и явно указывает на то, что в веб-приложении присутствует проблема с фильтрацией пользовательского ввода, которая приводит, в том числе, и к SQL-инъекциям.

    Расскажем, почему так происходит на примере SQL-инъекции для тех, кто не знает об этом. Смысл в том, что если в веб-приложении обнаруживается такое поведение, то скорее всего данные для SQL-запроса просто конкатенируются с самим запросом, и в первом случае, при передаче параметра Phone’, будет сформирован SQL-запрос:

    SELECT item FROM items WHERE item_name=’Phone’’ 

    Такой запрос явно не будет выполнен из-за некорректного синтаксиса и не вернет никакого результата. А второй запрос с параметром Phone’+’, в свою очередь, будет выглядеть вот так:

    SELECT item FROM items WHERE item_name=’Phone’+’’

    Такой запрос имеет корректный синтаксис и произведет выборку товаров по имени Phone.
    У такого способа детектирования уязвимостей есть большое преимущество во время тестирования веб-приложения, находящегося под защитой WAF. Символ одинарной кавычки большинство современных WAF не считает достаточной аномалией в параметре и пропускает запрос с ней.

    С детектированием мы разобрались, а как теперь обходить WAF и эксплуатировать уязвимость? После перебора некоторых вариантов обхода, была найдена проблема и в исследуемом WAF. Оказалось, что WAF уязвим к спецсимволам, добавленным в параметры JSON. По сути, подставив в любое текстовое поле JSON символы \r\n в raw-формате без какой-либо кодировки, WAF просто-напросто пропускал запрос, а веб-приложение считало данные корректными и обрабатывало. Судя по всему, проблема заключалась в парсере JSON, который не был расчитан на появление спецсимволов и парсил JSON ровно до места появления этих символов. Таким образом, в анализатор WAF попадал не полный запрос, и в после спецсимволов можно было вставлять любой атакующий вектор. Кроме переноса строки еще срабатывали и другие спецсимволы, например, null byte. В итоге, можно было составить следующий запрос, который по факту отключал WAF при попытке проверить весь этот запрос (символы переноса строки и возврата каретки заменены на их текстовое представление):

    {"request":{"kill-waf":"die\r\n", "Count":10,"Offset":0,"ItemName":["'+(SELECT 'Phone'+CHAR(ASCII(substring(@@version,1,1))-24))+'"]}}

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

    Похожие проблемы были найдены также в Nemesida WAF. Разница была лишь в том, что запрос был не в JSON-формате, а был обычный POST-запрос с параметрами, а сам параметр в веб-приложении подставлялся в SQL-запрос как число. К сожалению, мы не можем опубликовать сейчас технические подробности, поскольку разработчик Nemesida WAF запретил их публиковать в данный момент. Однако, мы обязательно опубликуем их позже. О проблемах было сообщено в компанию Pentestit и они были исправлены.

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

    Заключение


    Итак, стоит ли всецело полагаться на WAF? Ответ — “нет”.

    К сожалению, далеко не все разработчики это понимают и почему-то считают WAF серебрянной пулей от хакеров. Например, в одном из аудитов мы также обнаружили способ обхода WAF, который позволил нам проэксплуатировать уязвимости. Как нам стало известно после аудита, разработчики уже проводили аудит веб-приложения, когда оно ещё не было защищено WAF, и в ходе прошлого аудита эти уязвимости уже были найдены, но вместо того, чтобы закрыть их, было решено купить современный WAF с машинным обучением и полностью положиться на него. Очень жаль, что вендор WAF не настоял на том, чтобы разработчики веб-приложения исправили известные уязвимости. Либо сами разработчики решили, что WAF их защитит лучше, чем исправление багов в коде. Но подробностей мы не знаем. Так или иначе, и то и другое — очень плохая практика как со стороны вендора WAF, так и со стороны разработчиков.

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

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

    Обходили WAF'ы и собирали материал:

    Булатов Илья barracud4
    Рыбин Денис thefaeriedragon
    Романов Александр web_rock
    • +26
    • 10,9k
    • 3
    Digital Security 181,85
    Безопасность как искусство
    Поделиться публикацией
    Похожие публикации
    Комментарии 3
    • +7

      В PT Application Firewall проблема с парсингом JSON уже устранена. Мы тщательно заботимся о выявлении таких обходов, поэтому каждый год проводим конкурс WAF Bypass ([1], [2], [3], [4]) в рамках конференции Positive Hack Days. Приглашаем принять участие в следующем году.

      • +1
        Эта книжка у меня была в детстве! Димитр Ангелов «Когда человека не было»
        • +2
          Был недавно схожий опыт на проекте: WAF мешал в первую очередь фаззить, сильно замедляя обследование. Удалось найти копию сайта, не защищенную WAF, найти уязвимости на нем, а затем точечно пробить основной исследуемый сайт. Также использовался один из российских WAF.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое