Ку, киберпанки. На днях решил заняться вопросом фаззинга ПЕшек. Находил статьи на хакере и почти везде идет обзор гугловского форка AFL++ - WinAFL. Однако, во всех этих статьях нет практики, чтобы чисто попоробовать. Буквально на днях нашел на английском языке обзор инструмента WinAFL и посчитал, что это будет преступлением не поделиться переводом.
Автор оргинала: 2ourc3
Оригинальная статья: The art of fuzzing: Windows Binaries
Введение
Сегодня мы погрузимся в grey-box fuzzing, тестируя двоичные файлы Windows с закрытым исходным кодом. Этот тип фаззинга позволяет проверить цель, не имея доступа к ее исходному коду. Зачем заниматься таким типом фаззинга? Поскольку он требует больше настроек и продвинутых навыков, меньше людей склонны искать уязвимости / уметь их находить. Таким образом, это расширяет возможности для вас, исследователя уязвимостей, по поиску новых необнаруженных узявимостей.Чтобы добиться этого, нам придется преодолеть несколько трудностей:
- Инструментирование кода.
- Найти релевантную функцию для фаззинга.
- Модификация/патчинг бинарного кода, чтобы сделать его пригодным для фаззинга.
- Динамическое инструментирование через DynamoRIO. Динамическое инструментирование - это изменение инструкций программы во время ее выполнения.
- Статический инструментарий через Syzygy - статический инструментарий, изменяющий инструкции программы во время компиляции.
- Аппаратная трассировка с помощью Intel PTrace - аппаратная функция, которая асинхронно записывает поток управления программой.
Компиляция WinAFL
- Если вы собираете с поддержкой DynamoRIO, загрузите и соберите исходные тексты DynamoRIO или загрузите бинарный пакет DynamoRIO Windows с сайта https://github.com/DynamoRIO/dynamorio/releases
- Если вы собираете с поддержкой Intel PT, извлеките сторонние зависимости, выполнив git submodule update --init --recursive из каталога исходных текстов WinAFL
- Откройте командную строку Visual Studio (или Visual Studio x64 Win64 Command Prompt, если вам нужна 64-битная сборка). Обратите внимание, что вам нужна 64-битная сборка winafl.dll, если вы проверяете 64-битные цели, и наоборот.
- Перейдите в каталог, содержащий исходный код
- Введите следующие команды. Измените флаг -DDynamoRIO_DIR, чтобы он указывал на местоположение ваших файлов DynamoRIO cmake (либо полный путь, либо относительно исходного каталога)
Bash:
mkdir build32
cd build32
cmake -G"Visual Studio 16 2019" -A Win32 .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake -DINTELPT=1
cmake --build . --config Release
Для 64-битной сборки:
Bash:
mkdir build64
cd build64
cmake -G"Visual Studio 16 2019" -A x64 .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake -DINTELPT=1
cmake --build . --config Release
Параметры конфигурации сборки
Поддерживаются следующие параметры конфигурации cmake:
- -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake- необходим для сборки клиента DynamoRIO winafl.dll.
- -DINTELPT=1 - включить режим Intel PT. Для получения дополнительной информации см. https://github.com/googleprojectzero/winafl/blob/master/readme_pt.md
- -DUSE_COLOR=1 - поддержка цвета (Windows 10 Anniversary edition или выше)
- -DUSE_DRSYMS=1 - поддержка Drsyms (используйте символы, если они доступны для получения -target_offset из -target_method). Известно, что включение этого параметра приводит к проблемам в Windows 10 v1809, но есть обходные пути, см. #145
Поиск жертвы
Найти подходящую цель для фаззинга не всегда просто. Главное - найти достаточно сложное программное обеспечение, чтобы его можно было протестировать, но при этом достаточно доступное, чтобы вы могли понять, что именно нужно тестировать и какие функции интересны.
Одной из хороших стратегий является поиск программ, которые, как известно, содержат уязвимости и реагируют на них в рамках программы раскрытия информации. Хороший способ найти такие программы - заглянуть на сайт Zero Day Initiative.
В этом разделе находятся ранее раскрытые ошибки, которые могут дать вам хорошее общее представление о том, какие программы тестируются и на что они реагируют. Здесь мы видим, что уязвимость была раскрыта для продуктов Netgear и D-link. На этом сайте есть тонны ранее раскрытых уязвимостей, и вам решать, что искать в них и находить цель, которая интересует вас больше всего.
Поскольку фаззинг сложных целей требует некоторых продвинутых навыков, таких как реверс-инжиниринг, понимание большой базы кода и т.д., мы сосредоточимся на бинарной цели, которую я специально создал для целей этого курса. Это уязвимый файл-ридер, он принимает файл на вход, копирует его содержимое в буфер и закрывает файл.
Вы можете скачать файл здесь, пароль: "bushido". https://drive.google.com/file/d/1c-cOuzYbC-gOFW91a2EHKNpZTiPrVdBP/view?usp=sharing. В приложении будет лежать этот таргет.
Патчинг бинаря для обеспечения возможности фаззинга
К сожалению, во многих программах используются диалоговые окна, в которых пользователю предлагается ответить на вопрос перед выполнением определенной задачи, например "Этот файл уже существует. Хотите ли вы перезаписать его?" и т.д.
Это делает процесс фаззинга невозможным, так как требует от пользователя взаимодействия с диалоговым окном, что не позволит фаззеру нормально работать. Именно поэтому мы сейчас ищем, как улучшить/исправить бинарник, чтобы сделать его фаззабельным!
Скачайте и установите Ghidra, запустите приложение, затем создайте каталог проекта и проект. Импортируйте файл vulnerable_reader.exe, нажмите на "Options..." и включите "Load Local Libraries From Disk".
После загрузки библиотек вы можете начать реверс, нажав клавишу Enter или дважды щелкнув на имени файла, в результате чего появится диалоговое окно "Анализ", которое можно настроить.
Для данного упражнения менять ее не нужно, однако я приглашаю вас изучить доступные опции и их возможности. После нажатия кнопки OK вы увидите код разборки двоичного файла, вам нужно будет немного подождать, пока Ghidra проанализирует весь двоичный файл, индикатор выполнения можно найти в правом нижнем углу экрана:
Если вы сохраните свою программу после анализа, вам не придется анализировать ее снова в будущем. Этот двоичный код довольно мал, но имейте в виду, что так будет не всегда. Я рекомендую вам сохранять проанализированную программу в виде копии сразу после проведения анализа.
Теперь, когда анализ выполнен, мы можем посмотреть на программу. Исследовать этот бинарник будет довольно просто, так как мы уже знаем одну строку, используемую в диалоговом окне, давайте откроем Search > Program text, затем введем "You clicked Yes!", в fields включим all fields, а в блоке включим all blocks, затем нажмем Search all и дважды щелкнем по первой находке в результатах.
Мы видим, что возможны два варианта: либо функция позволяет выбрать "да" и закрыть, либо "нет" и закрыть. Реального назначения у этой функции нет, однако она не позволяет программе продолжить выполнение до того, как вы нажмете на кнопку мыши, и, следовательно, не дает вам запустить ее.
Интересной информацией, на которую стоит обратить внимание, являются XREFS, которые соответствуют месту, где вызывается эта функция (FUN_00401000). Здесь мы видим, что функция вызывается FUN_00401130, давайте дважды щелкнем и посмотрим, что это за функция.
Похоже, что эта функция, по сути, является нашей главной функцией. Она принимает два параметра в качестве аргументов и передает их второй функции. Первая функция отвечает за диалоговое окно.
Давайте заменим инструкцию "CALL FUN_00401000" на инструкцию NOP:
Как видите, теперь после нашей инструкции идет куча "?". Это потому, что начальная инструкция была больше, чем инструкция NOP (в шестнадцатеричном формате: 90), поэтому нам нужно заменить "?" на инструкцию NOP, чтобы соблюсти падинг. Дополнительная информация https://en.wikipedia.org/wiki/Data_structure_alignment
Результат должен выглядеть следующим образом:
Теперь экспортируйте программу в PE-файл, нажмите File > Export Program, затем выберите Original File и укажите правильный путь.
Запустим программу и посмотрим, не появится ли диалоговое окно снова:
Браво! Имейте в виду, что в большинстве программ требуются гораздо более сложные взаимодействия, и этот курс не посвящен реверс-инжинирингу. Однако большой аспект успешного фаззинга заключается в устранении того, что делает фаззер медленнее, и GUI является большой частью этого. Если вы хотите продолжить исследования в области фаззинга, вы должны иметь некоторый интерес к RE.
Смещение функций
WinAFL использует технику оптимизации процесса фаззинга, уменьшая медленное время выполнения, связанное с системным вызовом exec и типичной процедурой запуска процесса. Вместо того чтобы заново инициализировать целевую программу при каждой попытке фаззинга, она использует стратегию, вдохновленную концепцией форк-сервера.Основная идея заключается в выполнении программы до достижения желаемой точки фаззинга путем подачи рандомизированных входных данных. Благодаря такому подходу каждый подпроцесс обрабатывает только один вход, что позволяет обойти накладные расходы, связанные с операцией точного вызова системы.
В результате, при фаззинге программы с помощью WinAFL, если желаемая точка фаззинга достигается, например, во время третьего вызова, производительность остается неизменной. Однако существенное преимущество заключается в снижении накладных расходов на фаззинг всей программы, что приводит к более эффективным и результативным сеансам фаззинга.
Вот схема, иллюстрирующая этот процесс.
Как выбрать таргет-функцию?
Таргет-функция должна выполнять эти действия в течение всего времени своего существования:
- Откройте входной файл. Это должно происходить внутри целевой функции, чтобы вы могли читать новый входной файл для каждой итерации, поскольку входной файл переписывается между запусками таргет-функции.
- Разобрать его (чтобы можно было измерить охват разбора файла).
- Закройте входной файл. Это важно, потому что если входной файл не закрыт, WinAFL не сможет его переписать.
- Вернуться нормально (чтобы WinAFL мог "поймать" этот возврат и перенаправить выполнение. "Возврат" через ExitProcess() и тому подобное работать не будет)
- Статический анализ с помощью таких инструментов, как Ghidra и radare2
- Отладка кода с помощью WinDBG или x64dbg (установка точек останова и анализ параметров функций во время выполнения).
- Использование вспомогательных инструментов, таких как мониторы API, мониторы процессов и инструменты покрытия, такие как ProcMon.
Двоичный файл содержит несколько строк, одна из них - "Failed to open file", давайте щелкнем меню Search, затем Program Text и найдем это предложение:
Давайте нажмем кнопку Search all и изучим результат:
Давайте дважды щелкнем по первому вхождению в пространство имен FUN_00401060
Помните, что искомый поток выполнения выглядит следующим образом: Открыть файл > Прочитать его > Закрыть файл > вернуться к нормальному выполнению. Давайте проверим, происходит ли этот поток в псевдокоде функции. В упрощенном виде он дает нам:
C-подобный:
void __cdecl FUN_00401060(int argc, int argv)
{
uint openResult;
uint readResult;
WCHAR fileContentBuffer[6]; // Buffer to store file content
uint localVariable;
localVariable = DAT_0041c040 ^ (uint)&stack0xfffffffc;
if (argc < 2) {
FUN_00401130((int)s_Usage:_%s_<filename>_0041c000); // Print usage message
}
else {
openResult = FID_conflict:__open(*(char **)(argv + 4), 0x8000); // Open file specified in argv[1]
if ((int)openResult < 0) {
FUN_00401130((int)s_Failed_to_open_file:_%s_0041c018); // Print error message if file opening fails
}
else {
while (readResult = FUN_00406348(openResult, fileContentBuffer, 10), 0 < (int)readResult) {
FUN_00401130((int)&DAT_0041c034); // Print file contents
}
FUN_00407b70(openResult); // Close the file
}
}
FUN_0040116a(localVariable ^ (uint)&stack0xfffffffc); // Some additional function call
return;
}
Звучит как совпадение! Теперь давайте найдем смещение этой функции. Это довольно просто, давайте щелкнем правой кнопкой мыши на функции и покажем байт. Мы видим, что адрес функции - 0x00401060, а базовый адрес - 0x0040000, поэтому смещение функции - 0x01060.
Шпора GHIDRA: https://ghidra-sre.org/CheatSheet.html
Подготовьте среду для фаззинга
Фаззинг бинарных файлов - довольно ресурсоемкая задача, и вот несколько вещей, которые вы можете сделать, чтобы подготовить свое окружение к бесперебойной работе фаззинга:
- Отключение автоматической отладки
- Отключение антивирусного сканирования
Оптимизация
Наличие хорошего корпуса входных данных - очень важный аспект фаззинга. WinAFL предлагает два варианта оптимизации корпуса с помощью c-min.py. Примеры использования:- Типичное использование
winafl-cmin.py -D D:\DRIO\bin32 -t 100000 -i in -o minset -covtype edge -coverage_module m.dll -target_module test.exe -target_method fuzz -nargs 2 — test.exe @@- Запуск с сохранением крашей только с 4 рабочими с рабочим каталогом:
winafl-cmin.py -C –dry-run -w 4 –working-dir D:\dir -D D:\DRIO\bin32 -t 10000 -i in -i C:\fuzz\in -o out_mini -covtype edge -coverage_module m.dll -target_module test.exe -target_method fuzz -nargs 2 — test.exe @@- Чтение из определенного файла
winafl-cmin.py -D D:\DRIO\bin32 -t 100000 -i in -o minset -f foo.ext -covtype edge -coverage_module m.dll -target_module test.exe -target_method fuzz -nargs 2 — test.exe @@- Чтение из определенного файла с шаблоном
winafl-cmin.py -D D:\DRIO\bin32 -t 100000 -i in -o minset -f prefix-@@-foo.ext -covtype edge -coverage_module m.dll -target_module test.exe -target_method fuzz -nargs 2 — test.exe @@- Типичное применение со статическими приборами
nafl-cmin.py -Y -t 100000 -i in -o minset — test.exe @@Запуск winafl-cmin.py может занять некоторое время, поэтому наберитесь терпения.
Запуск фаззера
Мы запачили бинарь, чтобы сделать его пригодным для фаззинга, нашли смещение функции, которую хотим протестировать, теперь давайте повеселимся и запустим фаззер! WinAFL предлагает различные опции, давайте перечислим их:
- t - таймаут на одну итерацию фаззинга. Если он не завершен, WinAFL перезапускает программу;
- D - путь к DynamoRIO
- coverage_module - Модуль(и), записывающий(ие) покрытие.
- target_module - Модуль целевой функции.
- target_offset - Виртуальное смещение функции для фаззинга от начала модуля;
- fuzz_iterations - Итерации фаззинга перед перезапуском выполнения программы.
- call_convention - указание конвекции вызова: sdtcall, cdecl и thiscall.
- nargs - количество аргументов, которые принимает функция для фаззинга. Указатель this (используемый в конвенции вызова thiscall) также считается аргументом.
ВАЖНО! Помните, что нужно использовать правильную версию AFL для цели, которую вы хотите профаззить! Здесь мы будем использовать 32-битную версию!
Теперь давайте перейдем в каталог сборки WinAFL_32 и выполним следующую команду:
afl-fuzz.exe -i in -o out -t 10000 -D C:\WinAFL\DynamoRIO\bin32 -- -fuzz_iterations 500 -coverage_module vulnerable_reader.exe -target_module vulnerable_reader.exe -target_offset 0x01060 -nargs 3 -call_convention thiscall -- vulnerable_reader.exe @@Если все прошло успешно, вы должны увидеть, как появляется эта красота:
Теперь дело за временем. Дайте фаззеру поработать несколько минут, после чего вы должны увидеть, как появляются краши.
Анализ краша
Здесь WinAFL очень быстро обнаружил сбой. Я специально создал бинарник, очень простой для аварийного завершения работы, чтобы этот учебник было интересно делать. Как вы видите, WinAFL присваивает файлу сбойного кода имя со статусом и типом сбоя. Вы можете найти их в вашем каталоге out > crashes:
Очевидно, что это Stack BoF, поскольку программа была специально разработана для этого. Однако давайте откроем ее в WinDBG и проведем анализ первопричины сбоя.
Запустите WinDBG и нажмите File > Launch Executable (advanced), затем укажите путь к уязвимому бинарному файлу как Executable и файл crash_id как Arguments, затем нажмите Go, чтобы запустить программу.
Как вы можете видеть, WinDBG сразу же кричит, что обнаружен выход за пределы буфера стека. Если вы хотите узнать больше об анализе первопричин с помощью WinDBG, предлагаю вам это замечательное видео: https://hardik05.wordpress.com/2021...th-time-travel-debugging-with-windbg-preview/.
Эксплуатация
Эта статья не предназначена для того, чтобы научить вас эксплуатации, однако существует множество очень хороших ресурсов на эту тему, и мне показалось интересным перечислить некоторые из них здесь:
https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/
https://www.corelan.be/index.php/20...w-exploits-a-quick-and-basic-tutorial-part-2/
https://www.corelan.be/index.php/20...ploits-a-quick-and-basic-tutorial-part-3-seh/
https://www.corelan.be/index.php/20...orial-continued-just-another-example-part-3b/
https://www.corelan.be/index.php/20...part-4-from-exploit-to-metasploit-the-basics/
https://www.corelan.be/index.php/20...ugins-can-speed-up-basic-exploit-development/
Ссылки
- WinAFL – https://github.com/googleprojectzero/winafl
DynamoRIO – https://dynamorio.org/
Syzygy – https://github.com/google/syzygy/wiki
Intel PTrace – https://edc.intel.com/content/www/u...heet-volume-1-of-2/004/intel-processor-trace/
ProcMon – learn.microsoft.com/en-us/sysinternals/downloads/procmon
x64dbg – x64dbg.com/#start
WinDBG – https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools
Ghidra CheatSheet – https://ghidra-sre.org/CheatSheet.html
Windows Internals – scorpiosoftware.net/category/windows-internals/
Root Cause Analysis – https://hardik05.wordpress.com/2021...th-time-travel-debugging-with-windbg-preview/
Corelan – https://www.corelan.be