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

WARNING

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

INFO

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

Как работает WAF

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

  1. Парсинг HTTP-пакета, который пришел от клиента.
  2. Выбор правил в зависимости от типа входящего параметра.
  3. Нормализация данных до вида, пригодного для анализа.
  4. Применение правила детектирования.
  5. Вынесение решения о вредоносности пакета. На этом этапе WAF либо обрывает соединение, либо пропускает дальше — на уровень приложения.

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

  • регулярные выражения;
  • токенайзеры, лексические анализаторы;
  • репутация;
  • выявление аномалий;
  • score builder.

Большинство WAF используют именно механизмы регулярных выражений («регэкспы») для поиска атак. На это есть две причины. Во-первых, так исторически сложилось, ведь именно регулярные выражения использовал первый WAF, написанный в 1997 году. Вторая причина также вполне естественна — это простота подхода, используемого регулярками.

Наиболее популярные техники детекта вредоносной нагрузки в WAF
Наиболее популярные техники детекта вредоносной нагрузки в WAF

Напомню, что регулярные выражения выполняют поиск подстроки (в нашем случае — вредоносного паттерна) в тексте (в нашем случае — в HTTP-параметре). Например, вот одна из самых простых регулярок из ModSecurity:

(?i)(<script[^>]*>.*?)

Это выражение ищет HTML-инъекцию типа XSS в теле запроса. Первая часть ((?i)) делает последующую часть выражения нечувствительной к регистру, вторая (во вторых скобках) ищет открывающийся тег <script с произвольными параметрами внутри тега и произвольный текст после символа >.

INFO

Регулярные выражения очень популярны в security-продуктах. При работе с веб-приложениями ты встретишь их на всех уровнях. Самый первый и ближайший к пользователю — XSS Auditor, который встроен во все популярные браузеры (даже в IE, начиная с версии 7). Второй — это фронтендовые анализаторы, предотвращающие исполнение вредоносного кода, который может прийти с бэкенда в качестве данных. Третий уровень — бэкенд, на котором также могут использоваться регулярки для постобработки данных — проверять пользовательский ввод перед сохранением в БД, а также перед выводом пользователю.
 

Изучаем уязвимости правил

Давай скачаем актуальные версии шести топовых бесплатных WAF и вытащим из них все правила. В результате на диске у тебя скопится порядка 500 правил, из которых около 90% защищают веб-приложение от XSS- и SQL-инъекций.

Собираем правила детекта из популярных файрволов
Собираем правила детекта из популярных файрволов

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

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

Опытный специалист, пристально посмотрев на регулярное выражение, сможет, исходя из своего опыта, дать вердикт, можно ли обойти данное правило. Однако точной методологии, по которой неопытный хакер (или скрипт) может проверить регулярное выражение на наличие обхода, не существует. Давай ее создадим.

INFO


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

Модификаторы, числовые квантификаторы и позиционные указатели

Возьмем для начала несложный пример. Здесь у нас простое выражение, которое защищает функцию _exec(). Регулярка пытается найти паттерн attackpayload в GET-параметре a и, если он найден, предотвратить исполнение вредоносного кода:

if( !preg_match("/^(attackpayload){1,3}$/", $_GET['a']) ) {
    _exec($cmd . $_GET['a'] . $arg);
}

В этом коде есть как минимум три проблемы.

  1. Регистр. Выражение не учитывает регистр, поэтому, если использовать нагрузку разного регистра, ее удастся обойти:
    atTacKpAyloAd

    Пофиксить это можно при помощи модификатора (?i), благодаря которому регистр не будет учитываться.

  2. Символы начала и конца строки (^$). Выражение ищет вредоносную нагрузку, жестко привязываясь к позиции в строке. В большинстве языков, для которых предназначается вредоносная нагрузка (например, SQL), пробелы в начале и в конце строки не влияют на синтаксис. Таким образом, если добавить пробелы в начале и конце строки, защиту удастся обойти:
                 attackpayload              

    Чтобы не допускать подобного байпаса, нужно обращать особое внимание на то, как используются явные указатели начала и конца строки. Зачастую они не нужны.

  3. Квантификаторы ({1,3}). Регулярное выражение ищет количество вхождений от одного до трех. Соответственно, написав полезную нагрузку четыре или более раз, можно ее обойти:
    attackpayloadattackpayloadattackpayloadattackpayload...

    Пофиксить это можно, указав неограниченное число вхождений подстроки (+ вместо {1,3}). Квантификатора {m,n} вообще следует избегать. Например, раньше считалось, что четыре символа — это максимум для корневого домена (к примеру, .info), а сейчас появились TLD типа .university. Как следствие, регулярные выражения, в которых используется паттерн {2,4}, перестали быть валидными, и открылась возможность для байпаса.

 

