
A linux system call fuzzer using TriforceAFL
Новое: Для тех, кто хочет поиграть с TriforceAFL и TLSF, Ричард Джонсон создал Dockerfile, который устанавливает оба (и даже собирает для вас ядро Linux). Он доступен здесь https://hub.docker.com/r/moflow/afl-triforce/tags/.
Это набор файлов, используемых для фаззинга системных вызовов ядер Linux x86_64 с помощью AFL и QEMU. Для использования вам понадобятся TriforceAFL с https://github.com/nccgroup/TriforceAFL и образ ядра для фаззинга. Скрипты предполагают, что TriforceAFL находится в $TAFL или ../TriforceAFL/ (прим.: для сборки testAfl требуется, чтобы существовал ../TriforceAFL/config.h).
Для сборки:
make
Чтобы запустить, сначала установите ядро в ./kern/bzImage и извлеките /proc/kallsyms в ./kern/kallsyms. Установите переменную окружения K=kern, указывающую на ваше ядро. Затем выполните:
make inputs
./runFuzz -M M0
Обратите внимание, что скрипт runFuzz ожидает имя master или slave, поскольку он всегда работает в режиме master/slave. Дополнительную информацию об использовании см. в скрипте runFuzz.
Также обратите внимание, что это создаёт лишь небольшой набор примеров входных данных. Чтобы протестировать большое количество важных системных вызовов, вам, вероятно, захочется сгенерировать по одному примеру для каждого системного вызова или хотя бы по одному примеру для каждой «формы» системного вызова. Их следует поместить в inputs/. Пример см. в gen2.py.
Чтобы воспроизвести тестовые случаи (например, сбои), выполните:
./runTest inputs/ex1
./runTest outputs/crashes/id*
Вы также можете запустить драйвер вне эмулируемой среды с опцией -t, с подробным журналированием с помощью -vv и без фактического выполнения системных вызовов с помощью -x:
./driver -tvvx < inputs/ex1
strace ./driver -t < inputs/ex1
Иногда бывает полезно загрузить ядро и интерактивно запускать тесты. Для этого отредактируйте файлы rootTemplate по своему усмотрению (например, чтобы добавить дополнительные тестовые инструменты в корневую файловую систему), а затем выполните:
./runCmd
Другие команды, помимо командной оболочки, можно вызвать, указав их в качестве аргументов командной строки для runCmd.
Примечание: когда закончите работу с оболочкой, используйте ^A-c, чтобы получить приглашение QEMU, и введите quit.
Отладка проще всего с ядром, собранным с включёнными символами отладки.
Используйте runTest для запуска ядра и выполнения теста через драйвер, либо используйте runCmd для ручного запуска тестового случая из оболочки.
Отредактируйте свой скрипт запуска, чтобы включить опцию -s при запуске afl-qemu-system-trace.
Это включит поддержку gdb на TCP-порту 1234. Используйте getvmlinux для извлечения образа ядра vmlinux из вашего ядра bzImage и запустите gdb после загрузки системы:
cp kern/bzImage .
./getvmlinux
gdb ./vmlinux
target remote :1234
break somefunction
continue
Вы можете подключить отладчик после того, как runTest вызвал сбой, или до того, как вы вручную спровоцируете ошибку в runCmd.
Обратите внимание, что исходники Linux по умолчанию компилируются с включённой оптимизацией. Это может сделать отладку запутанной и сложной. Вы можете отключить оптимизацию для отдельных файлов, отредактировав make-файл Linux для подкаталога, в котором находится файл, и добавив CFLAGS_name.o = -O0 в Makefile. Например, редактирование kernel/Makefile и добавление CFLAGS_sys_ni.o = -O0 отключит оптимизацию при сборке kernel/sys_ni.o.
Скрипт оболочки getSyms использует runCmd для выполнения cat /proc/kallsyms и извлекает его в локальный файл с именем kallsyms. Обычно это используется для подготовки ядра к фаззингу:
K=yourKernDir ./getSyms, чтобы получить kallsymsmv kallsyms yourKernDir, чтобы установить егоПримечание: При фаззинге ядра Linux 2.* вам необходимо включить таймер процессора. Когда таймер не включён, обнаружение паник и журналирование, похоже, работают неправильно, и паники приводят к зависаниям. Чтобы включить таймер, вызывайте startForkserver(1) в driver.c вместо startForkserver(0). Похоже, эта проблема не возникает в ядрах Linux 3.* и Linux 4.*.