Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 138, ИЮНЬ 2010 г.

Фаззинг, фаззить, фаззер: ищем уязвимости в программах, сетевых сервисах, драйверах

Каждый год в Канаде на конференции CanSecWest проходит конкурс PWN2OWN по поиску багов с современных ОС и браузерах. За каждый успешный сплойт предлагается серьезное вознаграждение. И каждый год от участников, имеющих готовые решения, нет отбоя. Для поиска критических уязвимостей в столь популярном софте есть много техник и ноу-хау, но большинство из них строятся на такой простой технике как фаззинг.

Термин Fuzzing появился еще в 1988 году в работе "The Fuzz Generator", опубликованной Бартом Миллером. Именно в эту светлую голову впервые пришла идея подсовывать программе заведомо некорректные и зачастую вообще случайные данные, отлавливая ситуации, когда та не сможет их обработать и вылетит. Эффективность такого подхода до сих пор велика. Точки ввода данных в программу могут быть самые разные: текстовая строка, введенная через графический интерфейс, бинарные данные из файла или, например, значение поля в каком-нибудь сетевом запросе. Вместо программы может быть драйвер, ActiveX-компонент или, например, SWF-приложение. В той или иной мере фаззинг сейчас является одним из наиболее эффективных средств выявления проблем безопасности кода.

Что такое фаззинг?

В зависимости от того, где осуществляется манипуляции с данными, фаззинг разделяется на множество категорий. Один из самых простых видов — файловый фаззинг, подразумевающий, что некой программе предлагается открыть некорректно составленный файл. Возьмем, к примеру, прогу для просмотра картинок. Если взять JPEG-файл и интересным образом поменять несколько байтов, то эта программа вполне возможно выругается: "Что это ты мне подсунул?". А, возможно, вообще не сможет его переварить и вылетит, к примеру, с проблемой переполнения  буфера. Значит, ее теоретически можно расковырять, доведя дело до рабочего эксплойта.

Если говорить о способе манипуляции с данными, то фаззинг распределяется на генерацию и мутацию. Генерация — это случайным образом придуманный набор байтов, который подсовывается той же проге для просмотра картинок со словами: "Это на самом деле JPEG-файл, читай его". Мутация — прием намного более изящный, подразумевающий внесение изменений в "хороший", то есть вполне корректный файл. Если в случае с файловым фаззингом еще можно использовать "генерацию", то в таких вещах, как сетевые протоколы, имеет смысл применять исключительно подход мутации. Более того, крайне желательно иметь представление, за что отвечает то или иное поле пакета и намеренно манипулировать с теми данными, которые могут быть некорректно обработаны. В зависимости от интеллекта, фаззеры бывают глупые и умные:

  • Глупый (dump) фаззер ничего не знает о структуре файлов. Если говорить о сетевых протоколах, то единственное, что он может сделать — это изменить несколько байтов в исходном пакете и отправить его в надежде, что это может вызвать сбой.
  • Умный (smart) фаззер имеет некоторое представленные о структуре данных. Вместо того, чтобы полностью надеяться на удачу, он может играться только с теми данными, которые отвечают, например, за размер буфера. Или подставлять в поля такие значения, которые заведомо, с учетом известного формата, будут некорректными.

Фаззим файлы

Одна из простейших утилит для реализации глупого фаззинга —MiniFuzz. Проект разработан внутри Microsoft для тестирования своих собственных проектов. Дело в том, что использование фаззеров является обязательным этапом методологии SDL (Security Development Lifecycle), принятой в Microsoft для разработчиков безопасного кода, включающей помимо прочего обильное fuzz-тестирование. Minifuzz можно натравить на любое приложение; главное, чтобы в качестве параметра для запуска оно воспринимало указание на файл, который ему необходимо открыть (скажем, winword.exe test_sample.doc). Для начала работы необходимо набрать несколько образцов "правильных" файлов и положить их каталог, обозначенный как Template files, а также выбрать приложение для проверки, указав формат параметров для его запуска. Когда ты нажмешь на кнопку Start Fuzzing, программа возьмет один из образцов, изменит некоторые байты внутри него (количество изменяемых данных зависит от параметра Aggressiveness) и скормит его исследуемому приложению. Если тестируемая программа не вылетит через некоторый таймаут (по умолчанию 2 секунды), значит, тест пройден успешно.

