- PVSM.RU - https://www.pvsm.ru -
[1] Одна из самых популярных тенденций в области защиты приложений нынешнего десятилетия — технология виртуального патчинга (virtual patching, VP), позволяющая защитить веб-приложение от эксплуатации имеющихся в нем известных уязвимостей на уровне межсетевого экрана уровня веб-приложений (web application firewall; здесь и далее под WAF подразумевается выделенное решение, функционирующее на отдельном узле, между шлюзом во внешнюю сеть и веб-сервером). Технология VP основана на построении правил фильтрации HTTP-запросов на стороне WAF по результатам работы средств статического анализа защищенности приложения (static application security testing, SAST). Однако из-за того, что средства SAST и WAF опираются на различные модели представления приложения и различные методы принятия решений, на рынке до сих пор нет по-настоящему эффективных решений их интеграции. В рамках SAST работа с приложением осуществляется по модели белого ящика и, как правило, используются формальные подходы к поиску уязвимостей в коде. Для WAF же приложение представляет собой черный ящик, а для детектирования атак применяются эвристики. Это не позволяет эффективно использовать VP для защиты от атак в тех случаях, когда условия эксплуатации уязвимости выходят за рамки тривиальной схемы `http_parameter=plain_text_attack_vector`.
Но что, если «подружить» SAST и WAF таким образом, чтобы информация о внутреннем устройстве приложения, полученная с помощью SAST, стала доступной на стороне WAF и дала ему возможность детектировать атаки на обнаруженные уязвимости — не угадывая, но доказывая факт атаки?
Традиционный подход к автоматизации создания виртуальных патчей для веб-приложений заключается в предоставлении WAF информации о каждой обнаруженной с помощью SAST уязвимости, включающей:
Для определения множеств значений параметров HTTP-запроса и опасных элементов уязвимого параметра могут использоваться как простое перечисление всех возможных элементов, так и генерирующая функция (как правило, на базе регулярных выражений). Рассмотрим фрагмент кода ASP.NET-страницы, уязвимый для атак XSS [2]:
01 var condition = Request.Params["condition"];
02 var param = Request.Params["param"];
03
04 if (condition == null || param == null)
05 {
06 Response.Write("Wrong parameters!");
07 return;
08 }
09
10 string response;
11 if (condition == "secret")
12 {
13 response = "Parameter value is `" + param + "`";
14 }
15 else
16 {
17 response = "Secret not found!";
18 }
19
20 Response.Write("<b>" + response + "</b>");
В результате анализа этого кода для вектора атаки будет выведена символическая формула условного множества его значений:
{condition = «secret» ⇒ param ∈ { XSShtml-text }}, где XSShtml-text — множество возможных векторов XSS-атаки в контексте TEXT, описанном в грамматике HTML.
Из этой формулы может быть выведен как эксплойт, так и виртуальный патч. На основе дескриптора виртуального патча WAF формирует правила фильтрации, позволяющие блокировать все HTTP-запросы, выполнение которых может привести к эксплуатации найденной уязвимости.
Такой подход, безусловно, позволяет защищаться от некоторого множества атак, однако обладает и существенными недостатками:
Эти недостатки приводят к тому, что технология VP, ориентированная на защиту от частных случаев, не позволяет эффективно защититься от всех возможных атак на обнаруженные с помощью средств SAST уязвимости. Кроме того, построенные таким образом правила фильтрации трафика часто приводят к блокированию штатных HTTP-запросов и нарушению работы защищаемого приложения. Немного изменим уязвимый код:
01 var condition = Request.Params["condition"];
02 var param = Request.Params["param"];
03
04 if (condition == null || param == null)
05 {
06 Response.Write("Wrong parameters!");
07 return;
08 }
09
10 string response;
11 // CustomDecode реализует цепочку преобразований base64-URL-base64
12 if (CustomDecode(condition).Contains("secret"))
13 {
14 response = "Parameter value is `" + CustomDecode(param) + "`";
15 }
16 else
17 {
18 response = "Secret not found!";
19 }
20
21 Response.Write(response);
Разница с предыдущим примером лишь в том, что теперь оба параметра запроса подвергаются некоторому преобразованию и условие на параметр `secret` ослаблено до включения подстроки. Формула вектора атаки в результате анализа этого кода примет вид:
(String.Contains (CustomDecode (condition)) («secret»)) ⇒ param ∈ (CustomDecode { XSShtml-text }).
При этом для функции CustomDecode в соответствующей вершине CompFG анализатором будет выведена формула, описывающая цепочку преобразований Base64-URL-Base64:
(Base64Decode (UrlDecode (Base64Decode argument))).
По формулам такого вида все еще возможно построить эксплойт (об этом мы подробно рассказывали в одной из наших предыдущих статей [3]), однако применить классический подход к построению виртуальных патчей здесь уже не представляется возможным, поскольку:
Поскольку все проблемы традиционного VP растут из отсутствия возможности работать с приложением на уровне WAF по модели белого ящика, очевидно, что для их устранения необходимо реализовать такую возможность и доработать подход таким образом, чтобы:
Так и родилась технология виртуального патчинга времени выполнения.
В основе технологии runtime virtual patching (RVP) лежит используемая в анализаторе исходных кодов PT Application Inspector (PT AI) модель исследуемого приложения под названием «граф потоков вычисления» (computation flow graph, CompFG). Эта модель была подробно описана в рамках мастер-класса «Трущобы AppSec» [4] на PHDays VII. CompFG строится во время анализа приложения в результате абстрактной интерпретации его кода в семантике, схожей с традиционными символическими вычислениями. Вершины данного графа содержат генерирующие формулы на целевом языке, задающие множества допустимых значений всех потоков данных, присутствующих в соответствующих точках выполнения. Эти потоки называются аргументами точки выполнения. Например, вершина уязвимой точки выполнения рассмотренного выше примера в CompFG выглядит так:
Одним из свойств CompFG является его конкретизируемость — возможность вычислить множества конкретных значений всех аргументов в любой точке выполнения приложения, задав значения для всех входных параметров.
Рабочий процесс RVP делится на два этапа, соответствующих этапам жизненного цикла приложения — развертывание (шаги D) и выполнение (шаги R):
Перед развертыванием очередной версии приложения осуществляется его анализ с помощью PT AI, в результате которого из каждой вершины CompFG, описывающей уязвимую точку выполнения, выводятся три формулы:
Все наборы формул группируются по признаку принадлежности уязвимости к потоку управления той или иной точки входа в веб-приложение. Само понятие точки входа специфично для каждого из поддерживаемых PT AI веб-фреймворков и определено в базе знаний анализатора.
После этого отчет c обнаруженными уязвимостями и относящимися к ним формулами выгружается в виде кода на специальном языке предметной области, основанном на синтаксисе S-выражений и позволяющем описывать формулы CompFG в форме, не зависящей от целевого языка. Формула значения аргумента уязвимой точки рассмотренного ранее примера кода выглядит следующим образом:
(+ «Parameter value is `» (FromBase64Str (UrlDecodeStr (FromBase64Str (GetParameterData (param))))) "`"),
а формула условия ее достижимости:
(Contains (FromBase64Str (UrlDecodeStr (FromBase64Str (GetParameterData (condition))))) «secret»).
Полученный отчет загружается в PT Application Firewall (PT AF), и на его основе генерируется бинарный модуль, позволяющий вычислять все присутствующие в нем формулы. Декомпилированный код расчета условия достижимости уязвимой точки рассмотренного примера выглядит так:
Для того, чтобы вычисление формул было возможным, на стороне PT AF необходимо иметь (на выбор):
Первый вариант дает максимальное быстродействие, но предполагает огромный объем ручной работы со стороны разработчиков WAF по описанию вычислителей (даже если ограничиваться только функциями стандартных библиотек). Второй вариант дает возможность вычислять все функции, которые могут встретиться в отчете, но и увеличивает время обработки каждого HTTP-запроса из-за необходимости вызова среды выполнения для вычисления каждой функции. Оптимальным здесь является вариант, при котором для наиболее часто встречающихся функций используется первый вариант, а все остальные вычисляются с помощью второго.
Вполне возможна ситуация, когда в формуле встретится функция, в которую анализатор не сможет «провалиться» (например, вызов метода, относящегося к отсутствующей зависимости проекта или к native-коду) и (или) вычисление которой также невозможно на стороне PT AF (например, функция чтения данных из внешних источников или окружения сервера). Такие функции отмечаются в формулах флагом unknown и обрабатываются особым образом (см. ниже).
На этапе эксплуатации при каждом HTTP-запросе WAF делегирует его обработку сгенерированному бинарному модулю. Модуль анализирует запрос и определяет относящуюся к нему точку входа в веб-приложение. Для этой точки выбираются формулы всех обнаруженных в результате ее анализа уязвимостей — и далее вычисляются определенным образом.
Сначала вычисляются формулы обоих условий: достижимости уязвимой точки и значений всех ее аргументов. Вместо переменных в каждую формулу подставляются значения соответствующих параметров запроса, после чего вычисляется ее значение. Если в формуле присутствуют выражения с флагом unknown, она обрабатывается следующим образом:
Если в результате вычисления было получено ложное значение исходной формулы, то это значит, что данный HTTP-запрос не может привести приложение в уязвимую точку с опасными значениями всех ее аргументов. В этом случае RVP просто возвращает обработку запроса основному модулю WAF.
В случае выполнимости условий атаки на уязвимость наступает очередь вычисления значения аргумента уязвимой точки. Используемый для этого алгоритм зависит от класса уязвимости, к которому относится обрабатываемая точка. Общей для них является только логика обработки формул, содержащих unknown-ноды: в отличие от формул условий, такие формулы аргументов не могут быть вычислены каким-либо образом, о чем сразу сообщается WAF — и затем осуществляется переход к вычислению следующей уязвимой точки. В качестве примера рассмотрим наиболее сложный из алгоритмов, используемый для детектирования атак класса инъекций.
К классу инъекций относятся любые атаки, целью которых является нарушение целостности текста на каком-либо формальном языке (HTML, XML, JavaScript, SQL, URL, файловые пути и т. п.), формируемого на основе данных, контролируемых атакующим. Атака осуществляется через передачу приложению специально сформированных входных данных, подстановка которых в атакуемый текст приведет к выходу за пределы токена и внедрению в текст синтаксических конструкций, не предусмотренных логикой приложения.
В том случае, если текущая уязвимая точка приложения относится к данному классу атак, значение ее аргумента рассчитывается по алгоритму так называемого инкрементального вычисления с абстрактной интерпретацией в семантике taint-анализа. Суть данного алгоритма заключается в том, что каждое выражение формулы рассчитывается отдельно, снизу вверх, причем результат вычисления, полученный на каждом шаге, дополнительно размечается границами «загрязненности», исходя из семантики каждой вычисленной функции и правил традиционного taint-анализа [5]. Это позволяет выделить в конечном результате вычисления все фрагменты, которые были получены в результате каких-либо преобразований входных данных (tainted-фрагменты).
Например, для приведенного выше кода и HTTP-запроса с параметрами `?condition=YzJWamNtVjA%3d¶m=UEhOamNtbHdkRDVoYkdWeWRDZ3hLVHd2YzJOeWFYQjBQZyUzRCUzRA%3d%3d` результат применения этого алгоритма для формулы аргумента уязвимой точки будет выглядеть следующим образом (красным отмечены tainted-фрагменты):
Далее полученное значение разбивается на токены в соответствии с грамматикой аргумента уязвимой точки, и если на любой из tainted-фрагментов пришлось более одного токена, то это и является формальным признаком детектированной атаки (по определению инъекции):
По окончании вычисления формул всех уязвимостей, относящихся к текущей точке входа, обработка запроса передается в основной модуль WAF вместе с результатами детектирования.
Реализованный таким образом подход к защите приложения на основе результатов анализа защищенности его кода обладает рядом существенных преимуществ по сравнению с традиционным VP:
Для обкатки технологии и подтверждения ее эффективности был разработан прототип модуля интеграции PT Application Inspector и PT Application Firewall в виде HTTP-модуля веб-сервера IIS под платформу .NET. Демонстрацию его работы с рассмотренным примером кода можно посмотреть на YouTube [6]. Тесты производительности на полутора десятках открытых CMS показали более чем приемлемые результаты: время обработки HTTP-запросов с помощью RVP оказалось сравнимо со временем их обработки традиционными (эвристическими) методами WAF. Средний процент замедления реакции веб-приложения на запросы составил:
Несмотря на очевидные преимущества перед традиционным VP, RVP все же обладает рядом концептуальных ограничений, от которых хотелось бы избавиться:
Впрочем, и эти недостатки оказалось возможным устранить, перенеся часть функциональности RVP на сторону приложения и применив технологии, лежащие в основе самозащиты приложений времени выполнения (runtime application self-protection, RASP).
Но об этом — уже во второй части статьи :)
Автор: Владимир Кочетков
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/informatsionnaya-bezopasnost/263947
Ссылки в тексте:
[1] Image: https://habrahabr.ru/company/pt/blog/338110/
[2] XSS: https://habrahabr.ru/company/pt/blog/149152/
[3] статей: https://habrahabr.ru/company/pt/blog/305000/
[4] «Трущобы AppSec»: https://www.youtube.com/watch?v=apQEQQm6GaE&feature=youtu.be
[5] традиционного taint-анализа: https://en.wikipedia.org/wiki/Taint_checking
[6] YouTube: https://www.youtube.com/watch?v=U1NbKuZkb8c
[7] Источник: https://habrahabr.ru/post/338110/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.