ronin-support 0.5.0.rc1 → 0.5.0.rc2

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.
@@ -6,6 +6,10 @@
6
6
  * Added {Ronin::Binary::Template}.
7
7
  * Added {Ronin::Binary::Struct}.
8
8
  * Added {Ronin::Binary::Hexdump::Parser}.
9
+ * Added {Ronin::Fuzzing::Template}.
10
+ * Added {Ronin::Fuzzing::Repeater}.
11
+ * Added {Ronin::Fuzzing::Fuzzer}.
12
+ * Added {Ronin::Fuzzing::Mutator}.
9
13
  * Added {Ronin::Wordlist.create}.
10
14
  * Added {Ronin::Wordlist#path} and {Ronin::Wordlist#words}.
11
15
  * Added {Ronin::Wordlist#save}.
@@ -44,6 +48,7 @@
44
48
  * Support unhexdumping floats / doubles.
45
49
  * Allow {String#mutate} to accept Symbols that map to {Ronin::Fuzzing}
46
50
  generator methods.
51
+ * {Ronin::Fuzzing.[]} now raises a `NoMethodError` for unknown fuzzing methods.
47
52
  * Use `module_function` in {Ronin::Fuzzing}, so the generator methods can be
48
53
  included into other Classes/Modules.
49
54
  * Require uri-query_params ~> 0.6.
@@ -127,7 +132,7 @@
127
132
  was not being escaped.
128
133
  * Allow {Ronin::Network::HTTP.request} to accept `:query` and `:query_params`
129
134
  options.
130
- * Fixed a bug in {Ronin::Network::Mixins::HTTP#http_session}, where
135
+ * Fixed a bug in `Ronin::Network::Mixins::HTTP#http_session`, where
131
136
  normalized options were not being yielded.
132
137
  * {Ronin::Network::HTTP#http_get_headers} and
133
138
  {Ronin::Network::HTTP#http_post_headers} now return a Hash of Capitalized
@@ -17,21 +17,20 @@
17
17
  # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
18
  #
19
19
 
20
- require 'ronin/extensions/regexp'
20
+ require 'ronin/fuzzing/template'
21
+ require 'ronin/fuzzing/repeater'
22
+ require 'ronin/fuzzing/fuzzer'
23
+ require 'ronin/fuzzing/mutator'
21
24
  require 'ronin/fuzzing/fuzzing'
22
-
23
- require 'combinatorics/generator'
24
- require 'combinatorics/list_comprehension'
25
- require 'combinatorics/power_set'
26
- require 'chars'
25
+ require 'ronin/extensions/regexp'
27
26
 
28
27
  class String
29
28
 
30
29
  #
31
30
  # Generate permutations of Strings from a format template.
32
31
  #
33
- # @param [Array(<String,Symbol,Enumerable>, <Integer,Array,Range>)] template
34
- # The template which defines the string or character sets which will
32
+ # @param [Array(<String,Symbol,Enumerable>, <Integer,Array,Range>)] fields
33
+ # The fields which defines the string or character sets which will
35
34
  # make up parts of the String.
36
35
  #
37
36
  # @yield [string]
@@ -86,69 +85,14 @@ class String
86
85
  #
87
86
  # @api public
88
87
  #
89
- def self.generate(*template)
90
- return enum_for(:generate,*template) unless block_given?
91
-
92
- sets = []
93
-
94
- template.each do |pattern|
95
- set, length = pattern
96
- set = case set
97
- when String
98
- [set].each
99
- when Symbol
100
- name = set.to_s.upcase
101
-
102
- unless Chars.const_defined?(name)
103
- raise(ArgumentError,"unknown charset #{set.inspect}")
104
- end
105
-
106
- Chars.const_get(name).each_char
107
- when Enumerable
108
- set
109
- else
110
- raise(TypeError,"set must be a String, Symbol or Enumerable")
111
- end
112
-
113
- case length
114
- when Integer
115
- length.times { sets << set.dup }
116
- when Array, Range
117
- sets << Combinatorics::Generator.new do |g|
118
- length.each do |sublength|
119
- superset = Array.new(sublength) { set.dup }
120
-
121
- superset.comprehension { |strings| g.yield strings.join }
122
- end
123
- end
124
- when nil
125
- sets << set
126
- else
127
- raise(TypeError,"length must be an Integer, Range or Array")
128
- end
129
- end
130
-
131
- sets.comprehension do |strings|
132
- new_string = ''
133
-
134
- strings.each do |string|
135
- new_string << case string
136
- when Integer
137
- string.chr
138
- else
139
- string.to_s
140
- end
141
- end
142
-
143
- yield new_string
144
- end
145
- return nil
88
+ def self.generate(*fields,&block)
89
+ Ronin::Fuzzing::Template.new(fields).each(&block)
146
90
  end
147
91
 
148
92
  #
149
93
  # Repeats the String.
150
94
  #
151
- # @param [Enumerable, Integer] n
95
+ # @param [Enumerable, Integer] lengths
152
96
  # The number of times to repeat the String.
153
97
  #
154
98
  # @yield [repeated]
@@ -181,26 +125,17 @@ class String
181
125
  #
182
126
  # @since 0.4.0
183
127
  #
184
- def repeating(n)
185
- if n.kind_of?(Integer)
186
- # if n is an Integer, simply multiply the String and return
187
- repeated = (self * n)
128
+ def repeating(lengths,&block)
129
+ case lengths
130
+ when Integer
131
+ # if lengths is an Integer, simply multiply the String and return
132
+ repeated = (self * lengths)
188
133
 
189
134
  yield repeated if block_given?
190
135
  return repeated
136
+ else
137
+ return Ronin::Fuzzing::Repeater.new(lengths).each(self,&block)
191
138
  end
192
-
193
- return enum_for(:repeating,n) unless block_given?
194
-
195
- unless n.kind_of?(Enumerable)
196
- raise(TypeError,"argument must be Enumerable or an Integer")
197
- end
198
-
199
- n.each do |length|
200
- yield(self * length)
201
- end
202
-
203
- return self
204
139
  end
205
140
 
206
141
  #
@@ -232,54 +167,8 @@ class String
232
167
  #
233
168
  # @api public
234
169
  #
235
- def fuzz(substitutions={})
236
- return enum_for(:fuzz,substitutions) unless block_given?
237
-
238
- substitutions.each do |pattern,substitution|
239
- pattern = case pattern
240
- when Regexp
241
- pattern
242
- when String
243
- Regexp.new(Regexp.escape(pattern))
244
- when Symbol
245
- Regexp.const_get(pattern.to_s.upcase)
246
- else
247
- raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
248
- end
249
-
250
- substitution = case substitution
251
- when Enumerable
252
- substitution
253
- when Symbol
254
- Ronin::Fuzzing[substitution]
255
- else
256
- raise(TypeError,"substitutions must be Enumerable or a Symbol")
257
- end
258
-
259
- scanner = StringScanner.new(self)
260
- indices = []
261
-
262
- while scanner.scan_until(pattern)
263
- indices << [scanner.pos - scanner.matched_size, scanner.matched_size]
264
- end
265
-
266
- indices.each do |index,length|
267
- substitution.each do |substitute|
268
- substitute = case substitute
269
- when Proc
270
- substitute.call(self[index,length])
271
- when Integer
272
- substitute.chr
273
- else
274
- substitute.to_s
275
- end
276
-
277
- fuzzed = dup
278
- fuzzed[index,length] = substitute
279
- yield fuzzed
280
- end
281
- end
282
- end
170
+ def fuzz(substitutions={},&block)
171
+ Ronin::Fuzzing::Fuzzer.new(substitutions).each(self,&block)
283
172
  end
284
173
 
285
174
  #
@@ -310,84 +199,8 @@ class String
310
199
  #
311
200
  # @api public
312
201
  #
313
- def mutate(mutations={})
314
- return enum_for(:mutate,mutations) unless block_given?
315
-
316
- matches = Set[]
317
-
318
- mutations.each do |pattern,mutation|
319
- pattern = case pattern
320
- when Regexp
321
- pattern
322
- when String
323
- Regexp.new(Regexp.escape(pattern))
324
- when Symbol
325
- Regexp.const_get(pattern.to_s.upcase)
326
- else
327
- raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
328
- end
329
-
330
- mutation = case mutation
331
- when Symbol
332
- Ronin::Fuzzing[mutation]
333
- when Enumerable
334
- mutation
335
- else
336
- raise(TypeError,"mutation #{mutation.inspect} must be a Symbol or Enumerable")
337
- end
338
-
339
- scanner = StringScanner.new(self)
340
-
341
- while scanner.scan_until(pattern)
342
- length = scanner.matched_size
343
- index = scanner.pos - length
344
- original = scanner.matched
345
-
346
- mutator = Combinatorics::Generator.new do |g|
347
- mutation.each do |mutate|
348
- g.yield case mutate
349
- when Proc
350
- mutate.call(original)
351
- when Integer
352
- mutate.chr
353
- else
354
- mutate.to_s
355
- end
356
- end
357
- end
358
-
359
- matches << [index, length, mutator]
360
- end
361
- end
362
-
363
- matches.powerset do |submatches|
364
- # ignore the empty Set
365
- next if submatches.empty?
366
-
367
- # sort the submatches by index
368
- submatches = submatches.sort_by { |index,length,mutator| index }
369
- sets = []
370
- prev_index = 0
371
-
372
- submatches.each do |index,length,mutator|
373
- # add the previous substring to the set of Strings
374
- if index > prev_index
375
- sets << [self[prev_index,index - prev_index]]
376
- end
377
-
378
- # add the mutator to the set of Strings
379
- sets << mutator
380
-
381
- prev_index = index + length
382
- end
383
-
384
- # add the remaining substring to the set of Strings
385
- if prev_index < self.length
386
- sets << [self[prev_index..-1]]
387
- end
388
-
389
- sets.comprehension { |strings| yield strings.join }
390
- end
202
+ def mutate(mutations={},&block)
203
+ Ronin::Fuzzing::Mutator.new(mutations).each(self,&block)
391
204
  end
392
205
 
393
206
  end
@@ -0,0 +1,118 @@
1
+ #
2
+ # Copyright (c) 2006-2012 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/fuzzing/fuzzing'
21
+ require 'ronin/extensions/regexp'
22
+
23
+ require 'strscan'
24
+
25
+ module Ronin
26
+ module Fuzzing
27
+ #
28
+ # Fuzzing class that incrementally fuzzes a String, given substitution
29
+ # rules.
30
+ #
31
+ # @api semipublic
32
+ #
33
+ # @since 0.5.0
34
+ #
35
+ class Fuzzer
36
+
37
+ # Patterns and their substitutions
38
+ attr_reader :rules
39
+
40
+ #
41
+ # Initializes a new Fuzzer.
42
+ #
43
+ # @param [Hash{Regexp,String => #each}] rules
44
+ # Patterns and their substitutions.
45
+ #
46
+ def initialize(rules)
47
+ @rules = {}
48
+
49
+ rules.each do |pattern,substitution|
50
+ pattern = case pattern
51
+ when Regexp
52
+ pattern
53
+ when String
54
+ Regexp.new(Regexp.escape(pattern))
55
+ when Symbol
56
+ Regexp.const_get(pattern.to_s.upcase)
57
+ else
58
+ raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
59
+ end
60
+
61
+ substitution = case substitution
62
+ when Enumerable
63
+ substitution
64
+ when Symbol
65
+ Fuzzing[substitution]
66
+ else
67
+ raise(TypeError,"substitutions must be Enumerable or a Symbol")
68
+ end
69
+
70
+ @rules[pattern] = substitution
71
+ end
72
+ end
73
+
74
+ #
75
+ # Incrementally fuzzes the String.
76
+ #
77
+ # @yield [fuzz]
78
+ # The given block will be passed every fuzzed String.
79
+ #
80
+ # @yieldparam [String] fuzz
81
+ # A fuzzed String.
82
+ #
83
+ # @return [Enumerator]
84
+ # If no block is given, an Enumerator will be returned.
85
+ #
86
+ def each(string)
87
+ return enum_for(__method__,string) unless block_given?
88
+
89
+ @rules.each do |pattern,substitution|
90
+ scanner = StringScanner.new(string)
91
+ indices = []
92
+
93
+ while scanner.scan_until(pattern)
94
+ indices << [scanner.pos - scanner.matched_size, scanner.matched_size]
95
+ end
96
+
97
+ indices.each do |index,length|
98
+ substitution.each do |substitute|
99
+ substitute = case substitute
100
+ when Proc
101
+ substitute.call(string[index,length])
102
+ when Integer
103
+ substitute.chr
104
+ else
105
+ substitute.to_s
106
+ end
107
+
108
+ fuzzed = string.dup
109
+ fuzzed[index,length] = substitute
110
+ yield fuzzed
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -58,12 +58,17 @@ module Ronin
58
58
  # @return [Enumerator]
59
59
  # An Enumerator for the fuzzer method.
60
60
  #
61
+ # @raise [NoMethodError]
62
+ # The fuzzing method could not be found.
63
+ #
61
64
  # @api semipublic
62
65
  #
63
66
  def self.[](name)
64
- if (!Object.respond_to?(name) && respond_to?(name))
65
- enum_for(name)
67
+ if (!respond_to?(name) || Module.respond_to?(name))
68
+ raise(NoMethodError,"no such fuzzing method: #{name}")
66
69
  end
70
+
71
+ return enum_for(name)
67
72
  end
68
73
 
69
74
  module_function
@@ -0,0 +1,161 @@
1
+ #
2
+ # Copyright (c) 2006-2012 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/fuzzing/fuzzing'
21
+ require 'ronin/extensions/regexp'
22
+
23
+ require 'combinatorics/list_comprehension'
24
+ require 'combinatorics/power_set'
25
+ require 'combinatorics/generator'
26
+ require 'strscan'
27
+ require 'set'
28
+
29
+ module Ronin
30
+ module Fuzzing
31
+ #
32
+ # Fuzzer class that permutates over every mutation of a String, given
33
+ # mutation rules.
34
+ #
35
+ # @api semipublic
36
+ #
37
+ # @since 0.5.0
38
+ #
39
+ class Mutator
40
+
41
+ # Mutation rules
42
+ attr_reader :rules
43
+
44
+ #
45
+ # Initialize the Mutator.
46
+ #
47
+ # @param [Hash{Regexp,String,Symbol => Symbol,Enumerable}] rules
48
+ # The patterns and substitutions to mutate the String with.
49
+ #
50
+ # @raise [TypeError]
51
+ # A mutation pattern was not a Regexp, String or Symbol.
52
+ # A mutation substitution was not a Symbol or Enumerable.
53
+ #
54
+ def initialize(rules)
55
+ @rules = {}
56
+
57
+ rules.each do |pattern,mutation|
58
+ pattern = case pattern
59
+ when Regexp
60
+ pattern
61
+ when String
62
+ Regexp.new(Regexp.escape(pattern))
63
+ when Symbol
64
+ Regexp.const_get(pattern.to_s.upcase)
65
+ else
66
+ raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
67
+ end
68
+
69
+ mutation = case mutation
70
+ when Enumerable
71
+ mutation
72
+ when Symbol
73
+ Ronin::Fuzzing[mutation]
74
+ else
75
+ raise(TypeError,"mutation #{mutation.inspect} must be a Symbol or Enumerable")
76
+ end
77
+
78
+ @rules[pattern] = mutation
79
+ end
80
+ end
81
+
82
+ #
83
+ # Permutes over every possible mutation of the String.
84
+ #
85
+ # @param [String] string
86
+ # The String to be mutated.
87
+ #
88
+ # @yield [mutant]
89
+ # The given block will be yielded every possible mutant String.
90
+ #
91
+ # @yieldparam [String] mutant
92
+ # A mutated String.
93
+ #
94
+ # @return [Enumerator]
95
+ # If no block is given, an Enumerator will be returned.
96
+ #
97
+ def each(string)
98
+ return enum_for(__method__,string) unless block_given?
99
+
100
+ matches = Set[]
101
+
102
+ @rules.each do |pattern,mutation|
103
+ scanner = StringScanner.new(string)
104
+
105
+ while scanner.scan_until(pattern)
106
+ length = scanner.matched_size
107
+ index = scanner.pos - length
108
+ original = scanner.matched
109
+
110
+ mutator = Combinatorics::Generator.new do |g|
111
+ mutation.each do |mutate|
112
+ g.yield case mutate
113
+ when Proc
114
+ mutate.call(original)
115
+ when Integer
116
+ mutate.chr
117
+ else
118
+ mutate.to_s
119
+ end
120
+ end
121
+ end
122
+
123
+ matches << [index, length, mutator]
124
+ end
125
+ end
126
+
127
+ matches.powerset do |submatches|
128
+ # ignore the empty Set
129
+ next if submatches.empty?
130
+
131
+ # sort the submatches by index
132
+ submatches = submatches.sort_by { |index,length,mutator| index }
133
+ sets = []
134
+ prev_index = 0
135
+
136
+ submatches.each do |index,length,mutator|
137
+ # add the previous substring to the set of Strings
138
+ if index > prev_index
139
+ sets << [string[prev_index,index - prev_index]]
140
+ end
141
+
142
+ # add the mutator to the set of Strings
143
+ sets << mutator
144
+
145
+ prev_index = index + length
146
+ end
147
+
148
+ # add the remaining substring to the set of Strings
149
+ if prev_index < string.length
150
+ sets << [string[prev_index..-1]]
151
+ end
152
+
153
+ sets.comprehension { |strings| yield strings.join }
154
+ end
155
+
156
+ return nil
157
+ end
158
+
159
+ end
160
+ end
161
+ end