Приложение будет закрыто, и начнется следующая итерация проверки. Если же во время тестирования программа вылетит (бинго!), то для анализа у тебя будет, во-первых, файл-образец, который вызвал сбой при открытии, а, во-вторых, crash-файл с дампом программы. Для большего удобства Minifuzz легко прикручивается в Visual Studio, позволяя запускать fuzz-тестирование прямо из среды разработки через меню "Tools -> MiniFuzz". Впрочем, если по каким-то причинам MiniFuzz тебе не подойдет, то можно попробовать другой инструмент для dumb-фаззинга —FileFuzz, который разработан не Microsoft, а известной security-командой iDefense Labs.

Фаззим протоколы

Если MiniFuzz — это очень простой (хотя и эффективный) dump-фаззер, то проект Peach (в переводе — персик), разработанный Майком Эддингтоном — это уже мощное решение для smart-фаззинга, поддерживающее как режим мутации, так и генерации. Для проведения умного фаззинга программе необходимо знать структуру данных, с которыми она будет экспериментировать. Поэтому на вход фаззера подаются так называемые PeachPit’ы ("косточки от персика") — специальные XML-конфиги, в которых задается структура данных, описание взаимоотношений между различными ее элементами, а также подходы для реализации мутаций. В отличие от Minifuzz, Peach может фаззить не только файлы, но и сетевые сервисы, RPC, COM/DCOM, SQL-хранимые процедуры и многое другое. Правда, такая универсальность приводит и к некоторым трудностям в использовании.

Сразу предупреждаю: это не та программа, которую запускаешь и сразу понимаешь, что к чему. Чтобы внести ясность, предлагаю разобраться с Peach на конкретном примере, но вместо фаззинга файлов обратиться к другой области, а именно — поиску уязвимостей в сетевых сервисах. Для успеха придется дополнительно установить WinDBG в качестве отладчика, а также снифер Wireshark и драйвер Winpcap, чтобы иметь возможность перехватывать сетевые пакеты во время фаззинга сетевых протоколов.

Любой фаззинг в Peach'е начинается с создания PeachPit. Как я уже сказал, в этом XML-файле определяется цель фаззинга, описывается структура данных, с которой будет работать фаззер, а также определяются правила манипуляции с ними. Для удобства автор фреймворка предлагает библиотеку для Visual Studio, серьезно упрощающую работу с PeachPit'ами, в том числе с помощью автодополнения кода. Любая "косточка" состоит из нескольких функциональных блоков. Чтобы не составлять весь файл с нуля, в корневом каталоге фаззера есть файл-шаблон template.xml, который мы и возьмем за основу.

Важная часть любого PeachPit'а — описание модели данных. Именно в этом месте мы делаем фаззер "умным", рассказываем ему о структуре файле или протокола (размерах полей, смещениях и т.д.), с которым предстоит работать. Возьмем для примера простейший протокол TFTP и попробуем профаззить запрос на чтение файла (Read). Если верить RFC, то выглядит он следующим образом:

TFTP PACKET

--------------------------------------
| \x00\x01 | Filename | 0 | Mode | 0 |
--------------------------------------

Получается, что запрос начинается с HEX-символов "\x00\x01", после которых следует название файла и флаги режима передачи файла. При этом после полей Filename и Mode идут нулевые байты. Итак, задача — написать фаззер, который будет играться со значением Filename. Начнем с создания модели запроса в нашем PeachPit'е в соответствии с RFC:

<DataModel name="tftprrx">
 <Blob name="opcode" valueType="hex" value="00 01" token="true"/>
 <String name="filename" value="filename.txt" nullTerminated="true"/>
 <String name="mode" value="NETASCII" token="true" nullTerminated="true"/>
</DataModel>

В первой строке модели мы задаем двухбайтный код, обозначающий запрос на чтение. Указанный здесь параметр token="true" мы будем использовать каждый раз, чтобы дать понять Peach, что это поле остается как есть, и его не нужно фаззить. Обрати внимание, что в следующей строке, которая описывает поле filename, этого флага как раз нет, и именно поэтому фаззер будет манипулировать со значением в этом поле (или, другими словами, фаззить). В последней строке описывается поле, обозначающее режим работы. Для полей "filename" и "mode" мы подставляем флаг nullTerminated, указывая на то, что после них идут нулевые байты-разделители. Обрати внимание, что для каждого из трех полей указывается его тип (blob или string). Таким образом мы рассказываем фаззеру, с каким типом данных он будет иметь дело. Понятно, что это очень простая модель, в большинстве случаев с ее составлением придется поработать намного плотнее.

