-
Notifications
You must be signed in to change notification settings - Fork 37
Fuzzing golang image (Go) project with sydr fuzz (go fuzz backend) (rus)
В этой статье мы попробуем применить гибридный подход к фаззингу с помощью инструмента sydr-fuzz для проектов на языке Go. Sydr-fuzz сочитает в себе преимущества инструмента динамического символьного выполнения Sydr и фаззера AFLplusplus. Он также поддерживает другой движок фаззинга libFuzzer, который мы будем использовать. Нам повезло, go-fuzz имеет поддержку libFuzzer'а, поэтому мы можем попробовать его использовать как движок libFuzzer'а в sydr-fuzz. Мы сосредоточимся в этом гайде на создании фаззинг-целей для go-fuzz(libFuzzer), Sydr и сбора покрытия. Мы проведём гибридный фаззинг с использованием Sydr и go-fuzz, а потом собирём покрытие по исходному коду. Также мы будем использовать инструмент для сортировки аварийных завершений casr, и проверим предикаты безопасности с помощью Sydr. И конечно, я расскажу об интересных случиях, которые мне встретились во время моего знакомства с фаззингом Go проектов.
Парсеры изображений, такие как golang/image являются хорошими примерами для фаззинга. Перед тем как я начну, есть уже готовый для сборки докер [контейнер] (https://github.com/ispras/oss-sydr-fuzz/tree/master/projects/image-go) со всем необходимым окружением для фаззинга: фазиннг-цели для go-fuzz, Sydr и покрытия по коду.
В репозитории go-fuzz есть подробная инструкция о том, как подготовить фаззинг-цели и начать фаззинг. Также, есть отличный репозиторий go-fuzz-corpus где вы можете найти фаззинг-цели и начальный корпус данных. В этом репозитории есть и фаззинг-цели для проекта golang/image (png, webp, tiff, jpeg, и т.д.). Исходя из гайда по проекту go-fuzz, мы можем собрать фаззинг-цели прям в репозитории go-fuzz-corpus, но давайте возмём их, представим, что разрабатываем их с нуля сами, может что-то поменяем.
Хорошо, давайте напишем фаззинг-цель для декодера webp. Сперва, я клонирую репозиторий golang/image и создаю модуль fuzz.go, со следующим содержимым:
package image
import (
"bytes"
"golang.org/x/image/webp"
)
func FuzzWebp(data []byte) int {
cfg, err := webp.DecodeConfig(bytes.NewReader(data))
if err != nil {
return 0
}
if cfg.Width*cfg.Height > 4000000 {
return 0
}
if _, err := webp.Decode(bytes.NewReader(data)); err != nil {
return 0
}
return 1
}Я немного изменил оригинальную фаззинг-цель используя фаззинг-цель из image-rs webp target:
if cfg.Width*cfg.Height > 4000000 { // originally 1e6Для сборки фаззинг-цели нам необходимо просто выполнить команду go-fuzz-build с опцией -libfuzzer:
go-fuzz-build -libfuzzer -func=FuzzWebp -o webp.a
clang -fsanitize=fuzzer webp.a -o fuzz_webpОтлично, сейчас мы имеем фаззинг-цель для Go проекта которая выглядит и работает как фаззинг-цель для libFuzzer. Давайте собирём цель для DSE инструмента (Sydr). Для этого мы создадим исполняемый файл, который получает входные данные из файла и вызывает функцию FuzzWebp. Создадим файл cmd/sydr_webp/main.go со следующим содержимым:
package main
import (
"os"
"golang.org/x/image"
)
func main() {
data, _ := os.ReadFile(os.Args[1])
image.FuzzWebp(data)
}Для сборки DSE-цели (Sydr) нам нужно выполнить следующую команду:
cd cmd/sydr_webp && go buildПрекрасно, у нас есть sydr_webp бинарник для Sydr. Нам остаётся только собрать бинарник для сбора покрытия по исходному коду. Мне протребовалось немало времи, чтобы разобраться с этим. У нас нет опций компилятора, таких как "-C instrument-coverage" для компилятора Rust, или "-fprofile-instr-generate -fcoverage-mapping" для компилятора clang/clang++. В OSS-Fuzz сделано немало работы, но это всё затруднительно использовать не в инфраструктуре OSS-Fuzz. Я обнаружил интересный способ подходяжий мне и подходу гибридного фаззинга, который я использую. Давайте поговорим об этом чуть позже в секции Покрытие. Пред тем, как мы начнём фаззинг, давайте собирём докер контейнер.