Ошибки логики

Теперь давай рассмотрим несколько выражений посложнее.

  1. (a+)+ — это пример так называемого ReDoS, отказа в обслуживании при парсинге текста уязвимым регулярным выражением. Проблема в том, что это регулярное выражение будет обрабатываться парсером слишком долго из-за чрезмерного количества вхождений в строку. То есть если мы передадим aaaaaaa....aaaaaaaab, то в некоторых парсерах такой поиск будет выполнять 2^n операций сравнивания, что и приведет к отказу в обслуживании запущенной функции.
  2. a'\s+b — в этом случае неверно выбран квантификатор. Знак + в регулярных выражениях означает «1 или более». Соответственно, мы можем передать «a'-пробел-0-раз-b», тем самым обойдя регулярку и выполнив вредоносную нагрузку.
  3. a[\n]*b — здесь используется черный список. Всегда нужно помнить, что большинству Unicode-символов существуют эквивалентные альтернативы, которые могут быть не учтены в списке регулярки. Использовать блек-листы нужно с осторожностью. В данном случае обойти правило можно так: a\rb.
 

Особенности парсеров и опечатки

  1. [A-z] — в этом примере разрешен слишком широкий скоуп. Кроме желаемых диапазонов символов A-Z и a-z, такое выражение разрешает еще и ряд спецсимволов, в числе которых \, `,[,] и так далее, что в большинстве случаев может привести к выходу за контекст.
  2. [digit] — здесь отсутствует двоеточие до и после класса digit (POSIX character set). В данном случае это просто набор из четырех символов, все остальные разрешены.
  3. a |b, a||b. В первом случае допущен лишний пробел — такое выражение будет искать не «a или b», а «а пробел, или b». Во втором случае подразумевался один оператор «или», а написано два. Такое выражение найдет все вхождения a и пустые строки (ведь после | идет пустая строка), но не b.
  4. \11 \e \q — в этом случае конструкции с бэкслешами неоднозначны, так как в разных парсерах спецсимволы могут обрабатываться по-разному в зависимости от контекста. В разных парсерах спецсимволы могут обрабатываться по-разному. В этом примере \11 может быть как бэклинком с номером 11, так и символом табуляции (0x09 в восьмеричном коде); \e может интерпретироваться как очень редко описываемый в документации wildcard (символ Esc); \q — просто экранированный символ q. Казалось бы, один и тот же символ, но читается он по-разному в зависимости от условий и конкретного парсера.
 

Ищем уязвимые регэкспы

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

Запустив инструмент на выборке из 500 регулярных выражений, найденных при сборе правил, я получил интересные результаты: программа обнаружила более 300 потенциальных байпасов. Здесь и далее символы, подсвеченные желтым, — это потенциально уязвимые места в регулярных выражениях.

В первой строке регулярка уязвима к ReDoS.

Пример запуска анализатора на выборке правил, отобранной через grep
Пример запуска анализатора на выборке правил, отобранной через grep

Еще один пример — некорректно выбранная длина строки в запросе union select. Очевидно, что ограничение можно обойти, просто вставив 101 символ и больше.

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

Теперь давай потестируем тулкит на более свежей базе. В качестве примера скачаем последний билд WordPress и при помощи grep вытащим из его исходного кода все регулярные выражения в файл regexp.txt.

Сохраняем все регулярные выражения из кодовой базы WordPress в файл regexp.txt
Сохраняем все регулярные выражения из кодовой базы WordPress в файл regexp.txt

Запустим наш анализатор и взглянем на сгенерированный отчет. В файле wp-includes/class-phpmailer.php обнаружилось выражение [A-z] с описанной выше уязвимостью (вхождение непредназначенных символов). Вот лишь малый список open source CMS, в которых он используется: WordPress, Drupal, 1CRM, SugarCRM, Yii, Joomla.

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


7 комментариев

Подпишитесь на ][, чтобы участвовать в обсуждении

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

Check Also

В редакторе для движка Unity найдена и устранена опасная RCE-уязвимость

Стало известно, что в редакторе Unity обнаружена RCE-уязвимость, представляющая опасность …