После того, как модель данных готова, необходимо описать логику работы фаззера, которая описывается в следующем блоке PeachPit'а. Поскольку единственное место, где мы будем осуществлять фаззинг — это поле filename, то и логика нас будет очень простая. Укажем Peach'у, что необходимо отправлять данные (Action type="output"), используя ранее описанную модель данных "tftprrx":

<StateModel name="state1" initialState="Initial">
<State name="Initial">
 <Action type="output">
  <DataModel ref="tftprrx"/>
 </Action>
</State>
</StateModel>

Следующий блок конфигурации фаззера — описание агентов. Агенты присоединяют к нужному процессу отладчик и постоянно следят за его состоянием. В случае вылета приложения из-за ошибки агенты записывают различные детали падения в логфайл, в том числе и вызвавший сбой запрос (в случае, если речь идет о снифинге сетевого протокола). Для того, чтобы классифицировать падение (Exploitable, Probably Exploitable, Probably Not Exploitable, Unknown), разработчик рекомендует дополнительно к отладчику WinDBG установить плагин!exploitable. Обозначим в этом блоке, что будем отслеживать состояние приложения TFTPD32 и весь UDP-трафик, поступающий на 69 порт (TFTP):

<Agent name="RemoteAgent" location="http://192.168.1.10:9000">
<Monitor class="debugger.WindowsDebugEngine">
 <Param name="Service" value="TFTPD32" />
</Monitor>
<Monitor class="network.PcapMonitor">
 <Param name="filter" value="udp port 69" />
</Monitor>
</Agent>

Вот теперь почти все готово. Осталось связать между собой модель данных, логику и агентов, объединив их в единое целое — secuiryt-тест . У нас будет только один тест (для фаззинга поля filename), но в реальной ситуации можно написать столько тестов, сколько необходимо. По сути, нужен один тест для каждого описанного блока с моделью данных.

<Test name="tftprrx">
<Agent ref="RemoteAgent"/>
<StateModel ref="state1"/>
<Publisher class="udp.Udp">
 <Param name="host" value="192.168.1.10" />
 <Param name="port" value="69" />
</Publisher>
</Test>

После "publisher" указывается, каким образом будут передаваться данные. Поскольку TFTP работает по UDP-протоколу, то его мы и используем при составлении теста. Последний блок, который необходимо изменить в файле-шаблоне — это блок для запуска фаззера ("Run"). Здесь определяется, куда ты хочешь сохранить логи с результатами и какие тесты хочешь провести:

<Run name="DefaultRun">
<Logger class="logger.Filesystem">
 <Param name="path" value="logs"/>
</Logger>
<Test ref="tftprrx"/>
</Run>
</Peach>

Составление "косточек" для Peach'а может показаться непростой задачей, и это действительно так. А как иначе объяснить фаззеру особенности формата данных и то, каким образом ему эффективнее играться с теми или иными параметрами? В этом и есть смысл умного фаззинга. С другой стороны, если прямо сейчас попробовать реализовать другой метод того же протокола TFTP (скажем, метод write), то сразу осознаешь, что кода потребуется намного меньше — большая часть XML-конфига уже готова. Специально для проверки корректности PeachPits'ов в состав фаззера входит специальный скрипт peachvalidator.pyw. Если валидатор отдаст отмашку на старт фаззера, можно запускать Peach:

peach.py -a
peach.py tftpfuzzer.xml

Первая команда активирует агентов, а вторая позволяет запустить фаззер с использованием только что составленного XML-конфига.

Фаззим драйвера

Итак, мы уже разобрались с фаззингом файлов, протоколов — теперь попробуем использовать фаззинг для поиска ошибок в драйверах. Тут надо понимать, что драйверы используются не только для управления устройствами, вовсе нет. Многие программы устанавливают в систему драйвер в качестве посредника для доступа в более привилегированный режим — Ring0. Прежде всего, это антивирусы и утилиты, обеспечивающие (по крайней мере, обещающие обеспечить) безопасность системы. Драйверы, в общем, ничем не отличаются от программы в плане безопасности: как и везде, большое количество уязвимостей связано с неправильной обработкой данных, в особенности тех, что поступают в IRP-запросах. I/O request packets (IRP) — это специальные структуры, использующиеся моделью драйверов Windows для взаимодействия и обмена данными драйверов друг с другом и самой системой. Получается, и здесь есть все условия для того, чтобы автоматизировать поиск уязвимостей. Конечно, инструмент тут нужен совершенно особенный, потому как обычным фаззерам доступ в недра системы закрыт.

