Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
ruzzy — Фаззер, управляемый покрытием, для чистого кода Ruby и C-расширений Ruby | Kitploit
Инструменты/GitHubGitHub/trailofbits/ruzzy
Динамический анализ (песочница)Анализ уязвимостейАнализ КодаФаззингАнализ Бинарных ФайловОбучение и Образование
GitHubtrailofbits/ruzzy

ruzzy

Фаззер, управляемый покрытием, для чистого кода Ruby и C-расширений Ruby

Репозиторий
12371 месяц назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Ruzzy

Test Gem Version

Фаззер с управлением покрытием для чистого Ruby-кода и C-расширений Ruby.

Ruzzy во многом вдохновлён Atheris от Google — фаззером для Python. Ruzzy использует libFuzzer (или LibAFL) для инструментирования покрытия и в качестве движка фаззинга. Ruzzy также поддерживает AddressSanitizer и UndefinedBehaviorSanitizer при фаззинге C-расширений. Если вы хотите узнать больше о том, что вдохновило Ruzzy, см. нашу статью: Design and Implementation of a Coverage-Guided Ruby Fuzzer.

Содержание:

  • Установка
  • Использование
    • Начало работы
    • Фаззинг чистого Ruby-кода
    • Фаззинг C-расширений Ruby
  • API
    • Ruzzy
    • FuzzedDataProvider
  • Примечания для пользователей macOS
  • Найденные ошибки
  • Разработка
    • Компиляция
    • Тестирование
    • Линтинг
    • Релиз
  • Дополнительные материалы

Установка

Ruzzy поддерживает Linux (x86-64, AArch64/ARM64) и macOS (Apple Silicon). В Windows вы можете собрать Dockerfile и/или использовать окружение для разработки. Ruzzy требует свежую версию clang (проверена работа начиная с 14.0.0), желательно последний релиз. Инструкции для macOS см. в примечаниях для пользователей macOS.

Установите Ruzzy следующей командой:

root@kitploit:~
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
    gem install ruzzy

Здесь происходит много всего, поэтому разберём по частям:

  • Переменная окружения MAKE переопределяет команду make при компиляции C-расширения Ruzzy. Она указывает make учитывать последующие переменные окружения при компиляции расширения.
  • Остальные переменные окружения используются во время компиляции, чтобы гарантировать использование правильных бинарников clang. Это обеспечивает наличие новейших возможностей clang, необходимых для корректного фаззинга.

Если при установке возникнут проблемы, выполните следующую команду, чтобы получить отладочный вывод:

root@kitploit:~
RUZZY_DEBUG=1 gem install --verbose ruzzy

Если ваша цель фаззинга на чистом Ruby активно использует регулярные выражения, установите regexp_parser:

root@kitploit:~
gem install regexp_parser

После установки Ruzzy автоматически использует эту возможность для сэмплирования (т.е. решения) регулярных выражений, встречающихся во время фаззинга. Это позволяет фаззеру проходить условия с регулярными выражениями и открывать дополнительное покрытие.

Использование

Начало работы

Ruzzy включает игрушечный пример для демонстрации работы. Сначала задайте следующую переменную окружения:

root@kitploit:~
export ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=0:use_sigaltstack=0"
Понимать эти опции не обязательно, но если вам интересно, нажмите сюда.

ASAN_OPTIONS

  1. Отказы выделения памяти распространены и имеют низкое влияние (DoS), поэтому пока пропустим их.
  2. Как и Python, интерпретатор Ruby утекает данные, так что пока игнорируем это.
  3. Ruby рекомендует отключать sigaltstack.

Затем можно запустить пример следующей командой:

root@kitploit:~
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby -e 'require "ruzzy"; Ruzzy.dummy'

LD_PRELOAD требуется по тем же причинам, что и в Atheris. Однако, в отличие от ASAN_OPTIONS, вероятно, не стоит export'ить его, так как он может мешать другим программам.

