Crowbar, a fuzzing library

#1

Hi all,
I’ve been working on a project I would love some feedback on called Crowbar, it’s an all-purpose fuzzer that use perlin noise to automate the creation of recreatable test cases using a sample piece of data. It’s inspired by Radamsa, which is already a wonderful tool to use if you don’t already.

The tool is library only for now, but in the future I plan to make a CLI app for it as well.

The fuzzer itself is busted into three parts, selectors, mutators, and generators.

Selectors split and mark parts of the sample input for mutation. For example, you could write a selector that selects only a specific field in a header, or find all matches by regex. So far there is only one selector, Crowbar::Selector::Regex.

Mutators take matches from the selector, and mutate it in some way. When this happens, text that is changed is replaced by the text from the Mutator#mutate method. So far there are three mutators, Crowbar::Mutator::Replacer, Crowbar::Mutator::Repeater, Crowbar::Mutator::Remover

Generators don’t have to be used, but they can provide a useful way to make large sets of test data. It generates data using the Generator#make method. Generators are also free to use other generators to generate more complicated data.

Crowbar itself uses a perlin noise generator to make recreatable test sessions, a test session can always be recovered by resupplying the seed used.

Selectors, mutators, and generators all have “weights” attached to them that determine how often they might be used. This is to allow a little more control over when these types get run. Not all selectors, mutators, or generators might be used at the same time, however, the way the system is designed, it will never output text that is exactly the same as the original input.
Simple Example

require "./crowbar"
sample_input = "{ \"json\" : \"A String\", \"x\" : 0x123AA }"
cr = Crowbar.new(sample_input, seed: 4321) do |cr|
  # Selects quoted strings
  Crowbar::Selector::Regex.new(cr, Crowbar::Constants::Regex::IN_QUOTES) do |s|
    Crowbar::Mutator::Replacer.new(s) do |m|
      Crowbar::Generator::Wrapper.new(m, Crowbar::Generator::Naughty.new(m, types: [:null, :logic]))
    end
  end
end 

10.times do |x|
  pp cr.next
end

Output:

"{ \"json\" : (null), \"x\" : 0x123AA }"
"{ \"json\" : (null), \"x\" : 0x123AA }"
"{ \"json\" : \"A String\", \"FALSE\" : 0x123AA }"
"{ \"FALSE\" : \"A String\", \"x\" : 0x123AA }"
"{ \"json\" : \"false\", \"x\" : 0x123AA }"
"{ NAN : \"A String\", \"x\" : 0x123AA }"
"{ \"json\" : \"false\", \"x\" : 0x123AA }"
"{ \"json\" : undefined, \"x\" : 0x123AA }"
"{ \"json\" : undefined, \"x\" : 0x123AA }"
"{ \"json\" : undefined, \"x\" : 0x123AA }"

Complicated Example

require "./crowbar"
sample_input = "{ \"json\" : \"A String\", \"x\" : 0x123AA }"
cr = Crowbar.new(sample_input, seed: 321) do |cr|
  # Selects quoted strings
  Crowbar::Selector::Regex.new(cr, Crowbar::Constants::Regex::IN_QUOTES) do |s|
    Crowbar::Mutator::Replacer.new(s) do |m|
      Crowbar::Generator::Wrapper.new(m, Crowbar::Generator::Naughty.new(m, types: [:null, :logic]))
    end
    Crowbar::Mutator::Replacer.new(s) do |m|
      Crowbar::Generator::Wrapper.new(m, Crowbar::Generator::Bytes.new(m))
    end

    Crowbar::Mutator::Replacer.new(s) do |m|
      Crowbar::Generator::Wrapper.new(m, Crowbar::Generator::Naughty.new(m, types: [:null, :logic]))
    end
  end

    # Selects symbols/spaces, removes them and duplicates them
  s = Crowbar::Selector::Regex.new(cr, /\W/) do |s|
    Crowbar::Mutator::Remover.new(s) {|m|}.personal_weight = 0.3_f32
    Crowbar::Mutator::Repeater.new(s) {|m|}.personal_weight = 0.4_f32
  end
  # weigh the slector less so it doesn't go too wild
  s.personal_weight = 0.7_f32

  s = Crowbar::Selector::Regex.new(cr, /[a-zA-Z0-9]{1}/) do |s|
    Crowbar::Mutator::Remover.new(s) {|m|}.personal_weight = 0.1_f32
  end
  s.personal_weight = 0.1_f32
end 

10.times do |x|
  pp cr.next
end

Output:

"  ull : \xAEA,nll   :::  }"
"{ \"\u001D\xA4\u001D\" : \"A String\", \"false\" : 0x123AA }"
"{ \"json\"    \"A String\", x ::    }"
"{ \"&\xE2\xB9\" : \"A String\", \"x\" : 0x123AA }"
"{ \"json\" : \"A String\",x  ::  }"
"{ false : D!\xB7, FALSE : 0x123AA }"
"{ \"json\" : \"A String\"\"\"\"\"\"x\"\"\" :  }"
"{ \"json\" : \xE3\x9D\xC6, false : 0x123AA }"
"{ \"json\" : \"A   String   \"x\" : }"
"{ \"json\" : \"תW\", True : 0x123AA }"

Let me know what you think!

2 Likes