diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff16c5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +build/ +data/ + +AFLplusplus/ +aflfuzz/configs/ +aflfuzz/fuzz_out/ + +test_data.db diff --git a/README.md b/README.md index adb4563..0957a9a 100644 --- a/README.md +++ b/README.md @@ -143,4 +143,65 @@ bash scripts/run.sh Добавить нового пользователя: ``` ./build/release/add_user -username test -password 1234 -``` \ No newline at end of file +``` + +## Запуск фаззинга + +Сначала соберите приложение и инициализируйте тестовую базу (init_users). + +Далее (для учебных целей фаззинг ограничен по времени - 5 минут): + +1. Запуск нативного фаззера (проверка функции аутентификации, ограничение в 1 поток для нормальной работы БД SQLite) + +1.1. cd myfuzz + +1.2. ./run_fuzz.sh + +1.3. ./view_fuzz_coverage.sh + +2. Запуск AFL++ в режиме blackbox (фаззинг готового бинарника) + +2.1. cd aflfuzz + +2.2. ./install.sh + +2.3. ./run_fuzz.sh + +### Попытка скрестить gccgo и afl провалилась, оставлю для истории + +3. Запуск AFL++ при компиляции через gccgo (установите AFL++ как в пункте 2.2. если ещё не сделали этого) + +3.1. cd aflfuzz + +3.2. ./install_gccgo_for_afl.sh + +3.3. ./build_for_afl.sh + +3.4. ./run_afl.sh + +### Черновики для go-fuzz + +На данный момент установка по официальной инструкции https://github.com/dvyukov/go-fuzz/blob/master/README.md не работает по причине golang.org/x/tools@v0.44.0 requires go >= 1.25.0 (running go 1.24.4) + +4. Запуск go-fuzz + +4.1. cd myfuzz + +4.2. ./install_go-fuzz.sh (тут возникают проблемы, поэтому следующие шаги проверить не удалось + оказалось, что первая версия конфликтует с нативным фаззингом из пункта 1) + +4.3. ./build_for_go-fuzz.sh + +4.4. ./run_go-fuzz.sh + +### Последняя попытка (тоже неудачная) + +Утилита обещает поддержку 4 фаззеров, оказалось это просто обёртка для генерации тестов в 4 форматах и запуска фаззеров. +Из заявленных фаззеров удалось запустить только нативный (но я это и так сделал в пункте 1), а также сгенерировать тест для go-fuzz (но запустить не удалось, см. пункт 4 выше), для libfuzzer и afl даже тесты не сгенерировались (как я понял, тоже надо самому установить все нужные бинарники). + +5. Запуск golang-fuzz + +5.1. cd golang-fuzz + +5.2. ./install_golang-fuzz.sh + +5.3. ./run_golang-fuzz.sh diff --git a/aflfuzz/build_for_afl.sh b/aflfuzz/build_for_afl.sh new file mode 100755 index 0000000..e4b3af1 --- /dev/null +++ b/aflfuzz/build_for_afl.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +export AFL_CC=gccgo +export GCCGO=afl-gcc-fast + +#unset GOROOT +go build -v -x -compiler=gccgo -o authapp_for_afl ../cmd/authapp/main.go + +exit 0 + +go build -a -toolexec="afl-gcc-fast" -o authapp_for_afl ../cmd/authapp/main.go + +exit 0 + +export AFL_CC=gccgo + +afl-gcc-fast -o authapp_for_afl ../cmd/authapp/main.go + +exit 0 diff --git a/aflfuzz/fuzz_in/blank_test.txt b/aflfuzz/fuzz_in/blank_test.txt new file mode 100644 index 0000000..139597f --- /dev/null +++ b/aflfuzz/fuzz_in/blank_test.txt @@ -0,0 +1,2 @@ + + diff --git a/aflfuzz/fuzz_in/right_admin.txt b/aflfuzz/fuzz_in/right_admin.txt new file mode 100644 index 0000000..baed26a --- /dev/null +++ b/aflfuzz/fuzz_in/right_admin.txt @@ -0,0 +1,2 @@ +admin +admin123 diff --git a/aflfuzz/fuzz_in/right_user.txt b/aflfuzz/fuzz_in/right_user.txt new file mode 100644 index 0000000..ceba222 --- /dev/null +++ b/aflfuzz/fuzz_in/right_user.txt @@ -0,0 +1,2 @@ +user1 +password1 diff --git a/aflfuzz/fuzz_in/wrong_admin.txt b/aflfuzz/fuzz_in/wrong_admin.txt new file mode 100644 index 0000000..353f219 --- /dev/null +++ b/aflfuzz/fuzz_in/wrong_admin.txt @@ -0,0 +1,2 @@ +admin +admin diff --git a/aflfuzz/fuzz_in/wrong_user.txt b/aflfuzz/fuzz_in/wrong_user.txt new file mode 100644 index 0000000..df042e5 --- /dev/null +++ b/aflfuzz/fuzz_in/wrong_user.txt @@ -0,0 +1,2 @@ +user +password diff --git a/aflfuzz/install.sh b/aflfuzz/install.sh new file mode 100755 index 0000000..036b720 --- /dev/null +++ b/aflfuzz/install.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# apt install afl++ не поддерживает qemu режим, соберём из исходников +# https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/INSTALL.md + +sudo apt-get update +sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev +# try to install llvm-18 and install the distro default if that fails +sudo apt-get install -y lld-18 llvm-18 llvm-18-dev clang-18 || sudo apt-get install -y lld llvm llvm-dev clang +sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev +sudo apt-get install -y meson ninja-build # for QEMU mode +sudo apt-get install -y cpio libcapstone-dev # for Nyx mode +sudo apt-get install -y wget curl # for Frida mode +sudo apt-get install -y python3-pip # for Unicorn mode +git clone https://github.com/AFLplusplus/AFLplusplus +cd AFLplusplus +git submodule update --init +make distrib +sudo make install diff --git a/aflfuzz/install_gccgo_for_afl.sh b/aflfuzz/install_gccgo_for_afl.sh new file mode 100755 index 0000000..e1cc32b --- /dev/null +++ b/aflfuzz/install_gccgo_for_afl.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +sudo apt-get update +sudo apt-get install -y gccgo diff --git a/aflfuzz/run_afl.sh b/aflfuzz/run_afl.sh new file mode 100755 index 0000000..2317ac0 --- /dev/null +++ b/aflfuzz/run_afl.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +mkdir -p data +cp ../data/users.db ./data/users.db +mkdir -p configs +cp ../configs/config.toml ./configs/config.toml + +mkdir -p fuzz_out + +afl-fuzz -V $(( 60 * 5 )) -i fuzz_in -o fuzz_out -- ./authapp_for_afl diff --git a/aflfuzz/run_fuzz.sh b/aflfuzz/run_fuzz.sh new file mode 100755 index 0000000..c6bda54 --- /dev/null +++ b/aflfuzz/run_fuzz.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +mkdir -p data +cp ../data/users.db ./data/users.db +mkdir -p configs +cp ../configs/config.toml ./configs/config.toml + +mkdir -p fuzz_out + +afl-fuzz -V $(( 60 * 5 )) -Q -i fuzz_in -o fuzz_out -- ../build/release/authapp diff --git a/docs/gofuzz.odp b/docs/gofuzz.odp new file mode 100644 index 0000000..df47bc3 Binary files /dev/null and b/docs/gofuzz.odp differ diff --git a/docs/gofuzz.pdf b/docs/gofuzz.pdf new file mode 100644 index 0000000..0b89bb9 Binary files /dev/null and b/docs/gofuzz.pdf differ diff --git a/go.mod b/go.mod index 8dbf61f..11359eb 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,16 @@ module linux-auth -go 1.23.0 +go 1.24.0 + +toolchain go1.24.4 require ( github.com/mattn/go-sqlite3 v1.14.33 github.com/pelletier/go-toml/v2 v2.2.4 golang.org/x/term v0.38.0 ) + +require ( + github.com/dvyukov/go-fuzz v0.0.0-20240924070022-e577bee5275c // indirect + golang.org/x/sys v0.39.0 // indirect +) diff --git a/go.sum b/go.sum index b7bc065..7fc4ea4 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,10 @@ +github.com/dvyukov/go-fuzz v0.0.0-20240924070022-e577bee5275c h1:oLpHpHwNuAPvw3bBviEZNrJbigNNi5dRadfZnagGgZI= +github.com/dvyukov/go-fuzz v0.0.0-20240924070022-e577bee5275c/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= diff --git a/golang-fuzz/auth_fuzz.go b/golang-fuzz/auth_fuzz.go new file mode 100644 index 0000000..f284750 --- /dev/null +++ b/golang-fuzz/auth_fuzz.go @@ -0,0 +1,39 @@ +package my_golang_fuzz + +import ( + "bytes" + + "linux-auth/internal/auth" + "linux-auth/internal/db" +) + +func Fuzz(data []byte) int { + if err := db.Init("test_data.db"); err != nil { + return -1 + } + defer db.Close() + + parts := bytes.SplitN(data, []byte{0}, 2) + if len(parts) != 2 { + return 0 + } + + username := string(parts[0]) + password := string(parts[1]) + + ok, err := auth.Authenticate(username, password) + + if err != nil { + return -1 + } + + if ok { + if (username == "admin" && password == "admin123") || + (username == "user1" && password == "password1") { + return 1 + } + return -1 + } + + return 1 +} diff --git a/golang-fuzz/auth_fuzz_golangfuzz_test.go b/golang-fuzz/auth_fuzz_golangfuzz_test.go new file mode 100644 index 0000000..c91429a --- /dev/null +++ b/golang-fuzz/auth_fuzz_golangfuzz_test.go @@ -0,0 +1,38 @@ +// Code generated by golang-fuzz; DO NOT EDIT. + +package my_golang_fuzz + +import ( + "os" + "path/filepath" + "testing" +) + +func Fuzz_(f *testing.F) { + loadTestCases(f) + f.Fuzz(func(_ *testing.T, input []byte) { Fuzz(input) }) +} + +func loadTestCases(f *testing.F) { + dir, err := os.ReadDir(`corpus`) + if err != nil { + f.Logf(`Running without adding corpus: %s`, err) + return + } + + if len(dir) == 0 { + f.Log(`Running without adding corpus: directory is empty`) + } + + for _, corpus := range dir { + if corpus.IsDir() { + continue + } + + input, err := os.ReadFile(filepath.Join(`corpus`, corpus.Name())) + if err != nil { + f.Logf(`Could not read corpus input %s: %s`, corpus.Name(), err) + } + f.Add(input) + } +} diff --git a/golang-fuzz/install_golang-fuzz.sh b/golang-fuzz/install_golang-fuzz.sh new file mode 100755 index 0000000..609c4c5 --- /dev/null +++ b/golang-fuzz/install_golang-fuzz.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +go install github.com/ultraware/golang-fuzz@latest diff --git a/golang-fuzz/run_golang-fuzz.sh b/golang-fuzz/run_golang-fuzz.sh new file mode 100755 index 0000000..3f87a05 --- /dev/null +++ b/golang-fuzz/run_golang-fuzz.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cp ../data/users.db test_data.db + +export PATH=$PATH:$(go env GOPATH)/bin + +golang-fuzz -gofuzz -run diff --git a/myfuzz/auth_fuzz.go.backup b/myfuzz/auth_fuzz.go.backup new file mode 100644 index 0000000..d1b5aef --- /dev/null +++ b/myfuzz/auth_fuzz.go.backup @@ -0,0 +1,48 @@ +package myfuzz + +import ( + "bytes" + + "linux-auth/internal/auth" + "linux-auth/internal/db" +) + +// Fuzz — это функция, которую будет вызывать go-fuzz. +// Принимает только []byte — все входные данные в одном байтовом потоке. +func Fuzz(data []byte) int { + // Инициализируем БД на каждом вызове — go-fuzz не имеет f.Cleanup() + if err := db.Init("test_data.db"); err != nil { + return -1 // ошибка инициализации — краш + } + defer db.Close() // закрываем после каждого вызова + + // Разделяем байты по нулевому байту (\x00) — это разделитель между username и password + parts := bytes.SplitN(data, []byte{0}, 2) + if len(parts) != 2 { + return 0 // недостаточно данных — пропускаем + } + + username := string(parts[0]) + password := string(parts[1]) + + ok, err := auth.Authenticate(username, password) + + // Если произошла ошибка — это баг + if err != nil { + return -1 // краш + } + + // Если аутентификация прошла успешно — проверяем, разрешён ли такой вход + if ok { + // Разрешённые пары (аналогично f.Add) + if (username == "admin" && password == "admin123") || + (username == "user1" && password == "password1") { + return 1 // норма — это ожидаемый результат + } + // Неожиданный успешный вход — это баг! + return -1 // краш + } + + // Неудачная аутентификация — это нормально + return 1 // норма +} diff --git a/myfuzz/build_for_go-fuzz.sh b/myfuzz/build_for_go-fuzz.sh new file mode 100755 index 0000000..405f5a5 --- /dev/null +++ b/myfuzz/build_for_go-fuzz.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +go-fuzz-build -libfuzzer ./myfuzz diff --git a/myfuzz/cover.out b/myfuzz/cover.out new file mode 100644 index 0000000..68c914d --- /dev/null +++ b/myfuzz/cover.out @@ -0,0 +1,33 @@ +mode: set +linux-auth/internal/auth/auth.go:21.60,23.16 2 1 +linux-auth/internal/auth/auth.go:23.16,26.3 1 1 +linux-auth/internal/auth/auth.go:29.2,29.17 1 1 +linux-auth/internal/auth/auth.go:29.17,31.3 1 0 +linux-auth/internal/auth/auth.go:34.2,34.55 1 1 +linux-auth/internal/auth/auth.go:34.55,37.17 2 1 +linux-auth/internal/auth/auth.go:37.17,39.4 1 0 +linux-auth/internal/auth/auth.go:42.3,44.41 3 1 +linux-auth/internal/auth/auth.go:44.41,46.4 1 0 +linux-auth/internal/auth/auth.go:48.3,48.20 1 1 +linux-auth/internal/auth/auth.go:52.2,53.18 2 1 +linux-auth/internal/db/sqlite.go:29.30,33.16 3 1 +linux-auth/internal/db/sqlite.go:33.16,35.3 1 0 +linux-auth/internal/db/sqlite.go:38.2,38.39 1 1 +linux-auth/internal/db/sqlite.go:38.39,40.3 1 0 +linux-auth/internal/db/sqlite.go:42.2,42.23 1 1 +linux-auth/internal/db/sqlite.go:48.14,49.21 1 1 +linux-auth/internal/db/sqlite.go:49.21,51.3 1 1 +linux-auth/internal/db/sqlite.go:57.27,70.2 3 1 +linux-auth/internal/db/sqlite.go:77.46,97.26 6 1 +linux-auth/internal/db/sqlite.go:97.26,99.3 1 1 +linux-auth/internal/db/sqlite.go:100.2,100.16 1 1 +linux-auth/internal/db/sqlite.go:100.16,102.3 1 0 +linux-auth/internal/db/sqlite.go:104.2,105.19 2 1 +linux-auth/internal/db/sqlite.go:112.43,121.2 3 1 +linux-auth/internal/db/sqlite.go:127.40,136.2 3 1 +linux-auth/internal/db/sqlite.go:142.38,151.2 3 0 +linux-auth/internal/db/sqlite.go:158.54,165.16 3 0 +linux-auth/internal/db/sqlite.go:165.16,167.3 1 0 +linux-auth/internal/db/sqlite.go:169.2,169.12 1 0 +linux-auth/internal/utils/hash.go:13.43,16.2 2 1 +linux-auth/internal/utils/hash.go:24.48,26.2 1 1 diff --git a/myfuzz/cover.txt b/myfuzz/cover.txt new file mode 100644 index 0000000..7fbd7f9 --- /dev/null +++ b/myfuzz/cover.txt @@ -0,0 +1,3 @@ +PASS +coverage: 75.0% of statements in ../... +ok linux-auth/myfuzz 0.046s diff --git a/myfuzz/install_go-fuzz.sh b/myfuzz/install_go-fuzz.sh new file mode 100755 index 0000000..e9d0524 --- /dev/null +++ b/myfuzz/install_go-fuzz.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +go install github.com/dvyukov/go-fuzz/go-fuzz@latest github.com/dvyukov/go-fuzz/go-fuzz-build@latest diff --git a/myfuzz/myfuzz_test.go b/myfuzz/myfuzz_test.go new file mode 100644 index 0000000..6de50da --- /dev/null +++ b/myfuzz/myfuzz_test.go @@ -0,0 +1,37 @@ +package myfuzz + +import ( + "testing" + + "linux-auth/internal/auth" + "linux-auth/internal/db" +) + +func FuzzAuth(f *testing.F) { + err := db.Init("test_data.db") + if err != nil { + f.Fatalf("DB init error: %v", err) + } + f.Cleanup(func() { + db.Close() + }) + + f.Add("admin", "admin123") // right admin + f.Add("user1", "password1") // right user + f.Add("admin", "admin") // wrong admin + f.Add("user", "password") // wrong user + f.Add("", "") // blank test + + f.Fuzz(func(t *testing.T, username string, password string) { + ok, err := auth.Authenticate(username, password) + if err != nil { + t.Errorf("For username %q and password %q error: %v", username, password, err) + } + if ok { + if (username == "admin" && password == "admin123") || (username == "user1" && password == "password1") { + return + } + t.Errorf("Unexpected login for username %q and password %q", username, password) + } + }) +} diff --git a/myfuzz/run_fuzz.sh b/myfuzz/run_fuzz.sh new file mode 100755 index 0000000..944a692 --- /dev/null +++ b/myfuzz/run_fuzz.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cp ../data/users.db test_data.db + +go test -fuzz=FuzzAuth -fuzztime=5m --parallel=1 +go test -run=FuzzAuth -coverprofile=cover.out -coverpkg=../... | tee cover.txt diff --git a/myfuzz/run_go-fuzz.sh b/myfuzz/run_go-fuzz.sh new file mode 100755 index 0000000..ae4e419 --- /dev/null +++ b/myfuzz/run_go-fuzz.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cp ../data/users.db test_data.db + +go-fuzz -bin=myfuzz-fuzz.zip -workdir=work diff --git a/myfuzz/view_fuzz_coverage.sh b/myfuzz/view_fuzz_coverage.sh new file mode 100755 index 0000000..151a7c2 --- /dev/null +++ b/myfuzz/view_fuzz_coverage.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +go tool cover -html=cover.out diff --git a/scripts/config.sh b/scripts/config.sh index fb5af12..327c772 100644 --- a/scripts/config.sh +++ b/scripts/config.sh @@ -9,7 +9,10 @@ sudo apt update echo "Установка Go, GCC и SQLite..." sudo apt install -y golang gcc sqlite3 libsqlite3-dev ca-certificates +echo "Создание директории для хранения БД" +mkdir -p data + echo "Установка Go модулей..." go mod tidy -echo "Среда готова." \ No newline at end of file +echo "Среда готова."