Обычно нет проблем, если вы хотите фаззить безголовое приложение. Безголовое приложение может быть запущено только в терминале и не имеет графического интерфейса. Вы можете выбрать свой любимый фаззер и передать фаззинговые данные в приложение. Обычно безголовое приложение просто обрабатывает данные, а затем сразу же завершает работу или аварийно завершает работу. Но может быть по-другому, если вы пытаетесь фаззить приложение с графическим интерфейсом. Давайте попробуем фаззить текстовый редактор с открытым исходным кодом AbiWord.
Может возникнуть несколько проблем с фаззингом приложения с графическим интерфейсом. Приложение с графическим интерфейсом может продолжать работать после того, как вы введете в него неверные данные. Обычно это нормальное поведение для приложения с графическим интерфейсом. Например, вы можете попробовать открыть неверный документ в AbiWord. Затем он просто сообщает, что не может открыть документ, и продолжает работать (главное окно AbiWord все еще существует). Это ожидаемое и правильное поведение.
Но во время фаззинга мы хотим передать приложению много неверных данных. Если нам приходится закрывать приложение вручную каждый раз, когда оно открывает недопустимый документ, возможно, это не то, что нам нужно.
Другая проблема заключается в том, что во время фаззинга появляется много новых окон. Если у вас есть специальная машина для фаззинга, то это не должно быть проблемой. Но если вы используете один и тот же компьютер для фаззинга и других дел, то множество открывающихся окон точно вас будут раздражать.
Некоторые приложения имеют пакетный режим. По сути, это приложение можно использовать как инструмент командной строки. Например, AbiWord поддерживает преобразование документа в другой формат:
$ abiword --help Usage: abiword [OPTION...] [FILE...] - commandline options Help Options: -h, --help Show help options --help-all Show all help options --help-gtk Show GTK+ Options Application Options: -g, --geometry=GEOMETRY Set initial frame geometry -t, --to=FORMAT Target format of the file (abw, zabw, rtf, txt, utf8, html, ...), depends on available filter plugins
Вы можете попросить его преобразовать нечеткий документ в другой формат. Он будет анализировать документ, чтобы иметь возможность его преобразовать, но не будет отображать его. Это может быть хорошо, но такой подход не позволяет вам покрыть код, отвечающий за рендеринг, так что вы можете пропустить некоторые ошибки. Это не хорошо.
Есть несколько решений вышеперечисленных проблем.
Используйте виртуальный дисплей
Во-первых, давайте избавимся от надоедливых окон. В *nix есть команда Xvfb, которая создает виртуальный дисплей. Он точно доступен в Ubuntu. Вот пример того, как вы можете создать виртуальный дисплей «:1»
Xvfb :1 -screen 0 1024x768x16
Затем вы можете заставить свое приложение использовать этот дисплей. Некоторые приложения могут иметь параметр командной строки, который указывает используемый дисплей. Или вы можете попробовать поставить «: 1» в переменную DISPLAY.
Чтобы убедиться, что ваше приложение действительно использует виртуальный дисплей и отображает на нем свои данные, вы можете сделать снимок экрана с помощью таких команд:
import -display :1 -window root image.png display image.png
Первая команда делает снимок экрана и сохраняет результат в файл «image.png». Вторая команда просто отображает «image.png».
Использовать команду тайм-аута
Теперь мы не увидим надоедливых окон, создаваемых нашим приложением во время фаззинга. Но он все равно будет ждать, пока мы его закроем. Наверное, самое простое решение — убить приложение по таймауту. Это можно легко сделать с помощью команды «тайм-аут» в * nix. Вот пример скрипта для фаззинга AbiWord с помощью zzuf fuzzer, который использует команду «timeout» для уничтожения AbiWord по тайм-ауту:
#!/bin/bash seed=${1} while [ ${seed} != 10000000 ]; do echo "seed: ${seed}" ${ZZUF_HOME}/bin/zzuf -c -s${seed} -r0.001:0.003 < test.doc > corrupted.doc ASAN_OPTIONS="detect_leaks=0" timeout 2s ${ABIWORD} \ --display=:1 corrupted.doc > log 2>&1 if grep AddressSanitizer log > /dev/null 2>&1 ; then # ASan found something echo "Game over" break fi seed=`expr ${seed} + 1` done
Кратко опишу, что здесь происходит:
- Сценарий запускает цикл while, в котором увеличивается начальное значение для zzuf fuzzer. Он печатает начальное значение для каждой итерации, чтобы затем можно было легко воспроизвести все сбои (просто запустите zzuf с этим начальным числом).
- «test.doc» является действительным документом.
- Чтобы сгенерировать нечеткий документ, мы передаем действительный «test.doc» в zzuf и сохраняем нечеткий документ в «corrupted.doc».
- Параметр «-r» сообщает zzuf, какую часть исходного документа следует изменить.
- Затем мы передаем нечеткий файл «corrupted.doc» в AbiWord. Мы запускаем AbiWord с командой «timeout». Он убьет процесс AbiWord через две секунды, если он не завершится сам по себе.
- Мы используем опцию «-display», чтобы попросить AbiWord использовать виртуальный дисплей. Мне также нравится использовать AddressSanitizer всякий раз, когда это возможно, потому что этот отличный инструмент помогает обнаруживать больше повреждений памяти. Меня обычно не интересуют утечки памяти, поэтому я передаю «detect_leaks=0» в ASAN_OPTIONS, чтобы отключить проверку утечек памяти. Позже я покажу, как можно построить AbiWord с помощью ASan.
- Наконец, мы перенаправляем весь вывод AbiWord в файл журнала и ищем там строку «AddressSanitizer». Если мы нашли строку, мы сразу прекращаем фаззинг, потому что кажется, что мы нашли потенциальную проблему.
Когда скрипт останавливается с сообщением «Игра окончена», нам просто нужно заглянуть в журналы, чтобы выяснить, в чем проблема. Это самая веселая часть. Когда скрипт останавливается, «corrupted.doc» содержит нечеткие данные, вызвавшие сбой, поэтому мы можем легко воспроизвести его. Скрипт может быть обновлен, чтобы не прекращать фаззинг в случае обнаружения сбоя. В этом случае нам нужно сохранить поврежденный файл под другим именем, чтобы затем использовать его для воспроизведения сбоя.
Используйте сторожевой скрипт
zzuf — очень простой фаззер. Он просто случайным образом изменяет входные данные. Есть более умный фаззер, о котором, наверное, все знают. Это американский нечеткий лоп (AFL). Этот фаззер намного умнее. Он инструментирует приложение перед фаззингом, чтобы иметь возможность получить информацию о пройденных путях в нем. Затем он использует эту информацию для создания новых нечетких данных.
Итак, сначала вам нужно создать приложение с графическим интерфейсом с помощью AFL. В случае AbiWord это можно сделать с помощью следующих команд:
./autogen.sh CC="/home/artem/tools/afl/afl-gcc" \ CXX="/home/artem/tools/afl/afl-g++" \ AFL_USE_ASAN=1 \ ./configure --prefix=/path/to/abiword/directory \ make make install
Обычно вы можете запустить фаззинг с помощью AFL с помощью следующей команды:
afl-fuzz -m 1000 -t 10000+ -i in -o out \ /path/to/abiword/directory/bin/abiword --display=:1 @@
Но теперь вы не можете использовать команду «тайм-аут». Если вы попробуете следующую команду, то AFL будет жаловаться, что вы используете неинструментированные двоичные файлы (и это действительно так, потому что мы инструментировали AbiWord, а не «тайм-аут»):
afl-fuzz -m 1000 -t 10000+ -i in -o out \ timeout 2s /path/to/abiword/directory/bin/abiword --display=:1 @@
Таким образом, мы больше не можем использовать команду «тайм-аут», потому что нам нужно использовать инструментированные двоичные файлы с AFL. Есть еще одно решение, которое может помочь здесь. Мы можем запустить сторожевой скрипт, который каждую секунду будет пытаться закрыть окно AbiWord. Это может быть что-то простое, например:
#!/bin/bash while true ; do sleep 1 DISPLAY=:1 xdotool key alt+F4 done
Здесь мы используем команду «xdotool», чтобы отправить ALT+F4 в окно AbiWord на виртуальном дисплее.
Теперь мы можем запустить этот скрипт как отдельный процесс, а затем начать фаззинг с помощью ALF.
Как собрать AbiWord с AddressSanitizer
Я использовал следующие команды для сборки AbiWord с ASan:
CFLAGS="-g -fsanitize=address -fno-omit-frame-pointer -O0" \ CPPFLAGS="-g -fsanitize=address -fno-omit-frame-pointer -O0" \ LDFLAGS="-fsanitize=address" \ ./configure \ --prefix=/path/to/abiword/directory \ --disable-shared make make install
Я слышал, что теперь вы можете включить ASan в AbiWord, настроив его с опцией «-with-sanitizer=address», но я не пробовал.
Обнаруженные ошибки в AbiWord
Во время фаззинга с AFL я обнаружил пару ошибок в AbiWord. Они не похожи на проблемы с безопасностью, но все же лучше их исправить (на момент написания этого поста большинство из них уже исправлены). Вот список:
http://bugzilla.abisource.com/show_bug.cgi?id=13807: AbiWord аварийно завершает работу, если указана опция -display
http://bugzilla.abisource.com/show_bug.cgi?id=13826: Неверный доступ к памяти в IE_Imp_RTF::HandleStyleDefinition
http://bugzilla.abisource.com/show_bug.cgi?id=13827: функция _png_read() может получить доступ к массиву pBytes за его пределами
http://bugzilla.abisource.com/show_bug.cgi?id=13828: Ошибка сегментации в fp_ContainerObject::getDocSectionLayout()
Вывод
Есть еще одна проблема с этими двумя решениями выше. Проблема в том, что фаззинг будет очень медленным. По сути, одна итерация фаззинга занимает 1–2 секунды, что не очень быстро. С другой стороны, приложениям с графическим интерфейсом может потребоваться некоторое время для запуска и отображения содержимого документа. Я не измерял, сколько времени требуется AbiWord для точного запуска, но мне кажется, что на моем старом ноутбуке это около 1 секунды. Итак, похоже, что на моем оборудовании невозможно сделать лучше, чем один запуск в секунду.
Любые другие идеи или комментарии очень приветствуются!
Первоначально опубликовано на https://blog.gypsyengineer.com 26 ноября 2016 г.