Одна из немногих разработок в этой области —IOCTL Fuzzer, которая изначально нацелена на проведение fuzzing-тестов, манипулируя с данными в IRP-запросах. Программа устанавливает в систему вспомогательный драйвер (не удивляйся, что подобную активность антивирус посчитает подозрительной), который перехватывает вызовы NtDeviceIoControlFile, тем самым получая возможность контролировать все IRP-запросы от любого приложения к драйверам режима ядра. Это нужно потому, что изначально формат IRP-запроса для конкретного драйвера или программы неизвестен. А имея на руках перехваченный IRP-запрос, его можно легко изменить — получается классический фаззинг с помощью мутации. Проспуфенный IRP-запрос ничем не отличается от оригинального за исключением поля с данными, которые заполняется фаззером псевдослучайным образом. Поведение фаззера, лог-файл, названия драйверов для спуфинга и другие параметры задаются с помощью простейшего XML-конфига, который находится в корне программы. Но прежде чем рваться в бой, необходима некоторая подготовка.

Из эксперимента с драйверами на рабочей машине ничего хорошего не выйдет. Если IOCTL Fuzzer удастся нащупать слабое место в каком-нибудь из драйверов, то система легко улетит в BSOD, а это едва ли прибавит удобства для идентификации уязвимости :). По этой причине для использования фаззера нам понадобится отдельная виртуальная машина, к которой мы подключим удаленный дебаггер ядра. Тут честь и хвала Microsoft, которые не только смогли сделать толковый отладчик WinDbg, поддерживающий удаленный дебаггинг, но и распространяют его бесплатно. Взаимодействие между гостевой системой в VMware и удаленным отладчиком WinDbg осуществляется с помощью именованного канала (named pipe), который мы сейчас и создадим.

1. Сначала создаем именованный канал в VMware. Для этого переходим в меню "Settings „p Configuration Editor", нажимаем на кнопку добавить оборудование ("Add"), выбираем "Serial Port", жмем "Next", далее из списка выбираем тип порта — "Use named pipe" и оставляем дефолтное название для именованного канала (\\.\pipe\com_1). После этого задаем режим работы "This end is server. The other end is application" в двух выпадающих полях и напоследок нажимаем кнопку "Advanced", где активируем опцию "Yiled CPU on poll" (иначе ничего не заработает).

Осталось реализовать возможность загрузки гостевой системы с включенным режимом удаленной отладки. Для этого в boot.ini (будем считать, что в качестве гостевой системы используется Windows XP) необходимо вставить новую строку для запуска системы, добавив два важных ключа /debugport и /baudrate:

[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional - Debug" /fastdetect /debugport=com1 /baudrate=115200

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

windbg -b -k com:pipe,port=\\.\pipe\com_1,resets=0

Вот теперь можно запускать IOCTL Fuzzer в режиме фаззинга, не опасаясь BSOD’а на основной системе. Выполняем произвольные манипуляции с тестируемым ПО до тех пор, пока отладчик не сообщит нам о возникновении необрабатываемого исключения (это значит, что в обычных условиях, скорее всего, это закончилось бы аварийным завершением работы системы).

Далее необходимо возобновить выполнение кода на виртуальной машине (в случае с WinDbg надо просто нажать F5), после чего ОС, работающая на виртуальной машине, запишет аварийный дамп (crash dump) на диск. Готово: теперь у нас есть подробные логи, дамп и сам запрос, который привел к падению. Дело за малым — понять, как это можно эксплуатировать :).

А как же веб-фаззеры?

Я намеренно не стал упоминать в рамках этой статьи так называемые web-based фаззеры, которые работают на уровне HTTP и заваливают веб-сервер специально составленными запросами в поиске ошибок веб-приложения. Такая опция есть в каждом втором сканере веб-безопасности, которые мы не так давно рассматривали в рамках цикла "Инструменты пентестера". Если говорить об универсальных платформах для создания фаззеров, то грех не вспомнить о фреймворкеSulley, представленном на Blackhat'е в 2007 году. К сожалению, с тех самых пор он и не развивается, но несмотря на это остается эффективным решением.

