Ремонт, сервис, услуги » Информация » Фаззинг Linux через WTF




Фаззинг Linux через WTF

Автор: addministr от 11-05-2022, 11:30

Категория: Информация





Недавно появился фаззер 1. Чем WTF лучше AFL

2. Как работает WTF с таргетом для Windows

3. Как сделать снимок через gdb

3.1. Виртуальная память

3.2. Физическая память

3.3 Процессор

4. Пример

4.1. Границы фаззинга symbol-store.json

4.2. Дамп памяти mem.dmp

4.3. Дамп процессора regs.json

4.4. Покрытие stackoverflow.cov

4.5. Фаззер

5. Вывод

Прежде всего вспомним, что в blackbox умеет и AFL. Тогда зачем мучиться, когда есть готовое решение?



структуру task_struct, которая описывает процесс.

Эта структура поможет решить проблему с виртуальной памятью.



стуктуру CPUState*(поле env_ptr), и переменную такого типа можно найти в функции cpu_exec.

Если сделать одноразовый кастомный брейк на этой функции и запомнить CPUState* cpu, то по этому адресу можно будет найти любые регистры в любой момент времени.



Неожиданно получилось так, что WTF считает невалидными значения некоторых регистров, а некоторые вообще не видит. Поэтому пришлось пропатчить WTF (коммит e278c942848f2e211904320ff804df4ccb6fd7f8).



Функция bool SanitizeCpuState(CpuState_t &CpuState), удалить проверку:



for (Seg_t *Seg : Segments) {
if (Seg->Reserved != ((Seg->Limit > 16) & 0xF)) {
fmt::print("Segment with selector {:x} has invalid attributes.n",
Seg->Selector);
return false;
}
}

Метод bool KvmBackend_t::LoadSregs(const CpuState_t &CpuState), проблема с cs, заменить SEG(cs, Cs) на:



Run_->s.regs.sregs.cs = {
.base = 0,
.limit = 0xffffffff,
.selector = CpuState.Cs.Selector,
.type = uint8_t(CpuState.Cs.SegmentType),
.present = uint8_t(CpuState.Cs.Present),
.dpl = uint8_t(CpuState.Cs.DescriptorPrivilegeLevel),
.db = 0,
.s = uint8_t(CpuState.Cs.NonSystemSegment),
.l = 1,
.g = 1,
.avl = 0,
};


В чем именно проблема — неизвестно, но в qemu точно правильные значения, поэтому все под нож.



Правки можно не вносить самостоятельно, все есть в этом форке, там же и пример фаззинга ELF.



example/stackoverflow. Все необходимые скрипты, бинари, образы — в том же репозитории.

Тяжелые файлы лежат отдельно тут:




archlinux-root-123.tar.xz — образ диска для qemu, должен лежать в example/archlinux-root-123.qcow2;
vmlinux-5.17.4-arch1.tar.xz — ядро, example/vmlinux-5.17.4-arch1;
mem.dmp.tar.xz — дамп, example/stackoverflow/fuzzer/state/mem.dmp.


Чего в репозитории нет, так это собранного с отладкой qemu, оно несложно собирается.



git clone https://github.com/qemu/qemu && 
cd qemu &&
mkdir build &&
cd build &&
CXXFLAGS="-g"
CFLAGS="-g"
../configure
--cpu=x86_64
--target-list="x86_64-softmmu x86_64-linux-user" &&
make

state/symbol-store.json.

Выглядит так: "stop":"0x555555555272", "stack_chk_failed":"0x5555555551ca"



Границы очень важны, нужно учесть все варианты. Если этого не сделать, поток убежит, фаззер не остановится до таймаута.



Это простой пример, для чего-то большего и границ будут больше:




обработчик деления на ноль asm_exc_divide_error;
обработчики asm_exc_page_fault, page_fault_oops;
force_sigsegv
...


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



cpu. Эта команда найдет CPUState* структуру в памяти qemu и вытащит все регистры в regs.json.



Теперь есть все файлы, которые описывают состояние ОС — regs.json, symbol-store.json, mem.dmp. Можно останавливать qemu, gdb.



gen_cov.py:


загрузить бинарь;
File -> Script file


Появится файл stackoverflow.cov, его место — в example/stackoverflow/fuzzer/state.



здесь. Ничего сложного.

Здесь границы:



bool Init(const Options_t &Opts, const CpuState_t &) {

if (!g_Backend->SetBreakpoint("stop", [](Backend_t *Backend) {
Backend->Stop(Ok_t());
})) {
DebugPrint("Failed to SetBreakpoint stopn");
return false;
}

if (!g_Backend->SetBreakpoint("stack_chk_failed", [](Backend_t *Backend) {
Backend->Stop(Crash_t("crash"));

})) {
DebugPrint("Failed to SetBreakpoint stack_chk_failedn");
return false;
}
return true;
}


Тут вставляется новая порция мусора:



bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) {

const Gva_t Rdi = Gva_t(g_Backend->GetReg(Registers_t::Rdi));

if (!g_Backend->VirtWrite(Rdi, Buffer, BufferSize, true)) {
DebugPrint("Failed to write next testcase!");
return false;
}

g_Backend->SetReg(Registers_t::Rsi, BufferSize);

return true;
}

Неприятная особенность WTF: модуль с фаззером становится частью инструмента. WTF нужно пересобирать каждый раз, когда появляются правки.



Чтобы запустить фаззер:



tmux-pane1:
./run.master

tmux-pane2:
sudo ./run.worker

Мастер запускает сокет-сервер на выбранном порту, воркеры подлкючаются к нему за новыми тест-кейсами и отправляют статистику по ним обратно мастеру.



It works!



Вывод



What The Fuzz — мощный инструмент, и требует от юзера значительных усилий по настройке. Еще больше нужно для настройки фаззинга Linux, потому что WTF по дефолту его не поддерживает. Все равно WTF стоит попробовать в случае изучения тяжелых приложений, ядра или драйверов. Эта статья была о том, как преодолеть ограничение по Linux и что такое snapshot-based подход в фаззинге.

 

Источник: https://habr.com/ru/company/dsec/blog/664230/





Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Архив | Связь с админом | Конфиденциальность

RSS канал новостей     Яндекс.Метрика