Персональные инструменты
Вы здесь: Главная Блог Модели уязвимостей, используемые при статическом и динамическом анализе веб-приложений

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

Автор: Андрей Петухов at 2010-04-24 11:14 |

Цель и статического и динамического анализа – выявление интересующих нас свойств программы. Актуальным свойством для выявления являются уязвимости. Однако для того, чтобы что-то найти, надо сначала это что-то формально определить. В данном посте рассматриваются формальные модели уязвимостей в ПО.

Итак, мы решили найти уязвимости в программе. Возникает вопрос, как переформулировать классическое определение уязвимости (свойство, позволяющее нарушить конфиденциальность, целостность или доступность информационных ресурсов ) в терминах представлений программы (см. пост по представлениям)? Давайте попробуем разобраться.

Фундаментальное определение уязвимости в ПО было дано Деннинг аж в 1975 году и потом было развито в моделях невыводимости и невмешательства, а также их расширениях (хороший обзор есть вот тут). Больше всего нас интересует модель невмешательства. Вкратце, модель определяет уязвимость так:
Пусть программа получает данные из high-level (доверенных) и low-level (недоверенных) каналов и генерирует вывод в high-level (критические) и low-level (обычные) каналы. Вывод low-level (недоверенных) данных в high-level (критический) канал считается нарушением принципа невмешательства. 2

С этим определением уже можно работать - надо только определить, что в программе мы будем считать каналами ввода и каналами вывода.

Самый простой вариант, известный как taint analysis (или taint checking), решает задачу в лоб: недоверенные каналы ввода - это методы, возвращающие данные HTTP-запросов, а критические каналы вывода - это вызов критических функций (нам надо явно указать, каких). Модель уязвимости выглядит так:

  • данные, полученные из HTTP-запросов, считаются ненадежными; это low-level каналы ввода;
  • данные, полученные из локального хранилища данных (файловая система, СУБД), считаются надежными; это high-level каналы ввода;
  • ненадежные данные могут стать надежными в следствие специальной обработки (фильтрации);
  • ненадежные данные не должны попадать в критические операции (запросы к СУБД, вывод HTML, системные вызовы, eval'ы и т.д.); это high-level каналы вывода.

 

Это значит, для применения модели для конкретного языка и конкретного веб-фреймворка надо сделать небольшое исследование. Во-первых, найдо перечислить все конструкции, возвращающие данные HTTP-запросов (например, для PHP к этим конструкциям будут относится обращения к массивам $_GET, $_POST, $_COOKIE и т.д.) и указать их анализатору. Соответственно, анализатор будет помечать переменные, получившие свое значение из таких конструкций, как "tainted". В графе зависимостей по данным вершины, соответствующие такой инициализации, будут раскрашиваться, например, в красный цвет.

Во-вторых, надо перечислить все критические операции, аргументы для которых мы бы не хотели отдавать под контроль злоумышленника. Для PHP в этот список войдут, например, mysql_query, echo, system, eval и т.д. В графе зависимостей по данным вершины, соответствующие вызовам этих функций, будут раскрашиваться, например, в синий цвет.

В-третьих, надо перечислить все фильтрующие конструкции, которые нормализуют, а потом удаляют, экранируют или кодируют специальные символы в пользовательском вводе. Для PHP в этот список войдут, например, htmlspecialchars, addslashes, escapeshellcmd.3

Как видно, определение уязвимости в рассмотренной модели идеально ложится на представление программы в виде графа зависимостей. Фактически, задача наличия уязвимости сводится к нахождению пути в графе от вершины, помеченной как "инициализация от пользовательского ввода" (красная), до вершины (синей) вызова критической операции, между которыми нет вершины (желтой) вызова фильтрующей функции.

Второй вариант претворения базового определения невмешательства в жизнь основан на следующем наблюдении. Все веб-приложения в процессе своей работы взаимодействуют с окружением: СУБД, LDAP, браузером клиента, файловой системой, стандартным интерпретатором ОС и т.д. Многие из этих элементов окружения предоставляют специальный язык (в общем смысле этого понятия) для взаимодействия с ними: СУБД - язык SQL, LDAP - язык запросов LDAP, браузер - HTML/CSS/JavaScript/расширения, файловая система - язык именования объектов (пути), стандартный интерпретатор ОС - *nix shell/CMD shell/whatever. Большинство обращений веб-приложений к окружению параметризованы, а параметры этих обращений зависят от пользовательского ввода.

Заметим теперь, что все уязвимости к внедрению кода (HTML, SQL, OS, и т.д.) позволяют злоумышленнику изменять структуру соответствующего обращения к окружению. А это значит, что дерево синтаксического разбора соответствующего обращения при внедренном коде и без внедренного кода будет различным не только в листьях (потому что запрос параметризован), но и на более верхних ярусах. Это тезис очень хорошо проиллюстрирован на рисунке ниже.4 На нем показаны два дерева разбора WHERE-клаузы в SQL-запросах с внедрением (а) и без внедрения (b):

SELECT cardnum FROM accounts WHERE uname='John' AND cardtype=2
SELECT cardnum FROM accounts WHERE uname='John' AND cardtype=2 OR 1=1

 

Деревья разбора WHERE-клаузы для SQL-запроса без внедрения (a) и с внедрением (b)

 

Таким образом, мы приходим к формулировке принципа невмешательства в следующем виде. Пусть у нас есть язык взаимодейтсвия с окружением. Если злоумышленник может изменить структуру дерева разбора для некоторого обращения к окружению, то в приложении содержится уязвимость. 5

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

Note 1. На самом деле, здесь есть небольшое лукавство. Статические анализаторы реально ищут не уязвимости, а ошибки, которые могут стать (а могут и не стать) уязвимостями. Пример ошибок в программе, которые не являются уязвимостями:

1. Допустим, у нас есть веб-приложение, со следующими характеристиками:
  • не имеет авторизации, все интерфейсы доступны в public;
  • приложение предоставляет функции по поиску и фильтрации результатов футбольных матчей;
  • приложение обращается в СУБД только к одной таблице, с теми самыми результатами матчей;
  • для этого в СУБД заведен специальный пользователь, которому даны права только на выполнение SELECT только в этой таблице;
  • возможные ошибки СУБД корректно обрабатываются - пользователь получает обычную HTML-страницу с некими общими словами "все плохо, попробуйте еще раз".

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

2. Еще один пример - это возможность переполнения буфера в программе ls. Ошибка программистов? Безусловно. Уязвимость? Ниразу...

Note 2. Изначально принцип невмешательства формулировался по-другому: он запрещал попадание конфиденциальных данных в недоверенный канал. Потом, однако, умные люди воспользовались идеей Биба о дуализме конфиденциальности и целостности данных, и принцип невмешательства был расширен.

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

Note 4. Рисунок взят из статьи "The Essence of Command Injection Attacks in Web Applications".

Note 5. Получается, что low-level канал вывода - это нетерминалы языка, из которых выводятся только строковые и числовые константы, а high-level канал вывода - это нетерминалы языка, из которых выводятся терминалы (например, ключевые слова). В low-level канал пользователи веб-приложений могут писать сколько угодно, а вот в high-level - никак. В него может записывать только программист, задающий структуру запросов.

Действия с Документом