Он должен быстро привести к падению, как показано ниже:

root@kitploit:~
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2527961537
...
==45==ERROR: AddressSanitizer: heap-use-after-free on address 0x50c0009bab80 at pc 0xffff99ea1b44 bp 0xffffce8a67d0 sp 0xffffce8a67c8
...
SUMMARY: AddressSanitizer: heap-use-after-free /var/lib/gems/3.1.0/gems/ruzzy-0.8.0/ext/dummy/dummy.c:18:24 in _c_dummy_test_one_input
...
==45==ABORTING
MS: 4 EraseBytes-CopyPart-CopyPart-ChangeBit-; base unit: 410e5346bca8ee150ffd507311dd85789f2e171e
0x48,0x49,
HI
artifact_prefix='./'; Test unit written to ./crash-253420c1158bc6382093d409ce2e9cff5806e980
Base64: SEk=

Мы видим, что он корректно нашёл входные данные ("HI"), вызвавшие нарушение памяти. Дополнительную информацию о том, почему произошло это нарушение, см. в dummy.c.

Вы можете повторно запустить аварийный случай следующей командой:

root@kitploit:~
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby -e 'require "ruzzy"; Ruzzy.dummy' \
    ./crash-253420c1158bc6382093d409ce2e9cff5806e980

Доступны следующие санитайзеры:

  • Ruzzy::ASAN_PATH для AddressSanitizer
  • Ruzzy::UBSAN_PATH для UndefinedBehaviorSanitizer

Фаззинг чистого Ruby-кода

Давайте в качестве примера отфаззим небольшой Ruby-скрипт. Фаззинг чистого Ruby-кода требует двух Ruby-скриптов: скрипт-трассировщик и фаззинг-обвязку (fuzzing harness). Скрипт-трассировщик необходим из-за особенности реализации интерпретатора Ruby.

Сначала скрипт-трассировщик, назовём его test_tracer.rb:

root@kitploit:~
# frozen_string_literal: true

require 'ruzzy'

Ruzzy.trace('test_harness.rb')

Затем фаззинг-обвязка, назовём её test_harness.rb:

root@kitploit:~
# frozen_string_literal: true

require 'ruzzy'

def fuzzing_target(input)
  if input.length == 4
    if input[0] == 'F'
      if input[1] == 'U'
        if input[2] == 'Z'
          if input[3] == 'Z'
            raise
          end
        end
      end
    end
  end
end

test_one_input = lambda do |data|
  fuzzing_target(data) # Your fuzzing target would go here
  return 0
end

Ruzzy.fuzz(test_one_input)

Вы можете запустить этот файл и начать фаззинг следующей командой:

root@kitploit:~
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby test_tracer.rb

Он должен быстро привести к падению, как показано ниже:

root@kitploit:~
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2311041000
...
/app/ruzzy/bin/test_harness.rb:12:in `block in <top (required)>': unhandled exception
	from /var/lib/gems/3.1.0/gems/ruzzy-0.8.0/lib/ruzzy.rb:15:in `c_fuzz'
	from /var/lib/gems/3.1.0/gems/ruzzy-0.8.0/lib/ruzzy.rb:15:in `fuzz'
	from /app/ruzzy/bin/test_harness.rb:35:in `<top (required)>'
	from bin/test_tracer.rb:7:in `require_relative'
	from bin/test_tracer.rb:7:in `<main>'
...
SUMMARY: libFuzzer: fuzz target exited
MS: 1 CopyPart-; base unit: 24b4b428cf94c21616893d6f94b30398a49d27cc
0x46,0x55,0x5a,0x5a,
FUZZ
artifact_prefix='./'; Test unit written to ./crash-aea2e3923af219a8956f626558ef32f30a914ebc
Base64: RlVaWg==

Мы видим, что он корректно нашёл входные данные ("FUZZ"), вызвавшие исключение.

Чтобы фаззить собственную цель, измените test_one_input lambda так, чтобы она вызывала вашу функцию.

