Skip to content

Fuzzing golang image (Go) project with sydr fuzz (go fuzz backend) (rus)

Andrey Fedotov edited this page Mar 6, 2023 · 11 revisions

Введение

В этой статье мы попробуем применить гибридный подход к фаззингу с помощью инструмента 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. Я обнаружил интересный способ подходяжий мне и подходу гибридного фаззинга, который я использую. Давайте поговорим об этом чуть позже в секции Покрытие. Пред тем, как мы начнём фаззинг, давайте собирём докер контейнер.

Фаззинг

Clone this wiki locally