ronin-fuzzer 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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