ronin-fuzzer 0.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ #
2
+ # ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data.
3
+ #
4
+ # Copyright (c) 2006-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ #
6
+ # This file is part of ronin-fuzzer.
7
+ #
8
+ # ronin-fuzzer is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Lesser General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # ronin-fuzzer is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public License
19
+ # along with ronin-fuzzer. If not, see <https://www.gnu.org/licenses/>.
20
+ #
21
+
22
+ require 'ronin/fuzzing'
23
+ require 'ronin/support/text/patterns'
24
+
25
+ require 'strscan'
26
+
27
+ module Ronin
28
+ module Fuzzing
29
+ #
30
+ # Fuzzing class that incrementally fuzzes a String, given substitution
31
+ # rules.
32
+ #
33
+ # @api semipublic
34
+ #
35
+ class Fuzzer
36
+
37
+ PATTERNS = Support::Text::Patterns
38
+
39
+ # Patterns and their substitutions
40
+ attr_reader :rules
41
+
42
+ #
43
+ # Initializes a new Fuzzer.
44
+ #
45
+ # @param [Hash{Regexp,String,Symbol => Enumerable,Symbol}] rules
46
+ # Patterns and their substitutions.
47
+ #
48
+ def initialize(rules)
49
+ @rules = {}
50
+
51
+ rules.each do |pattern,substitution|
52
+ pattern = case pattern
53
+ when Regexp then pattern
54
+ when String then Regexp.new(Regexp.escape(pattern))
55
+ when Symbol then PATTERNS.const_get(pattern.upcase)
56
+ else
57
+ raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
58
+ end
59
+
60
+ substitution = case substitution
61
+ when Enumerable then substitution
62
+ when Symbol then Fuzzing[substitution]
63
+ else
64
+ raise(TypeError,"substitutions must be Enumerable or a Symbol")
65
+ end
66
+
67
+ @rules[pattern] = substitution
68
+ end
69
+ end
70
+
71
+ #
72
+ # Incrementally fuzzes the String.
73
+ #
74
+ # @yield [fuzz]
75
+ # The given block will be passed every fuzzed String.
76
+ #
77
+ # @yieldparam [String] fuzz
78
+ # A fuzzed String.
79
+ #
80
+ # @return [Enumerator]
81
+ # If no block is given, an Enumerator will be returned.
82
+ #
83
+ def each(string)
84
+ return enum_for(__method__,string) unless block_given?
85
+
86
+ @rules.each do |pattern,substitution|
87
+ scanner = StringScanner.new(string)
88
+ indices = []
89
+
90
+ while scanner.scan_until(pattern)
91
+ indices << [scanner.pos - scanner.matched_size, scanner.matched_size]
92
+ end
93
+
94
+ indices.each do |index,length|
95
+ substitution.each do |substitute|
96
+ substitute = case substitute
97
+ when Proc then substitute[string[index,length]]
98
+ when Integer then substitute.chr
99
+ else substitute.to_s
100
+ end
101
+
102
+ fuzzed = string.dup
103
+ fuzzed[index,length] = substitute
104
+ yield fuzzed
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,163 @@
1
+ #
2
+ # ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data.
3
+ #
4
+ # Copyright (c) 2006-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ #
6
+ # This file is part of ronin-fuzzer.
7
+ #
8
+ # ronin-fuzzer is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Lesser General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # ronin-fuzzer is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public License
19
+ # along with ronin-fuzzer. If not, see <https://www.gnu.org/licenses/>.
20
+ #
21
+
22
+ require 'ronin/fuzzing'
23
+ require 'ronin/support/text/patterns'
24
+
25
+ require 'combinatorics/list_comprehension'
26
+ require 'combinatorics/power_set'
27
+ require 'combinatorics/generator'
28
+ require 'strscan'
29
+ require 'set'
30
+
31
+ module Ronin
32
+ module Fuzzing
33
+ #
34
+ # Fuzzer class that permutates over every mutation of a String, given
35
+ # mutation rules.
36
+ #
37
+ # @api semipublic
38
+ #
39
+ class Mutator
40
+
41
+ PATTERNS = Support::Text::Patterns
42
+
43
+ # Mutation rules
44
+ attr_reader :rules
45
+
46
+ #
47
+ # Initialize the Mutator.
48
+ #
49
+ # @param [Hash{Regexp,String,Symbol => Symbol,Enumerable}] rules
50
+ # The patterns and substitutions to mutate the String with.
51
+ #
52
+ # @raise [TypeError]
53
+ # A mutation pattern was not a Regexp, String or Symbol.
54
+ # A mutation substitution was not a Symbol or Enumerable.
55
+ #
56
+ def initialize(rules)
57
+ @rules = {}
58
+
59
+ rules.each do |pattern,mutation|
60
+ pattern = case pattern
61
+ when Regexp
62
+ pattern
63
+ when String
64
+ Regexp.new(Regexp.escape(pattern))
65
+ when Symbol
66
+ PATTERNS.const_get(pattern.upcase)
67
+ else
68
+ raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
69
+ end
70
+
71
+ mutation = case mutation
72
+ when Enumerable
73
+ mutation
74
+ when Symbol
75
+ Ronin::Fuzzing[mutation]
76
+ else
77
+ raise(TypeError,"mutation #{mutation.inspect} must be a Symbol or Enumerable")
78
+ end
79
+
80
+ @rules[pattern] = mutation
81
+ end
82
+ end
83
+
84
+ #
85
+ # Permutes over every possible mutation of the String.
86
+ #
87
+ # @param [String] string
88
+ # The String to be mutated.
89
+ #
90
+ # @yield [mutant]
91
+ # The given block will be yielded every possible mutant String.
92
+ #
93
+ # @yieldparam [String] mutant
94
+ # A mutated String.
95
+ #
96
+ # @return [Enumerator]
97
+ # If no block is given, an Enumerator will be returned.
98
+ #
99
+ def each(string)
100
+ return enum_for(__method__,string) unless block_given?
101
+
102
+ matches = Set[]
103
+
104
+ @rules.each do |pattern,mutation|
105
+ scanner = StringScanner.new(string)
106
+
107
+ while scanner.scan_until(pattern)
108
+ length = scanner.matched_size
109
+ index = scanner.pos - length
110
+ original = scanner.matched
111
+
112
+ mutator = Combinatorics::Generator.new do |g|
113
+ mutation.each do |mutate|
114
+ g.yield case mutate
115
+ when Proc
116
+ mutate.call(original)
117
+ when Integer
118
+ mutate.chr
119
+ else
120
+ mutate.to_s
121
+ end
122
+ end
123
+ end
124
+
125
+ matches << [index, length, mutator]
126
+ end
127
+ end
128
+
129
+ matches.powerset do |submatches|
130
+ # ignore the empty Set
131
+ next if submatches.empty?
132
+
133
+ # sort the submatches by index
134
+ submatches = submatches.sort_by { |index,length,mutator| index }
135
+ sets = []
136
+ prev_index = 0
137
+
138
+ submatches.each do |index,length,mutator|
139
+ # add the previous substring to the set of Strings
140
+ if index > prev_index
141
+ sets << [string[prev_index,index - prev_index]]
142
+ end
143
+
144
+ # add the mutator to the set of Strings
145
+ sets << mutator
146
+
147
+ prev_index = index + length
148
+ end
149
+
150
+ # add the remaining substring to the set of Strings
151
+ if prev_index < string.length
152
+ sets << [string[prev_index..-1]]
153
+ end
154
+
155
+ sets.comprehension { |strings| yield strings.join }
156
+ end
157
+
158
+ return nil
159
+ end
160
+
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,81 @@
1
+ #
2
+ # ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data.
3
+ #
4
+ # Copyright (c) 2006-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ #
6
+ # This file is part of ronin-fuzzer.
7
+ #
8
+ # ronin-fuzzer is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Lesser General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # ronin-fuzzer is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public License
19
+ # along with ronin-fuzzer. If not, see <https://www.gnu.org/licenses/>.
20
+ #
21
+
22
+ module Ronin
23
+ module Fuzzing
24
+ #
25
+ # Fuzzing class that generates repeated data.
26
+ #
27
+ # @api semipublic
28
+ #
29
+ class Repeater
30
+
31
+ # The lengths to repeat the data by
32
+ attr_reader :lengths
33
+
34
+ #
35
+ # Initializes a new Repeater.
36
+ #
37
+ # @param [Enumerable, Integer] lengths
38
+ # The lengths to repeat the data by.
39
+ #
40
+ # @raise [TypeError]
41
+ # `lengths` must either be Enumerable or an Integer.
42
+ #
43
+ def initialize(lengths)
44
+ @lengths = case lengths
45
+ when Integer
46
+ [lengths]
47
+ when Enumerable
48
+ lengths
49
+ else
50
+ raise(TypeError,"argument must be Enumerable or an Integer")
51
+ end
52
+ end
53
+
54
+ #
55
+ # Enumerates through each length of repeated data.
56
+ #
57
+ # @param [#*] repeatable
58
+ # The repeatable data.
59
+ #
60
+ # @yield [repeated]
61
+ # The given block will be passed every repeated String.
62
+ #
63
+ # @yieldparam [String] repeated
64
+ # A repeated version of the String.
65
+ #
66
+ # @return [Enumerator]
67
+ # If no block is given, an Enumerator will be returned.
68
+ #
69
+ def each(repeatable)
70
+ return enum_for(__method__,repeatable) unless block_given?
71
+
72
+ @lengths.each do |length|
73
+ yield(repeatable * length)
74
+ end
75
+
76
+ return nil
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,133 @@
1
+ #
2
+ # ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data.
3
+ #
4
+ # Copyright (c) 2006-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ #
6
+ # This file is part of ronin-fuzzer.
7
+ #
8
+ # ronin-fuzzer is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Lesser General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # ronin-fuzzer is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public License
19
+ # along with ronin-fuzzer. If not, see <https://www.gnu.org/licenses/>.
20
+ #
21
+
22
+ require 'chars'
23
+ require 'combinatorics/generator'
24
+ require 'combinatorics/list_comprehension'
25
+
26
+ module Ronin
27
+ module Fuzzing
28
+ #
29
+ # Fuzzing class that generates Strings based on a template.
30
+ #
31
+ # @api semipublic
32
+ #
33
+ class Template
34
+
35
+ include Enumerable
36
+
37
+ #
38
+ # Initializes a new Fuzzing template.
39
+ #
40
+ # @param [Array(<String,Symbol,Enumerable>, <Integer,Array,Range>)] fields
41
+ # The fields which defines the string or character sets which will
42
+ # make up parts of the String.
43
+ #
44
+ # @raise [ArgumentError]
45
+ # A given character set name was unknown.
46
+ #
47
+ # @raise [TypeError]
48
+ # A given string set was not a String, Symbol or Enumerable.
49
+ # A given string set length was not an Integer or Enumerable.
50
+ #
51
+ def initialize(fields)
52
+ @enumerators = []
53
+
54
+ fields.each do |(set,length)|
55
+ enum = case set
56
+ when String
57
+ [set].each
58
+ when Enumerable
59
+ set.each
60
+ when Symbol
61
+ name = set.upcase
62
+
63
+ unless Chars.const_defined?(name)
64
+ raise(ArgumentError,"unknown charset #{set.inspect}")
65
+ end
66
+
67
+ Chars.const_get(name).each_char
68
+ else
69
+ raise(TypeError,"set must be a String, Symbol or Enumerable")
70
+ end
71
+
72
+ case length
73
+ when Integer
74
+ length.times { @enumerators << enum.dup }
75
+ when Array, Range
76
+ @enumerators << Combinatorics::Generator.new do |g|
77
+ length.each do |sublength|
78
+ superset = Array.new(sublength) { enum.dup }
79
+
80
+ superset.comprehension { |strings| g.yield strings.join }
81
+ end
82
+ end
83
+ when nil
84
+ @enumerators << enum
85
+ else
86
+ raise(TypeError,"length must be an Integer, Range or Array")
87
+ end
88
+ end
89
+ end
90
+
91
+ #
92
+ # @see #initialize
93
+ #
94
+ def self.[](*fields)
95
+ new(fields)
96
+ end
97
+
98
+ #
99
+ # Generate permutations of Strings from a format template.
100
+ #
101
+ # @yield [string]
102
+ # The given block will be passed each unique String.
103
+ #
104
+ # @yieldparam [String] string
105
+ # A newly generated String.
106
+ #
107
+ # @return [Enumerator]
108
+ # If no block is given, an Enumerator will be returned.
109
+ #
110
+ def each
111
+ return enum_for(__method__) unless block_given?
112
+
113
+ @enumerators.comprehension do |fields|
114
+ string = ''
115
+
116
+ fields.each do |field|
117
+ string << case field
118
+ when Integer
119
+ field.chr
120
+ else
121
+ field.to_s
122
+ end
123
+ end
124
+
125
+ yield string
126
+ end
127
+
128
+ return nil
129
+ end
130
+
131
+ end
132
+ end
133
+ end