Статьи Тестирование web-приложений "на проникновение"

Discussion in 'Статьи' started by k00p3r, 8 Jun 2005.

  1. k00p3r

    k00p3r Banned

    Joined:
    31 May 2005
    Messages:
    430
    Likes Received:
    8
    Reputations:
    2
    Это первая из трех статей, посвященных тестированию web-приложений. В ней приведены общие сведения о web-прилолжениях - как они работают, как взаимодействуют с пользователями и, самое важное, каким образом неграмотно написанный код web-приложения может открыть доступ к закрытым данным и системам в целом.

    Замечание. Предполагается, что читатель имеет некоторое представление о протоколе HTTP - точнее, о формате запросов GET и POST и о назначении различных заголовков. Эта информация доступна в RFC2616.

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

    Сама природа web-приложений - их способность сопоставлять, обрабатывать и распространять информацию через Internet - делает их дважды уязвимыми. Во-первых, что самое очевидное, они полностью открыты по причине необходимости обеспечения публичного доступа. Это делает невозможным применение техники "security through obscurity" (безопасность за счет сокрытия информации) и повышает требование к устойчивости кода. Во-вторых, что более важно с точки зрения тестирования на проникновение, они обрабатывают даныне, полученные из запросов HTTP-протокола - протокола, который может использовать множество способов кодирования и инкапсуляции информации.

    Большинство сред, в которых выполняются приложения (включая ASP и PHP, оба из которых будут использованы в примерах), передают эти данные разработчику таким образом, что он оказывается не в состоянии определить, откуда они были получены и, следовательно, какой проверке должны быть подвергнуты. Поскольку web-среда столь разнообразна и содержит так много форм программируемого контента, контроль ввода и проверка осмысленности данных являются ключевыми факторами для обеспечения безопасности web-приложений. Это включает как выявление областей возможных значений для всех элементов данных, вводимых пользователем (и принудительное задание корректных значений), так и достаточное понимание того, откуда поступает информация, в целях определения тех данных, которые могут быть заданы пользователем.
    Основная задача: проверка ввода на корректность

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

    Как можно точно определить понятие web-приложения?

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

    Как это выглядит с точки зрения пользователя?

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

    HTTP-запрос, передаваемый типичным web-приложением, выглядит следующим образом:

    GET /sample.php?var=val&var2=val2 HTTP/1.1 | метод REQUEST-URI протокол/версия
    Session-ID: 361873127da673c | заголовок Session-ID
    Host: www.webserver.com | заголовок Host
    <CR><LF><CR><LF> | два перевода строки

    Каждый элемент этого запроса потенциально может быть использован web-приложением, его обрабатывающием. В REQUEST-URI находится адрес программной единицы, которая будет запущена для обработки запроса, а также строка со списком параметров - список пар &variable=value, определяющих входные параметры. Это основная часть входных данных web-приложения. Заголовок Session-ID содержит идентификатор сессии, установленной клиентом в качестве примитивной формы аутентификации. Заголовок Host предназначен для выбора одного из нескольких виртуальных хостов, испольующих один и тот же IP-адрес, и обычно обрабатывается web-сервером, но может быть доступен и web-приложению.

    Тестирующий должен использовать все доступные методы ввода данных для достижения исключительных условий внутри приложения. Т.е. вы не должны быть ограничены тем, что предоставляет броузел или автоматизированные средства. HTTP-запросы очень легко генерируются с помощью таких утилит как curl, или скриптов оболочки, использующих netcat. Процесс исчерпывающего тестирования по методу черного ящика включает в себя исследование каждого элемента данных, определение ожидаемого ввода, искажение его, и анализ результатов работы приложения на предмет выявления признаков неожиданного поведения.
    Этап сбора информации

    Определение окружения web-приложения

    Одним из первых шагов тестирования должно быть определение окружения, в котором исполняется web-приложение. Это включает информацию об используемых скриптовых языках, web-сервере, операционной системе. Все эти критичные детали легко выведать у типичного web-сервера следующим образом:
    Проанализировать отклик на HTTP-запросы HEAD и OPTIONS

    Заголовок и любая страница, возвращенное запросу HEAD и OPTIONS обычно содержит строчку SERVER: (или что-то аналогичное), в которой содержится информация о версии web-сервера и, возможно, информация об окружении или используемой операционной системе:

    OPTIONS / HTTP/1.0

    HTTP/1.1 200 OK
    Server: Microsoft-IIS/5.0
    Date: Wed, 04 Jun 2003 11:02:45 GMT
    MS-Author-Via: DAV
    Content-Length: 0
    Accept-Ranges: none
    DASL: <DAV:sql>
    DAV: 1, 2
    Public: OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH
    Allow: OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, LOCK, UNLOCK
    Cache-Control: private
    Исследовать формат и текст информации о 404-й ошибке сервера (и других)

    Некоторые системы (например, ColdFusion) имеют специфичные и, следовательно, легко узнаваемые сообщения об ошибках, а также часто позволяют узнать версии используемых скриптовых языков. Тестирующий должен преднамеренно запросить страницы, приводящие к подобным ошибкам, а также использовать альтернативные методы запроса (POST, PUT и т.п.) ля извлечения этой инфорамции из сервера.


    Проверить распознаваемые типы файлов/расширения/каталоги

    Многие web-серверы (например Microsoft IIS) по-разному реагируют на запросы файлов с поддерживаемыми и неизвестными расширениями. Тестирующему следует попытаться запросить файлы со стандартными расширениями, такими как .ASP, .HTM, .PHP, .EXE, и следить за появлением какого-либо необычного результата или кодов ошибок.

    GET /blah.idq HTTP/1.0

    HTTP/1.1 200 OK
    Server: Microsoft-IIS/5.0
    Date: Wed, 04 Jun 2003 11:12:24 GMT
    Content-Type: text/html

    <HTML>The IDQ file blah.idq could not be found.
    Проверить исходные тексты доступных страниц

    Исходный текст страницы, сгенерированной web-приложением, может дать намек на используемое программное окружение.

    <title>Home Page</title>
    <meta content="Microsoft Visual Studio 7.0" name="GENERATOR">
    <meta content="C#" name="CODE_LANGUAGE">
    <meta content="JavaScript" name="vs_defaultClientScript">

    В данном случае, похоже, разработчик использовал MS Visual Studio 7, а приложение, скорее всего, исполняется на Microsoft IIS 5.0 с .NET framework.


    Манипулировать входными данными для получения ошибок скриптов

    В следующем примере мы искажаем наиболее очевидную переменную (ItemID) для получения информации об окружении web-приложения:


    Использовать сканирование TCP/ICMP и сервисов

    С помощью традиционных средств сканирования, таких как Nmap и Queso, или более новые анализаторы приложений Amap и WebServerFP, тестирующий может получить более точную информацию об используемой операционной системе и используемом программном окружении, чем с помощью многих других методов. Nmap и Queso используют проверку особенностей реализации TCP/IP на исследуемом хосте для определения операционной системы и, в некоторых случаях, версий ядра и установленных патчей. Анализаторы приложений использут такую информацию как серверные HTTP-заголовки для определения запущенных на хосте приложений.

    Скрытые элементы форм и раскрытие исходных текстов

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

    Известно множество примеров безграмотно спроектированных систем, позволяющих, к примеру, сохранить локальную копию страницы с подтверждением заказа, отредактировать спрятанные переменные, в которых хранится цена/стоимость доставки, после чего отправить исправленный заказ. Web-приложение не проверяет данные, и заказ обслуживается с большой скидкой :)

    <FORM METHOD="LINK" ACTION="/shop/checkout.htm">
    <INPUT TYPE="HIDDEN" name="quoteprice" value="4.25">Quantity: <INPUT TYPE="text"
    NAME="totalnum"> <INPUT TYPE="submit" VALUE="Checkout">
    </FORM>

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

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

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

    <INPUT TYPE="SUBMIT" onClick="
    if (document.forms['product'].elements['quantity'].value >= 255) {
    document.forms['product'].elements['quantity'].value='';
    alert('Invalid quantity');
    return false;
    } else {
    return true;
    }
    ">

    Это означает, что приложение пытается защититься в обработчике формы от передачи в переменной quantity значений, больших или равных чем 255 - максимальное значение для поля с типом tinyint в большинстве баз данных. Эта проверка на стороне клиента обходится элементарно, после чего записываем в поле quantite большее значение, отправляем запрос и смотрим, не вызовет ли это какого-то нестандартного поведения приложения.

    Определение механизмов аутентификации

    Один из серьезнейших изъянов среды web-приложений - невозможность обеспечения стойкого механизма аутентификации. Еще большей проблемой является часто встречающаяся неспособность эффективно использовать доступные механизмы. Следует уточнить, что под средой web-приложений понимается множество протоколов, языков и форматов (HTTP, HTTPS, HTML, CSS, JavaScript и т.п.), которые используются в качестве платформы при создании web-приложений. HTTP обеспечивает два вида аутентификации: Basiс и Digest. Они оба реализованы в качестве серии HTTP-запросов и ответов, в рамках которой клиент запрашивает ресурс, сервер требует аутентификацию, и клиент повторяет запрос, снабдив его аутентификационным удостоверением (логин+пароль). Вся разница между ними заключается в том, что в случае Basic-аутентификации информация передается в открытом виде, а в случае Digest - шифруется с помощью ключа, сгенерированного сервером.

    Помимо очевидной проблемы использования открытого текста в случае Basic-аутентификации, здесь нет ничего принципиально плохого, да и эта проблема открытого текста смягчается при использовании HTTPS. Реальная проблема двояка. Во-первых, поскольку эта аутентификация осуществляется web-сервером, ее не так легко контролировать из web-приложения в случае отсутствия интерфейса взаимодействия с аутентификационной базой данных web-сервера. Следовательно, часто используются самодельные механизмы аутентификации, тем самым открывая настоящий ящик Пандоры. Во-вторых, разработчикам часто просто не удается корректно применить механизм аутентификации для ограничения доступа ко всем ресурсам.

    С учетом этого, тестирующий должен попытаться установить как используемый механизм аутентификации, так и то, каким образом этот механизм применен по отношении к каждому ресурсу, используемому web-приложением. Многие web-системы предлагают средства поддержки сессий, основанные на сохранении в cookie или Session-ID псевдоуникальной строки, характеризующей их статус. Такой подход может быть уязвим к таки атакам, как прямой перебор, повторная отправка, или попытка восстановления - в том случае если данная строка является простым хэшем или строкой, составленной из известных элементов.

    Следует попытаться получить доступ ко всем ресурсам через каждую точку входа. Это может выявить следующую ошибку - в то время как главное меню требует аутентификации, ресурсы, к которым оно предоставляет доступ, ее уже не требуют. Примером может послужить web-приложение, обеспечивающее доступ к различным документам следующим образом. Приложение требует пароль, после чего авторизованный пользователь получает меню с документами, и каждый документ представлен в нем как ссылка на ресурс вида http://www.server.com/showdoc.asp?docid=10 .

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

    В данной статье мы рассмотрели работу web-приложений и то, каким образом разработчики получают и обрабатывают пользовательский ввод. Мы также показали важность идентификации среды, в которой выполняется web-приложения, и важность выработки представления о его функционировании. Вооружившись этой информацией, тестирующий может перейти к целенаправленному тестированию уязвимостей. В следующей статье цикла будут рассмотрены атаки, использующие манипулирование кода и контента, такие как внедрение кодов PHP и ASP, SQL-внедрение, SSI и межсайтовый скриптинг.