Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
ruzzy — 순수 Ruby 코드와 Ruby C 확장을 위한 커버리지 기반 퍼저 | Kitploit
도구/GitHubGitHub/trailofbits/ruzzy
Dynamic Analysis (Sandboxing)Vulnerability AnalysisCode AnalysisFuzzingBinary AnalysisLearning & Education
GitHubtrailofbits/ruzzy

ruzzy

순수 Ruby 코드와 Ruby C 확장을 위한 커버리지 기반 퍼저

저장소 보기
123742개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

Ruzzy

Test Gem Version

순수 Ruby 코드와 Ruby C 확장을 위한 커버리지 기반 퍼저입니다.

Ruzzy는 Python 퍼저인 Google의 Atheris에서 큰 영감을 받았습니다. Ruzzy는 커버리지 계측과 퍼징 엔진을 위해 libFuzzer(또는 LibAFL)를 사용합니다. 또한 Ruzzy는 C 확장을 퍼징할 때 AddressSanitizer와 UndefinedBehaviorSanitizer를 지원합니다. Ruzzy의 영감에 대해 더 자세히 알고 싶다면 저희 논문인 Design and Implementation of a Coverage-Guided Ruby Fuzzer를 참조하세요.

목차:

  • 설치
  • 사용
    • 시작하기
    • 순수 Ruby 코드 퍼징
    • Ruby C 확장 퍼징
  • 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 환경 변수는 Ruzzy C 확장을 컴파일할 때 make 명령을 재정의합니다. 이는 익스텐션 컴파일 시 후속 환경 변수들을 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

다음과 같은 sanitizer를 사용할 수 있습니다:

  • Ruzzy::ASAN_PATH — AddressSanitizer용
  • Ruzzy::UBSAN_PATH — UndefinedBehaviorSanitizer용

순수 Ruby 코드 퍼징

예시로 작은 Ruby 스크립트를 퍼징해 보겠습니다. 순수 Ruby 코드를 퍼징하려면 트레이서 스크립트(tracer script)와 퍼징 하니스(fuzzing harness)라는 두 개의 Ruby 스크립트가 필요합니다. 트레이서 스크립트가 필요한 이유는 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를 수정하여 대상 함수를 호출하도록 하세요.

Ruby C 확장 퍼징

예시로 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를 지정하고 있습니다. 이 플래그들은 퍼징 과정을 돕습니다. address sanitizer와 같은 유용한 기능과 개선된 스택 트레이스 정보를 활성화합니다. 자세한 내용은 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 options을 참조하세요.

자신의 대상을 퍼징하려면 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 사용자 참고 사항

macOS에서 Ruzzy를 사용하려면 Homebrew로 설치한 LLVM(Apple Clang에는 libFuzzer가 포함되어 있지 않음)과 시스템 Ruby가 아닌 Ruby가 필요합니다(/usr/bin/ruby에 있는 시스템 Ruby는 SIP로 보호되어 Ruby가 시작되기 전에 DYLD_* 환경 변수가 제거됩니다).

전제 조건

root@kitploit:~
brew install llvm ruby

시스템 Ruby가 아닌 어떤 Ruby든 사용할 수 있습니다(brew, rbenv, asdf). 다만 shim 기반 버전 관리자에 대해서는 아래 주의 사항을 참조하세요.

설치

Homebrew Clang 경로와 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

실행

LD_PRELOAD 대신 DYLD_INSERT_LIBRARIES를 사용하세요:

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

macOS에서 Ruzzy::ASAN_PATH와 Ruzzy::UBSAN_PATH는 .dylib 파일로 확인됩니다.

주의 사항

  • 버전 관리자 shim(asdf, rbenv)은 DYLD_* 환경 변수를 제거합니다. 이러한 shim은 #!/usr/bin/env bash를 사용하며 /usr/bin/env는 SIP로 보호되므로 macOS는 Ruby가 시작되기 전에 DYLD_INSERT_LIBRARIES를 제거합니다. Homebrew Ruby(shim이 없음)를 사용하거나 설치된 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로 ASan dylib을 주입할 때 시작 중 프로세스가 멈추는 버그가 있습니다. Ruzzy가 실행 시 멈춘다면 Homebrew LLVM을 업데이트하세요(brew upgrade llvm).

발견 사례

Ruzzy로 발견된 버그:

  • toml gem: #76
  • toml-rb gem: #150
  • ox gem: #351, #410
  • Ruby Marshal 가비지 컬렉터 크래시: #20941
  • XML 파서 차등 분석: REXML vs. Nokogiri
  • redcarpet gem: #813

개발

개발은 로컬에서 하거나 이 저장소에 제공된 Dockerfile을 사용하여 수행할 수 있습니다.

다음 명령어로 Ruzzy Docker 이미지를 빌드할 수 있습니다:

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

그런 다음 다음 명령어로 컨테이너에 셸로 접속할 수 있습니다:

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

컴파일

Ruzzy의 C 확장을 컴파일하기 위해 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는 새 git 태그가 푸시되면 RubyGems에 자동으로 릴리스됩니다.

새 버전을 릴리스하려면 다음 명령어를 실행하세요:

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

추가 자료

  • Ruby C 확장
    • 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
    • https://github.com/google/atheris/blob/2.3.0/src/native/core.cc
도구 다운로드
메서드설명
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_PATHASan + 퍼저 래퍼의 경로. LD_PRELOAD(Linux) / DYLD_INSERT_LIBRARIES(macOS)와 함께 사용합니다.
Ruzzy::UBSAN_PATHUBSan용 동일한 래퍼.
Ruzzy::EXT_PATHext/cruzzy 빌드 디렉터리의 경로.
Ruzzy::DEFAULT_ARGS퍼저에 전달되는 기본 인자.
메서드설명반환 값
remaining_bytes소비되지 않고 남은 바이트 수Integer
consume_bytes(count)최대 count개의 원시 바이트 소비binary String
consume_random_length_string(max_length)가변 길이 문자열을 소비하며, \ + 비\ 바이트에서 종료String
consume_remaining_bytes남은 모든 바이트 소비binary String
consume_remaining_as_stringconsume_remaining_bytes의 별칭binary String
consume_uint(count)count 바이트에서 얻은 부호 없는 정수Integer
consume_int(count)count 바이트에서 얻은 부호 있는(2의 보수) 정수Integer
consume_int_in_range(min, max)[min, max] 범위에서 균일하게 분포하는 정수Integer
consume_bool한 바이트(LSB)에서 얻은 불리언true/false
consume_float전체 double 범위에 걸친 FloatFloat
consume_float_in_range(min, max)[min, max] 범위의 FloatFloat
consume_probability[0.0, 1.0] 범위의 FloatFloat
pick_value_in_list(list)list에서 무작위 요소 선택요소
  • 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