Щороку в Канаді на конференції 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-об'єктах і де завгодно ще. Але це не головне. Важливо зрозуміти, що в багатьох місцях пошук вразливостей можна автоматизувати: саме за допомогою фаззінга знаходиться велика кількість багів в сучасних браузерах і суміжних з ними продуктів. А якщо є розуміння того, де може бути виявлена помилка і як її шукати, то фаззер вже нескладно написати самому або підібрати готове рішення.