Фаззинг C-расширений Ruby

Давайте в качестве примера отфаззим библиотеку msgpack-ruby. Сначала установите gem:

root@kitploit:~
MAKE="make --environment-overrides V=1" \
CC="/path/to/clang" \
CXX="/path/to/clang++" \
LDSHARED="/path/to/clang -shared" \
LDSHAREDXX="/path/to/clang++ -shared" \
CFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
CXXFLAGS="-fsanitize=address,fuzzer-no-link -fno-omit-frame-pointer -fno-common -fPIC -g" \
    gem install msgpack

В дополнение к переменным окружения, использованным при компиляции Ruzzy, мы задаём CFLAGS и CXXFLAGS. Эти флаги помогают в процессе фаззинга: они включают полезные функции, такие как AddressSanitizer, и улучшают информацию в стек-трейсах. Подробнее см. AddressSanitizerFlags.

Далее нам нужна фаззинг-обвязка для msgpack. Следующий код может быть знаком тем, у кого есть опыт работы с libFuzzer:

root@kitploit:~
# frozen_string_literal: true

require 'msgpack'
require 'ruzzy'

test_one_input = lambda do |data|
  begin
    MessagePack.unpack(data)
  rescue Exception
    # We're looking for memory corruption, not Ruby exceptions
  end
  return 0
end

Ruzzy.fuzz(test_one_input)

Назовём этот файл fuzz_msgpack.rb. Вы можете запустить его и начать фаззинг следующей командой:

root@kitploit:~
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby fuzz_msgpack.rb

Опции libFuzzer можно передавать Ruby-скрипту следующим образом:

root@kitploit:~
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby fuzz_msgpack.rb /path/to/corpus

Подробнее см. опции libFuzzer.

Чтобы фаззить собственную цель, измените test_one_input lambda так, чтобы она вызывала вашу функцию.

API

Ruzzy

Модуль Ruzzy предоставляет точки входа верхнего уровня.

FuzzedDataProvider

Ruzzy::FuzzedDataProvider разделяет сырые байты фаззера на типизированные значения Ruby.

root@kitploit:~
test_one_input = lambda do |data|
  fdp = Ruzzy::FuzzedDataProvider.new(data)
  name = fdp.consume_random_length_string(50)
  age = fdp.consume_int_in_range(0, 150)
  score = fdp.consume_float_in_range(0.0, 100.0)
  role = fdp.pick_value_in_list(['admin', 'user', 'guest'])
  User.new(name: name, age: age, score: score, role: role).validate!
end

Ruzzy.fuzz(test_one_input)

Когда данные исчерпаны, все методы возвращают значения по умолчанию (0, "", false, min).

Примечания для пользователей macOS

Ruzzy на macOS требует LLVM, установленный через Homebrew (Apple Clang не включает libFuzzer), и несистемный Ruby (системный Ruby в /usr/bin/ruby защищён SIP, из-за чего переменные окружения DYLD_* удаляются до запуска Ruby).

Предварительные требования

root@kitploit:~
brew install llvm ruby

Подойдёт любой несистемный Ruby (brew, rbenv, asdf), но для менеджеров версий на основе шим см. предостережения ниже.

Установка

Используйте пути к Clang из Homebrew и подходящие для macOS флаги компоновщика:

root@kitploit:~
MAKE="make --environment-overrides V=1" \
CC="$(brew --prefix llvm)/bin/clang" \
CXX="$(brew --prefix llvm)/bin/clang++" \
LDSHARED="$(brew --prefix llvm)/bin/clang -dynamic -bundle -undefined dynamic_lookup" \
LDSHAREDXX="$(brew --prefix llvm)/bin/clang++ -dynamic -bundle -undefined dynamic_lookup" \
    gem install ruzzy

Запуск

Используйте DYLD_INSERT_LIBRARIES вместо LD_PRELOAD:

