фузинг частина 2

Фаззинг, частина 2 – Фаззинг за допомогою AFL

Як я писав у останній розділ, у цій статті я поясню лише те, як використовувати fuzz, коли є доступ до вихідного коду AFL. Для демонстрації я взяв стару програму з відкритим кодом, яку я знайшов на GitHub під назвою розрахунок. Як випливає з назви, це простий калькулятор, написаний мовою C. Я вибрав його, тому що ймовірно, що фаззинг легко знайде в ньому багато збоїв, і, як ви побачите, ця гіпотеза виявилася правильною.

Перш за все, нам потрібно визначити ціль. Я вирішив розтушувати функцію main() замість того, щоб шукати якусь конкретну функцію для розтушування. Це пояснюється тим, що програма аналізує необроблені вхідні дані в ряді функцій, перш ніж передати оброблені вхідні дані функціям, які фактично виконують обчислення. Оскільки програма відсіює неправильні вхідні дані під час синтаксичного аналізу, несправедливо розмивати будь-яку конкретну функцію.

Оригінальний main() виглядає більш-менш так (я трохи скоротив його):

Основна функція

Загалом, він містить дві гілки: він або отримує вхідні дані argc, argv[], або через стандартний ввід (stdin). Я не знаю чому, але творець AFL чітко рекомендує НЕ розпушувати argc, argv[], а також усі інші джерела в Інтернеті. Тому я трохи змінив це: спочатку я видалив розділ, який стосується введення, що надходить з argc, argv, а потім я спробував оптимізувати його, наскільки це можливо. Зрештою, кожного разу, коли фаззер намагається ввести інший вхід, ця функція виконується. Таким чином, невелика кількість може призвести до серйозних уповільнень. Ось кінцевий результат:

Основна функція після модифікації

Зверніть увагу на додану мною петлю. Він наказує AFL використовувати постійний режим. У стандартному режимі AFL щоразу, коли AFL запускає програми, він використовує системний виклик fork() для створення нового підпроцесу. Цей системний виклик має серйозні накладні витрати, що серйозно сповільнює весь процес фаззингу. Коли використовується постійний режим, фазер знову і знову використовує той самий процес. Єдина вимога — стирати всі змінні та буфери під час кожного запуску циклу.

Я викликав файл зі зміненою функцією harness.c, і щоб скомпілювати програму з цим файлом, а не з main.c, я змінив форму файлу automake:

автоматично створити файл перед зміною

до

automake файл після зміни

Настав час розпушити програму. Я запустив automake, щоб створити файли make, а потім make сам за допомогою команди:

команда automake

Таким чином, програма буде скомпільована з інструментарієм AFL.

Єдине, що залишилося зробити зараз, це створити початкову вхідну папку для AFL, яку я назвав «in», і вихідну папку «out». У вхідну папку поміщаються прості текстові файли (без формату), які містять допустимі введення, наприклад «20/2» або «5*4». Оскільки ми хочемо запустити кілька процесів AFL (кожен процес працює на своєму ядрі процесора), ми створюємо окрему вхідну папку для кожного процесу.

вхідну папку для кожного процесу

Для того, щоб зробити fuzz, потрібно просто виконати команду:

Команда запуску AFL для першого використання

Для першого процесу другий істота

Команда запуску AFL для другого використання

І так далі і так далі.

Як ви можете бачити на наступному знімку екрана, AFL має графічний інтерфейс, який надає важливу інформацію про процес фаззингу. Наприклад, вимірювання покриття наведено нижче покриття карти, і під висновки в глибину GUI також надає кількість збоїв і відповідну інформацію.

AFL прогрес GUI
AFL прогрес GUI

Після того, як буде знайдено достатню кількість (точна кількість залежить від вас) збоїв, як правило, після тривалого періоду часу без нового унікального збою, ви можете припинити процес. Для кожного процесу AFL, який ви запускаєте, у вихідній папці буде створено окрему папку:

Вихідна папка

У цих папках дані впорядковані таким чином:

Вивести вміст папки

Важливо пояснити, яку інформацію містить кожен із них:

  • plot_data – передає інформацію у форму, придатну для створення графіка.
  • fuzzer_stats – статистика процесу фаззингу.
  • fuzz_bitmap – растрове зображення, де кожен байт відповідає гілці програми.
    «AFL підтримує «фаз-бітове зображення», причому кожен байт у бітовому малюнку представляє кількість разів, коли певна гілка в рамках нечіткої програми була взята. AFL не виконує однозначне відображення між певною гілкою та байтом у бітовому зображенні. Натомість вбудовані інструменти AFL розміщують випадковий двобайтовий постійний ідентифікатор у кожну гілку. Кожного разу, коли виконання досягає інструментальної гілки, AFL виконує XOR ідентифікатора нової гілки та останнього ідентифікатора гілки, який бачили перед надходженням до нової гілки. Це фіксує як поточну гілку, так і унікальний шлях до неї (наприклад, коли та сама функція викликається з кількох місць у коді). Потім AFL застосовує функцію хешування до значення XOR, щоб визначити, який запис у растровому зображенні представляє цю комбінацію розгалужень. Кожного разу, коли виконується певна комбінація розгалужень, відповідний байт збільшується в бітовому зображенні».
  • cmdline – команда, яка надійшла до AFL. Практична назва програми.
  • .cur_input – поточний вхід.
  • черга – усі введення, які були спробовані до цього часу.
  • збої та зависання – результати. Кожен файл у цих папках містить дані, які спричинили збої або зависання. У назві кожного файлу вказується сигнал ядра про збій (звичайно, не має значення у зависаннях), ідентифікатор введення, використаного AFL для створення введення, яке спричинило збій, час, що минув із початку запуску AFL, і мутація, використана для генерації вхідних даних із її насіння.
Перейти до вмісту