ronin-rb/ronin-fuzzer

View on GitHub
lib/ronin/fuzzing.rb

Summary

Maintainability
A
0 mins
Test Coverage
#
# ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data.
#
# Copyright (c) 2006-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of ronin-fuzzer.
#
# ronin-fuzzer is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-fuzzer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-fuzzer.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/fuzzing/fuzzer'
require 'ronin/fuzzing/mutator'
require 'ronin/fuzzing/repeater'
require 'ronin/fuzzing/core_ext'

require 'set'

module Ronin
  #
  # Contains class-methods which generate malicious data for fuzzing.
  #
  # @see Fuzzing.[]
  #
  module Fuzzing
    # Short String lengths
    SHORT_LENGTHS = Set[1, 100, 500, 1_000, 10_000]

    # Long String lengths
    LONG_LENGTHS = Set[
      128, 255, 256, 257, 511, 512, 513, 1023, 1024, 2048, 2049, 4095,
      4096, 4097, 5_000, 10_000, 20_000, 32762, 32763, 32764, 32765, 32766,
      32767, 32768, 32769,
      0xffff-2, 0xffff-1, 0xffff, 0xffff+1, 0xffff+2,
      99_999, 100_000, 500_000, 1_000_000
    ]

    # Null bytes in various encodings
    NULL_BYTES = ['%00', '%u0000', "\x00"]

    # Newline characters
    NEW_LINES = ["\n", "\r", "\n\r"]

    # Format String flags
    FORMAT_STRINGS = ['%p', '%s', '%n']

    #
    # Returns a fuzzer method.
    #
    # @param [Symbol] name
    #   The name of the fuzzer to return.
    #
    # @return [Enumerator]
    #   An Enumerator for the fuzzer method.
    #
    # @raise [NoMethodError]
    #   The fuzzing method could not be found.
    #
    # @api semipublic
    #
    def self.[](name)
      if (!respond_to?(name) || Module.respond_to?(name))
        raise(NoMethodError,"no such fuzzing method: #{name}")
      end

      return enum_for(name)
    end

    module_function

    #
    # Various bad-strings.
    #
    # @yield [string]
    #   The given block will be passed each bad-string.
    #
    # @yieldparam [String] string
    #   A bad-string containing known control characters, deliminators
    #   or null-bytes (see {NULL_BYTES}), of varying length
    #   (see {SHORT_LENGTHS} and {LONG_LENGTHS}).
    #
    def bad_strings(&block)
      yield ''

      chars = [
        'A', 'a', '1', '<', '>', '"', "'", '/', "\\", '?', '=', 'a=', '&',
        '.', ',', '(', ')', ']', '[', '%', '*', '-', '+', '{', '}',
        "\x14", "\xfe", "\xff"
      ]
      
      chars.each do |c|
        LONG_LENGTHS.each { |length| yield c * length }
      end

      yield '!@#$%%^#$%#$@#$%$$@#$%^^**(()'
      yield '%01%02%03%04%0a%0d%0aADSF'
      yield '%01%02%03@%04%0a%0d%0aADSF'

      NULL_BYTES.each do |c|
        SHORT_LENGTHS.each { |length| yield c * length }
      end

      yield "%\xfe\xf0%\x00\xff"
      yield "%\xfe\xf0%\x00\xff" * 20

      SHORT_LENGTHS.each do |length|
        yield "\xde\xad\xbe\xef" * length
      end

      yield "\n\r" * 100
      yield "<>"   * 500
    end

    #
    # Various format-strings.
    #
    # @yield [fmt_string]
    #   The given block will be passed each format-string.
    #
    # @yieldparam [String] fmt_string
    #   A format-string containing format operators (see {FORMAT_STRINGS}).
    #
    def format_strings(&block)
      FORMAT_STRINGS.each do |fmt|
        yield fmt
        yield fmt * 100
        yield fmt * 500
        yield "\"#{fmt}\"" * 500
      end
    end

    #
    # Various bad paths and directory traversals.
    #
    # @yield [path]
    #   The given block will be passed each path.
    #
    # @yieldparam [String] path
    #   A known bad path.
    #
    def bad_paths(&block)
      padding = 'A' * 5_000

      yield "/.:/#{padding}\x00\x00"
      yield "/.../#{padding}\x00\x00"

      yield "\\\\*"
      yield "\\\\?\\"

      yield "/\\" * 5_000
      yield '/.'  * 5_000

      NULL_BYTES.each do |c|
        if c.start_with?('%')
          yield "#{c}/"
          yield "/#{c}"
          yield "/#{c}/"
        end
      end
    end

    #
    # The range of bit-fields.
    #
    # @yield [bitfield]
    #   The given block will be passed each bit-field.
    #
    # @yieldparam [String] bitfield
    #   A bit-field (8bit - 64bit).
    #
    def bit_fields(&block)
      ("\x00".."\xff").each do |c|
        yield c
        yield c << c # x2
        yield c << c # x4
        yield c << c # x8
      end
    end

    #
    # The range of signed bit-fields.
    #
    # @yield [bitfield]
    #   The given block will be passed each bit-field.
    #
    # @yieldparam [String] bitfield
    #   A signed bit-field (8bit - 64bit).
    #
    def signed_bit_fields(&block)
      ("\x80".."\xff").each do |c|
        yield c
        yield c << c # x2
        yield c << c # x4
        yield c << c # x8
      end
    end

    #
    # The range of unsigned 8bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A unsigned 8bit integer.
    #
    def uint8(&block)
      ("\x00".."\xff").each(&block)
    end

    #
    # The range of unsigned 16bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A unsigned 16bit integer.
    #
    def uint16
      uint8 { |c| yield c * 2 }
    end

    #
    # The range of unsigned 32bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A unsigned 32bit integer.
    #
    def uint32
      uint8 { |c| yield c * 4 }
    end

    #
    # The range of unsigned 64bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A unsigned 64bit integer.
    #
    def uint64
      uint8 { |c| yield c * 8 }
    end

    #
    # The range of signed 8bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A signed 8bit integer.
    #
    def int8(&block)
      ("\x00".."\x70").each(&block)
    end

    #
    # The range of signed 16bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A signed 16bit integer.
    #
    def int16
      int8 { |c| yield c * 2 }
    end

    #
    # The range of signed 32bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A signed 32bit integer.
    #
    def int32
      int8 { |c| yield c * 4 }
    end

    #
    # The range of signed 64bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A signed 64bit integer.
    #
    def int64
      int8 { |c| yield c * 8 }
    end

    #
    # The range of negative-signed 8bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A negative-signed 8bit integer.
    #
    def sint8(&block)
      ("\x80".."\xff").each(&block)
    end

    #
    # The range of negative-signed 16bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A negative-signed 16bit integer.
    #
    def sint16
      sint8 { |c| yield c * 2 }
    end

    #
    # The range of negative-signed 32bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A negative-signed 32bit integer.
    #
    def sint32
      sint8 { |c| yield c * 4 }
    end

    #
    # The range of negative-signed 64bit integers.
    #
    # @yield [int]
    #   The given block will be passed each integer.
    #
    # @yieldparam [String] int
    #   A negative-signed 64bit integer.
    #
    def sint64
      sint8 { |c| yield c * 8 }
    end

  end
end