root@kitploit:~
DYLD_INSERT_LIBRARIES=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    ruby -e 'require "ruzzy"; Ruzzy.dummy'

Ruzzy::ASAN_PATH и Ruzzy::UBSAN_PATH на macOS указывают на файлы .dylib.

Предостережения

  • Шимы менеджеров версий (asdf, rbenv) удаляют переменные окружения DYLD_*. Эти шимы используют #!/usr/bin/env bash, а /usr/bin/env защищён SIP, поэтому macOS удаляет DYLD_INSERT_LIBRARIES до запуска Ruby. Либо используйте Homebrew Ruby (у которого нет шима), либо вызывайте абсолютный путь к установленному бинарнику Ruby:
    root@kitploit:~
    DYLD_INSERT_LIBRARIES=$(/path/to/ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
        /path/to/ruby your_fuzzer.rb
    
  • Нужен свежий LLVM. В некоторых старых версиях Homebrew LLVM (особенно 19.x) есть баг: при использовании DYLD_INSERT_LIBRARIES с dylib ASan процесс зависает во время запуска. Если Ruzzy зависает при старте, обновите Homebrew LLVM (brew upgrade llvm).

Найденные ошибки

Ошибки, найденные с помощью Ruzzy:

  • gem toml: #76
  • gem toml-rb: #150
  • gem ox: #351, #410
  • Сбой сборщика мусора в Ruby Marshal: #20941
  • Расхождение XML-парсеров: REXML vs. Nokogiri
  • gem redcarpet: #813

Разработка

Разработку можно вести локально или с помощью Dockerfile, предоставленного в этом репозитории.

Вы можете собрать Docker-образ Ruzzy следующей командой:

root@kitploit:~
docker build --tag ruzzy .

Затем вы можете войти в контейнер с помощью следующей команды:

root@kitploit:~
docker run -it -v $(pwd):/app/ruzzy --entrypoint /bin/bash ruzzy

Компиляция

Для компиляции C-расширений Ruzzy мы используем rake-compiler.

Вы можете скомпилировать C-расширения внутри контейнера следующей командой:

root@kitploit:~
rake compile

Тестирование

Для тестирования Ruby-кода мы используем модульные тесты rake.

Вы можете запустить тесты внутри контейнера следующей командой:

root@kitploit:~
LD_PRELOAD=$(ruby -e 'require "ruzzy"; print Ruzzy::ASAN_PATH') \
    rake test

Линтинг

Для линтинга Ruby-кода мы используем rubocop.

Вы можете запустить rubocop внутри контейнера следующей командой:

root@kitploit:~
rubocop

Релиз

Ruzzy автоматически публикуется на RubyGems при пуше нового git-тега.

Чтобы выпустить новую версию, выполните следующие команды:

root@kitploit:~
git tag vX.X.X
root@kitploit:~
git push --tags

Дополнительные материалы

  • C-расширения Ruby
    • https://guides.rubygems.org/gems-with-extensions/
    • https://www.rubyguides.com/2018/03/write-ruby-c-extension/
    • https://rubyreferences.github.io/rubyref/advanced/extensions.html
    • https://silverhammermba.github.io/emberb/c/
    • https://ruby-doc.org/3.3.0/extension_rdoc.html
    • https://ruby-doc.org/3.3.0/stdlibs/mkmf/MakeMakefile.html
    • https://github.com/flavorjones/ruby-c-extensions-explained
    • https://github.com/ruby/ruby/blob/v3_3_0/lib/mkmf.rb
  • Фаззинг Ruby
    • https://github.com/twistlock/kisaten
    • https://github.com/richo/afl-ruby
    • https://github.com/krypt/FuzzBert
    • https://z2-2z.github.io/2024/jan/16/fuzzing-ruby-c-extensions-with-coverage-and-asan.html
    • https://bsidessf2018.sched.com/event/E6jC/fuzzing-ruby-and-c-extensions
  • Atheris
    • https://github.com/google/atheris/blob/master/native_extension_fuzzing.md
    • https://security.googleblog.com/2020/12/how-atheris-python-fuzzer-works.html
    • https://github.com/google/atheris/blob/2.3.0/setup.py
Скачать инструмент
МетодОписание
Ruzzy.fuzz(test_one_input, args = DEFAULT_ARGS)Фаззит test_one_input (proc/lambda, принимающий сырые байты).
Ruzzy.trace(harness_script)Оборачивает harness_script инструментированием покрытия ветвей Ruby, затем подключает его через require. Требуется для фаззинга чистого Ruby.
Ruzzy.dummyФаззит встроенную игрушечную обвязку (демо heap-use-after-free).
Ruzzy.dummy_test_one_input(data)Сама игрушечная обвязка.
КонстантаОписание
Ruzzy::ASAN_PATHПуть к обёртке ASan + фаззера. Используйте с LD_PRELOAD (Linux) / DYLD_INSERT_LIBRARIES (macOS).
Ruzzy::UBSAN_PATHАналогично, для UBSan.
Ruzzy::EXT_PATHПуть к каталогу сборки ext/cruzzy.
Ruzzy::DEFAULT_ARGSАргументы по умолчанию, передаваемые фаззеру.
МетодОписаниеВозвращаемое значение
remaining_bytesКоличество ещё не потреблённых байтовInteger
consume_bytes(count)Потребляет до count сырых байтовбинарная String
consume_random_length_string(max_length)Потребляет строку переменной длины; завершается на \ + не-\ байтеString
consume_remaining_bytesПотребляет все оставшиеся байтыбинарная String
consume_remaining_as_stringПсевдоним для consume_remaining_bytesбинарная String
consume_uint(count)Беззнаковое целое из count байтовInteger
consume_int(count)Знаковое целое (в дополнительном коде) из count байтовInteger
consume_int_in_range(min, max)Целое, равномерно распределённое в диапазоне [min, max]Integer
consume_boolЛогическое значение из одного байта (младший бит)true/false
consume_floatЧисло с плавающей запятой, покрывающее весь диапазон doubleFloat
consume_float_in_range(min, max)Число с плавающей запятой в диапазоне [min, max]Float
consume_probabilityЧисло с плавающей запятой в диапазоне [0.0, 1.0]Float
pick_value_in_list(list)Случайный элемент из listэлемент
https://github.com/google/atheris/blob/2.3.0/src/native/core.cc
  • https://github.com/google/atheris/blob/2.3.0/src/native/tracer.cc
  • https://github.com/google/atheris/blob/2.3.0/src/native/counters.cc
  • https://github.com/google/atheris/blob/2.3.0/src/instrument_bytecode.py
  • Покрытие кода
    • https://calabi-yau.space/blog/sanitizer-coverage-interface.html
    • https://carstein.github.io/2020/05/21/writing-simple-fuzzer-4.html
    • https://h0mbre.github.io/Fuzzing-Like-A-Caveman-5/
    • https://github.com/mirrorer/afl/blob/master/docs/technical_details.txt
    • https://lcamtuf.coredump.cx/afl/historical_notes.txt
    • https://www.code-intelligence.com/blog/the-magic-behind-feedback-based-fuzzing
    • https://blog.includesecurity.com/2024/04/coverage-guided-fuzzing-extending-instrumentation/
    • https://git.sr.ht/~myrrc/ba-thesis/blob/master/thesis.pdf
    • https://www.politesi.polimi.it/bitstream/10589/173614/3/2021_04_Frighetto.pdf
    • https://wcventure.github.io/FuzzingPaper/Paper/SP18_ColLAFL.pdf
    • https://www.ndss-symposium.org/wp-content/uploads/2020/02/24422.pdf
    • https://mboehme.github.io/paper/ICSE22.pdf
    • https://www.usenix.org/system/files/raid2019-wang-jinghan.pdf