На главную | Содержание | Назад | Вперёд
Наши друзья

 

 

Вернемся к Windows-программе APISPY

Практически для всех платформ существуют встроенные или специально созданные средства для отслеживания вызовов функций API. Вспомним хотя бы о программе Truss для платформы Solaris из главы 4, "Взлом серверных приложений". Много таких программ создано и для платформы Windows. В главе 3, "Восстановление исходного кода и структуры программы", мы рассмотрели использование программы APISPY32 для выявления всех вызовов функции strcpy, которые делала атакуемая программа при работе с SQL-сервером от Microsoft. Напомним, что мы выбрали этот вызов, поскольку с его помощью становится возможным проведение атаки на переполнение буфера, если строка входных данных задается злоумышленником. Приведенный простой пример включает в себя одновременное исследование двух фрагментов программного обеспечения: исполняемого файла сервера SQL и системной библиотеки kernel3 2 . dll.
Наиболее очевидный метод восстановления исходного кода программного обеспечения заключается в инвентаризации всех точек входа и выхода из программы и поиске интересующих фрагментов кода. На момент создания этой книги было доступно несколько хороших средств, которые уместно привести в описываемом нами исследовании. Можно создать электронную таблицу или написать программу, которая будет отслеживать все вызовы функций, при которых используются введенные пользователем данные. Большинство хакеров используют карандаш и листок бумаги для записи адресов, из которых вызываются интересные функции, например WSARecvO или fread(). Программа наподобие IDA-Pro позволяет создать комментарии для кода, полученного в результате дизассемблирования программы, что намного лучше, чем ничего. При исследовании кода также проверяйте все точки выхода, включая вызовы функций наподобие WSASend () и fwrite (). Обратите внимание, что исходящие данные иногда принимают форму системных вызовов.
Поиск ключевых мест в коде
Самый простой и самый быстрый метод восстановления исходного кода называют поиском ключевых мест (red pointing). Опытный инженер по восстановлению исходного кода просто просматривает программный код в поисках очевидно уязвимых мест, например вызовов функции strep у () и т.п. После выявления этих областей в коде проводится предварительно подготовленная атака для того, чтобы заставить программу перейти в эту потенциально уязвимую область кода во время исполнения. Проще всего это сделать с помощью программы для отслеживания вызовов функций API. Если конкретные области кода нельзя отследить с помощью простых средств, целесообразно воспользоваться отладчиком.
Наличие ключевого места в коде определяется двумя условиями: во-первых, это должна быть уязвимая область кода, в которой присутствует потенциально опасный вызов функции, и во-вторых, в этой области кода должны обрабатываться данные, предоставленные пользователем. Достаточно даже небольшой практики, чтобы научиться выявлять уязвимые места и понимать, какие входные данные могут быть обработаны в конкретной атакуемой области кода. С накоплением опыта эта задача значительно упрощается.
Основным свойством процесса выделения ключевых мест в коде можно назвать простоту этого процесса. Однако эта простота может показаться не такой привлекательной, когда после нескольких часов поиска не находится ни одного интересного места в коде. Иногда этот метод не дает никаких результатов. С другой стороны, иногда уязвимый код обнаруживается практически мгновенно.
Главный недостаток данного метода заключается в том, что пропускается практически все, кроме самых распространенных ошибок. Еще раз напомним, что в огромном количестве программ есть ошибки, что делает эту простую технологию весьма эффективной.
Чтобы повысить ваши шансы на успех путем выделения ключевых мест в коде, далее в этой книге мы расскажем о нескольких методах исследования программ: о поиске ключевых мест в коде, обратной трассировке и отслеживании входных данных.
Трассировка
Независимо от того, сколько хакеров хотели бы, чтобы все было так просто, как поиск ключевых мест, неизменным остается тот факт, что если вы хотите найти интересные возможности для атаки, придется основательно "закопаться" в программный код, т.е. необходимо отслеживание входных данных, что является весьма утомительной задачей. Одна из причин, по которой многие простые ошибки остаются в установленном программном обеспечении, состоит в том, что ни у кого не хватает терпения внимательно просмотреть весь программный код, как это делает настоящий хакер. Даже автоматизированные средства не позволяют найти все уязвимые места в программах.
Человеческий мозг работает ужасно медленно, но пока остается наилучшим из всех известных нам аналитических средств. Большинство уязвимых мест не являются шаблонными, и их нельзя выявить по заданному алгоритму, т.е. они не подходят под удобный для использования шаблон, который может быть встроен в автоматизированное программное средство. Люди по-прежнему остаются наилучшим инструментом для выявления уязвимых мест.
Проблема не только в том, что люди работают медленно, кроме того, их труд стоит достаточно дорого. Это означает, что выявление уязвимых мест остается дорогостоящим занятием. Тем не менее, следует всегда проводить аудит. Выявление уязвимого места в продаваемой программе может обойтись поставщику более чем в 100 тыс. долл., особенно если учесть общественную реакцию, установку заплат и техническую поддержку, не говоря уже о предоставлении ключей к "компьютерному королевству" для некоторых хакеров. Если посмотреть на ситуацию с другой стороны, то хакер, имеющий возможность доступа к удаленной программе, в которой присутствует уязвимое место, действительно как бы получает ключи от "компьютерного королевства" (особенно если уязвимое место обнаруживается в широко распространенной программе наподобие BIND, Apache или IIS).
Обратная трассировка из уязвимого места
Предположим, что мы обнаружили некоторые важные разделы в программе и начинаем их исследование на предмет наличия уязвимых мест. Использовать нашу хитрость для выявления вызова функции достаточно просто: нужно лишь запустить код для обработки каких-то тестовых входных данных и надеяться увидеть данные использованными в интересующем вызове. Безусловно, в реальном мире дела обстоят не так просто. Чаще всего приходится внимательно подготавливать входные данные, используя специальные символы и/или запросы определенного типа.
В любом случае, текущая цель заключается в поиске уязвимых мест, доступ к которым можно получить извне, т.е. с помощью входных данных, которые проходят через "границу" раздела. Например, если нас интересуют только библиотеки DLL, то нам необходимо найти все уязвимые места, доступ к которым можно получить посредством экспортируемых в DLL вызовов функций. Это будет очень полезно, поскольку потом мы сможем найти все программы, которые используют данную библиотеку DLL, и определить, как на них могут повлиять выявленные нами уязвимые места.
Первый шаг в обратной трассировке — это определить потенциально уязвимые вызовы функций. Если вы не уверены, что данный вызов является уязвимым, то напишите небольшую программку для проверки этого вызова. Это прекрасный способ изучения. Затем напишите отдельную программу, которая выдает все возможные входные данные как аргументы и отправляет результаты вызову функции. Опреде­лите, какие аргументы приводят к возникновению проблем и начинайте исследование исходя из этих сведений. Возможно, ваша небольшая программа аварийно завершит работу или выходной вызов функции сможет сделать что-то, что будет считаться нарушением принципов безопасности (например чтение файла). После этого нужно записать символы, которые приводят к проблемам при вызове функции (которые мы называем набором вредоносных символов) и все подобные строки (которые мы называем набором вредоносных выражений). После определения наборов вредоносных символов и выражений, можно начать обратную трассировку в атакуемой программе с целью узнать, где еще этот набор может быть внедрен хакером извне программы.
Чтобы начать обратную трассировку из атакуемой области, воздействуем на атакуемую программу в точках, удаленных от переходов между блоками кода в дереве управляющей логики программы (как правило, с помощью установки точек останова при использовании отладчика). Затем внедряем входные данные, содержащие вредоносные символы и комбинации команд (с помощью клиентской программы). Если входные данные достигают вызова, значит, дело сделано. Теперь можно изучить этот выявленный "уязвимый раздел". Обратите внимание, что мы все делаем вне внутреннего уязвимого места. Если вредоносные входные данные, поступающие через точку входа в новой ограниченной области кода, блокируются, то мы говорим о "проходных" разделах.
На 2 изображены три раздела программного кода. В первом разделе осуществляется обработка пользовательских данных, которые затем фильтруются и, возможно, блокируются во втором разделе, до того, как мы достигнем своей цели в третьем разделе (в котором находится уязвимая область кода). Возвращаясь к нашему предыдущему примеру, мы хотим, чтобы ограничения DLL были разрушены до того, как мы выйдем из уязвимой области кода.
На 3 показан пример обратной трассировки кода в библиотеке irc.dll, которая поставляется совместно с программой Trillian — популярным клиентом для общения пользователей в чате. Уязвимое место, на которое мы нацелились, было связано с ошибкой несовпадения знака. Обратная трассировка позволила узнать о наличии оператора switch выше подозрительной области кода.
Цель заключается в доставке пользовательских входных данных в уязвимую область кода. Один из методов заключается в продолжении обратной трассировки до тех пор, пока не будет обнаружена известная точка входа, например вызов функции WSARecv. Если можно осуществить обратную трассировку до подобного вызова, оставаясь в "уязвимом разделе" с помощью специальных команд, значит, вы обнаружили действительно уязвимое место (обратите внимание, что описанный нами способ весьма утомительный и требует много времени).
Если процесс обратной трассировки покажется вам слишком сложным, то вполне реально воспользоваться другим методом, который заключается в обратной трассировке до определения набора крупных разделов в программе. Затем можно провести прямую трассировку от действительных точек входа, чтобы определить возможность достижения любого из обнаруженных крупных разделов. Таким образом можно ускорить процесс поиска возможной атаки, двигаясь с противоположных концов как бы навстречу. Если можно добраться до уязвимого раздела с помощью вредоносных команд, значит, с помощью вредоносной команды можно провести всю атаку, начиная с точки входа и заканчивая желаемым событием на выходе.
Все описанные методы, безусловно, должны быть проверены на практике, но изложенный нами поиск возможности проведения атаки, несомненно, приносит результат. Этот метод намного интеллектуальнее простого поиска возможных атак с помощью перебора различных вредоносных входных данных по методу "черного ящика" (на котором основаны многие из первых программ по обеспечению безопас­ности, доступные на современном рынке программного обеспечения).
Тупиковые пути
При проведении обратной трассировки серьезной проблемой является тенденция к отрицательным результатам поиска. Это означает, что вы можете быстро двигаться к заветной цели и внезапно оказаться в тупике. Например, невозможно понять, откуда поступают данные. Один из способов обойти эту проблему заключается в том, чтобы запустить программу и непосредственно просмотреть программный код в тупиковом пути.
Например, это может пригодиться при исследовании подкачки сообщений в Windows-системах. Если проводится обратная трассировка обработчика сообщений Windows-системы, то иногда достаточно сложно определить, откуда поступают сообщения (и откуда они были отправлены). Однако во время выполнения программы можно без особого труда увидеть, откуда поступает сообщение, поскольку необхо­димые данные можно найти в стеке вызовов.

 

На главную | Содержание | Назад | Вперёд
 
Яндекс.Метрика