Каждый отдельный фаззер с его помощью конструируется отдельно, но в отличие от Peach, где все описывается декларативно в XML-файле, здесь тебе придется написать немного кода на Python. Есть еще один популярный конструктор фаззеров — проектSPIKE, но подружиться с ним смогут только те, кто хорошо знает язык C. Помимо этого можно было долго говорить о фаззерах для поиска уязвимостей в ActiveX, COM-объектах и где угодно еще. Но это не главное. Важно понять, что во многих местах поиск уязвимостей можно автоматизировать: именно с помощью фаззинга находится большое количество багов в современных браузерах и смежных с ними продуктов. А если есть понимание того, где может быть выявлена ошибка и как ее искать, то фаззер уже несложно написать самому или подобрать готовое решение.

WARNING

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

Содержание
загрузка...
Журнал Хакер #151Журнал Хакер #150Журнал Хакер #149Журнал Хакер #148Журнал Хакер #147Журнал Хакер #146Журнал Хакер #145Журнал Хакер #144Журнал Хакер #143Журнал Хакер #142Журнал Хакер #141Журнал Хакер #140Журнал Хакер #139Журнал Хакер #138Журнал Хакер #137Журнал Хакер #136Журнал Хакер #135Журнал Хакер #134Журнал Хакер #133Журнал Хакер #132Журнал Хакер #131Журнал Хакер #130Журнал Хакер #129Журнал Хакер #128Журнал Хакер #127Журнал Хакер #126Журнал Хакер #125Журнал Хакер #124Журнал Хакер #123Журнал Хакер #122Журнал Хакер #121Журнал Хакер #120Журнал Хакер #119Журнал Хакер #118Журнал Хакер #117Журнал Хакер #116Журнал Хакер #115Журнал Хакер #114Журнал Хакер #113Журнал Хакер #112Журнал Хакер #111Журнал Хакер #110Журнал Хакер #109Журнал Хакер #108Журнал Хакер #107Журнал Хакер #106Журнал Хакер #105Журнал Хакер #104Журнал Хакер #103Журнал Хакер #102Журнал Хакер #101Журнал Хакер #100Журнал Хакер #099Журнал Хакер #098Журнал Хакер #097Журнал Хакер #096Журнал Хакер #095Журнал Хакер #094Журнал Хакер #093Журнал Хакер #092Журнал Хакер #091Журнал Хакер #090Журнал Хакер #089Журнал Хакер #088Журнал Хакер #087Журнал Хакер #086Журнал Хакер #085Журнал Хакер #084Журнал Хакер #083Журнал Хакер #082Журнал Хакер #081Журнал Хакер #080Журнал Хакер #079Журнал Хакер #078Журнал Хакер #077Журнал Хакер #076Журнал Хакер #075Журнал Хакер #074Журнал Хакер #073Журнал Хакер #072Журнал Хакер #071Журнал Хакер #070Журнал Хакер #069Журнал Хакер #068Журнал Хакер #067Журнал Хакер #066Журнал Хакер #065Журнал Хакер #064Журнал Хакер #063Журнал Хакер #062Журнал Хакер #061Журнал Хакер #060Журнал Хакер #059Журнал Хакер #058Журнал Хакер #057Журнал Хакер #056Журнал Хакер #055Журнал Хакер #054Журнал Хакер #053Журнал Хакер #052Журнал Хакер #051Журнал Хакер #050Журнал Хакер #049Журнал Хакер #048Журнал Хакер #047Журнал Хакер #046Журнал Хакер #045Журнал Хакер #044Журнал Хакер #043Журнал Хакер #042Журнал Хакер #041Журнал Хакер #040Журнал Хакер #039Журнал Хакер #038Журнал Хакер #037Журнал Хакер #036Журнал Хакер #035Журнал Хакер #034Журнал Хакер #033Журнал Хакер #032Журнал Хакер #031Журнал Хакер #030Журнал Хакер #029Журнал Хакер #028Журнал Хакер #027Журнал Хакер #026Журнал Хакер #025Журнал Хакер #024Журнал Хакер #023Журнал Хакер #022Журнал Хакер #021Журнал Хакер #020Журнал Хакер #019Журнал Хакер #018Журнал Хакер #017Журнал Хакер #016Журнал Хакер #015Журнал Хакер #014Журнал Хакер #013Журнал Хакер #012Журнал Хакер #011Журнал Хакер #010Журнал Хакер #009Журнал Хакер #008Журнал Хакер #007Журнал Хакер #006Журнал Хакер #005Журнал Хакер #004Журнал Хакер #003Журнал Хакер #002Журнал Хакер #001