ronin-support 0.5.0.rc1 → 0.5.0.rc2

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