ronin-fuzzer 0.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +9 -0
- data/Gemfile +39 -0
- data/README.md +91 -0
- data/Rakefile +34 -0
- data/bin/ronin-fuzzer +37 -0
- data/gemspec.yml +30 -0
- data/lib/ronin/fuzzer/cli/command.rb +37 -0
- data/lib/ronin/fuzzer/cli/commands/fuzz.rb +326 -0
- data/lib/ronin/fuzzer/cli.rb +39 -0
- data/lib/ronin/fuzzer/root.rb +29 -0
- data/lib/ronin/fuzzer/version.rb +27 -0
- data/lib/ronin/fuzzing/core_ext/string.rb +203 -0
- data/lib/ronin/fuzzing/core_ext.rb +22 -0
- data/lib/ronin/fuzzing/fuzzer.rb +112 -0
- data/lib/ronin/fuzzing/mutator.rb +163 -0
- data/lib/ronin/fuzzing/repeater.rb +81 -0
- data/lib/ronin/fuzzing/template.rb +133 -0
- data/lib/ronin/fuzzing.rb +365 -0
- data/man/ronin-fuzzer-fuzz.1 +95 -0
- data/man/ronin-fuzzer-fuzz.1.md +73 -0
- data/ronin-fuzzer.gemspec +61 -0
- data/spec/core_ext/string_spec.rb +87 -0
- data/spec/fuzzer_spec.rb +109 -0
- data/spec/fuzzing_spec.rb +24 -0
- data/spec/mutator_spec.rb +112 -0
- data/spec/repeater_spec.rb +57 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/template_spec.rb +54 -0
- metadata +149 -0
@@ -0,0 +1,365 @@
|
|
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/fuzzer'
|
23
|
+
require 'ronin/fuzzing/mutator'
|
24
|
+
require 'ronin/fuzzing/repeater'
|
25
|
+
require 'ronin/fuzzing/core_ext'
|
26
|
+
|
27
|
+
require 'set'
|
28
|
+
|
29
|
+
module Ronin
|
30
|
+
#
|
31
|
+
# Contains class-methods which generate malicious data for fuzzing.
|
32
|
+
#
|
33
|
+
# @see Fuzzing.[]
|
34
|
+
#
|
35
|
+
module Fuzzing
|
36
|
+
# Short String lengths
|
37
|
+
SHORT_LENGTHS = Set[1, 100, 500, 1_000, 10_000]
|
38
|
+
|
39
|
+
# Long String lengths
|
40
|
+
LONG_LENGTHS = Set[
|
41
|
+
128, 255, 256, 257, 511, 512, 513, 1023, 1024, 2048, 2049, 4095,
|
42
|
+
4096, 4097, 5_000, 10_000, 20_000, 32762, 32763, 32764, 32765, 32766,
|
43
|
+
32767, 32768, 32769,
|
44
|
+
0xffff-2, 0xffff-1, 0xffff, 0xffff+1, 0xffff+2,
|
45
|
+
99_999, 100_000, 500_000, 1_000_000
|
46
|
+
]
|
47
|
+
|
48
|
+
# Null bytes in various encodings
|
49
|
+
NULL_BYTES = ['%00', '%u0000', "\x00"]
|
50
|
+
|
51
|
+
# Newline characters
|
52
|
+
NEW_LINES = ["\n", "\r", "\n\r"]
|
53
|
+
|
54
|
+
# Format String flags
|
55
|
+
FORMAT_STRINGS = ['%p', '%s', '%n']
|
56
|
+
|
57
|
+
#
|
58
|
+
# Returns a fuzzer method.
|
59
|
+
#
|
60
|
+
# @param [Symbol] name
|
61
|
+
# The name of the fuzzer to return.
|
62
|
+
#
|
63
|
+
# @return [Enumerator]
|
64
|
+
# An Enumerator for the fuzzer method.
|
65
|
+
#
|
66
|
+
# @raise [NoMethodError]
|
67
|
+
# The fuzzing method could not be found.
|
68
|
+
#
|
69
|
+
# @api semipublic
|
70
|
+
#
|
71
|
+
def self.[](name)
|
72
|
+
if (!respond_to?(name) || Module.respond_to?(name))
|
73
|
+
raise(NoMethodError,"no such fuzzing method: #{name}")
|
74
|
+
end
|
75
|
+
|
76
|
+
return enum_for(name)
|
77
|
+
end
|
78
|
+
|
79
|
+
module_function
|
80
|
+
|
81
|
+
#
|
82
|
+
# Various bad-strings.
|
83
|
+
#
|
84
|
+
# @yield [string]
|
85
|
+
# The given block will be passed each bad-string.
|
86
|
+
#
|
87
|
+
# @yieldparam [String] string
|
88
|
+
# A bad-string containing known control characters, deliminators
|
89
|
+
# or null-bytes (see {NULL_BYTES}), of varying length
|
90
|
+
# (see {SHORT_LENGTHS} and {LONG_LENGTHS}).
|
91
|
+
#
|
92
|
+
def bad_strings(&block)
|
93
|
+
yield ''
|
94
|
+
|
95
|
+
chars = [
|
96
|
+
'A', 'a', '1', '<', '>', '"', "'", '/', "\\", '?', '=', 'a=', '&',
|
97
|
+
'.', ',', '(', ')', ']', '[', '%', '*', '-', '+', '{', '}',
|
98
|
+
"\x14", "\xfe", "\xff"
|
99
|
+
]
|
100
|
+
|
101
|
+
chars.each do |c|
|
102
|
+
LONG_LENGTHS.each { |length| yield c * length }
|
103
|
+
end
|
104
|
+
|
105
|
+
yield '!@#$%%^#$%#$@#$%$$@#$%^^**(()'
|
106
|
+
yield '%01%02%03%04%0a%0d%0aADSF'
|
107
|
+
yield '%01%02%03@%04%0a%0d%0aADSF'
|
108
|
+
|
109
|
+
NULL_BYTES.each do |c|
|
110
|
+
SHORT_LENGTHS.each { |length| yield c * length }
|
111
|
+
end
|
112
|
+
|
113
|
+
yield "%\xfe\xf0%\x00\xff"
|
114
|
+
yield "%\xfe\xf0%\x00\xff" * 20
|
115
|
+
|
116
|
+
SHORT_LENGTHS.each do |length|
|
117
|
+
yield "\xde\xad\xbe\xef" * length
|
118
|
+
end
|
119
|
+
|
120
|
+
yield "\n\r" * 100
|
121
|
+
yield "<>" * 500
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Various format-strings.
|
126
|
+
#
|
127
|
+
# @yield [fmt_string]
|
128
|
+
# The given block will be passed each format-string.
|
129
|
+
#
|
130
|
+
# @yieldparam [String] fmt_string
|
131
|
+
# A format-string containing format operators (see {FORMAT_STRINGS}).
|
132
|
+
#
|
133
|
+
def format_strings(&block)
|
134
|
+
FORMAT_STRINGS.each do |fmt|
|
135
|
+
yield fmt
|
136
|
+
yield fmt * 100
|
137
|
+
yield fmt * 500
|
138
|
+
yield "\"#{fmt}\"" * 500
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Various bad paths and directory traversals.
|
144
|
+
#
|
145
|
+
# @yield [path]
|
146
|
+
# The given block will be passed each path.
|
147
|
+
#
|
148
|
+
# @yieldparam [String] path
|
149
|
+
# A known bad path.
|
150
|
+
#
|
151
|
+
def bad_paths(&block)
|
152
|
+
padding = 'A' * 5_000
|
153
|
+
|
154
|
+
yield "/.:/#{padding}\x00\x00"
|
155
|
+
yield "/.../#{padding}\x00\x00"
|
156
|
+
|
157
|
+
yield "\\\\*"
|
158
|
+
yield "\\\\?\\"
|
159
|
+
|
160
|
+
yield "/\\" * 5_000
|
161
|
+
yield '/.' * 5_000
|
162
|
+
|
163
|
+
NULL_BYTES.each do |c|
|
164
|
+
if c.start_with?('%')
|
165
|
+
yield "#{c}/"
|
166
|
+
yield "/#{c}"
|
167
|
+
yield "/#{c}/"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# The range of bit-fields.
|
174
|
+
#
|
175
|
+
# @yield [bitfield]
|
176
|
+
# The given block will be passed each bit-field.
|
177
|
+
#
|
178
|
+
# @yieldparam [String] bitfield
|
179
|
+
# A bit-field (8bit - 64bit).
|
180
|
+
#
|
181
|
+
def bit_fields(&block)
|
182
|
+
("\x00".."\xff").each do |c|
|
183
|
+
yield c
|
184
|
+
yield c << c # x2
|
185
|
+
yield c << c # x4
|
186
|
+
yield c << c # x8
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
#
|
191
|
+
# The range of signed bit-fields.
|
192
|
+
#
|
193
|
+
# @yield [bitfield]
|
194
|
+
# The given block will be passed each bit-field.
|
195
|
+
#
|
196
|
+
# @yieldparam [String] bitfield
|
197
|
+
# A signed bit-field (8bit - 64bit).
|
198
|
+
#
|
199
|
+
def signed_bit_fields(&block)
|
200
|
+
("\x80".."\xff").each do |c|
|
201
|
+
yield c
|
202
|
+
yield c << c # x2
|
203
|
+
yield c << c # x4
|
204
|
+
yield c << c # x8
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# The range of unsigned 8bit integers.
|
210
|
+
#
|
211
|
+
# @yield [int]
|
212
|
+
# The given block will be passed each integer.
|
213
|
+
#
|
214
|
+
# @yieldparam [String] int
|
215
|
+
# A unsigned 8bit integer.
|
216
|
+
#
|
217
|
+
def uint8(&block)
|
218
|
+
("\x00".."\xff").each(&block)
|
219
|
+
end
|
220
|
+
|
221
|
+
#
|
222
|
+
# The range of unsigned 16bit integers.
|
223
|
+
#
|
224
|
+
# @yield [int]
|
225
|
+
# The given block will be passed each integer.
|
226
|
+
#
|
227
|
+
# @yieldparam [String] int
|
228
|
+
# A unsigned 16bit integer.
|
229
|
+
#
|
230
|
+
def uint16
|
231
|
+
uint8 { |c| yield c * 2 }
|
232
|
+
end
|
233
|
+
|
234
|
+
#
|
235
|
+
# The range of unsigned 32bit integers.
|
236
|
+
#
|
237
|
+
# @yield [int]
|
238
|
+
# The given block will be passed each integer.
|
239
|
+
#
|
240
|
+
# @yieldparam [String] int
|
241
|
+
# A unsigned 32bit integer.
|
242
|
+
#
|
243
|
+
def uint32
|
244
|
+
uint8 { |c| yield c * 4 }
|
245
|
+
end
|
246
|
+
|
247
|
+
#
|
248
|
+
# The range of unsigned 64bit integers.
|
249
|
+
#
|
250
|
+
# @yield [int]
|
251
|
+
# The given block will be passed each integer.
|
252
|
+
#
|
253
|
+
# @yieldparam [String] int
|
254
|
+
# A unsigned 64bit integer.
|
255
|
+
#
|
256
|
+
def uint64
|
257
|
+
uint8 { |c| yield c * 8 }
|
258
|
+
end
|
259
|
+
|
260
|
+
#
|
261
|
+
# The range of signed 8bit integers.
|
262
|
+
#
|
263
|
+
# @yield [int]
|
264
|
+
# The given block will be passed each integer.
|
265
|
+
#
|
266
|
+
# @yieldparam [String] int
|
267
|
+
# A signed 8bit integer.
|
268
|
+
#
|
269
|
+
def int8(&block)
|
270
|
+
("\x00".."\x70").each(&block)
|
271
|
+
end
|
272
|
+
|
273
|
+
#
|
274
|
+
# The range of signed 16bit integers.
|
275
|
+
#
|
276
|
+
# @yield [int]
|
277
|
+
# The given block will be passed each integer.
|
278
|
+
#
|
279
|
+
# @yieldparam [String] int
|
280
|
+
# A signed 16bit integer.
|
281
|
+
#
|
282
|
+
def int16
|
283
|
+
int8 { |c| yield c * 2 }
|
284
|
+
end
|
285
|
+
|
286
|
+
#
|
287
|
+
# The range of signed 32bit integers.
|
288
|
+
#
|
289
|
+
# @yield [int]
|
290
|
+
# The given block will be passed each integer.
|
291
|
+
#
|
292
|
+
# @yieldparam [String] int
|
293
|
+
# A signed 32bit integer.
|
294
|
+
#
|
295
|
+
def int32
|
296
|
+
int8 { |c| yield c * 4 }
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# The range of signed 64bit integers.
|
301
|
+
#
|
302
|
+
# @yield [int]
|
303
|
+
# The given block will be passed each integer.
|
304
|
+
#
|
305
|
+
# @yieldparam [String] int
|
306
|
+
# A signed 64bit integer.
|
307
|
+
#
|
308
|
+
def int64
|
309
|
+
int8 { |c| yield c * 8 }
|
310
|
+
end
|
311
|
+
|
312
|
+
#
|
313
|
+
# The range of negative-signed 8bit integers.
|
314
|
+
#
|
315
|
+
# @yield [int]
|
316
|
+
# The given block will be passed each integer.
|
317
|
+
#
|
318
|
+
# @yieldparam [String] int
|
319
|
+
# A negative-signed 8bit integer.
|
320
|
+
#
|
321
|
+
def sint8(&block)
|
322
|
+
("\x80".."\xff").each(&block)
|
323
|
+
end
|
324
|
+
|
325
|
+
#
|
326
|
+
# The range of negative-signed 16bit integers.
|
327
|
+
#
|
328
|
+
# @yield [int]
|
329
|
+
# The given block will be passed each integer.
|
330
|
+
#
|
331
|
+
# @yieldparam [String] int
|
332
|
+
# A negative-signed 16bit integer.
|
333
|
+
#
|
334
|
+
def sint16
|
335
|
+
sint8 { |c| yield c * 2 }
|
336
|
+
end
|
337
|
+
|
338
|
+
#
|
339
|
+
# The range of negative-signed 32bit integers.
|
340
|
+
#
|
341
|
+
# @yield [int]
|
342
|
+
# The given block will be passed each integer.
|
343
|
+
#
|
344
|
+
# @yieldparam [String] int
|
345
|
+
# A negative-signed 32bit integer.
|
346
|
+
#
|
347
|
+
def sint32
|
348
|
+
sint8 { |c| yield c * 4 }
|
349
|
+
end
|
350
|
+
|
351
|
+
#
|
352
|
+
# The range of negative-signed 64bit integers.
|
353
|
+
#
|
354
|
+
# @yield [int]
|
355
|
+
# The given block will be passed each integer.
|
356
|
+
#
|
357
|
+
# @yieldparam [String] int
|
358
|
+
# A negative-signed 64bit integer.
|
359
|
+
#
|
360
|
+
def sint64
|
361
|
+
sint8 { |c| yield c * 8 }
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|
365
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
.\" Generated by kramdown-man 0.1.8
|
2
|
+
.\" https://github.com/postmodern/kramdown-man#readme
|
3
|
+
.TH ronin-fuzzer-fuzz 1 "2022-01-01" Ronin Fuzzer "User Manuals"
|
4
|
+
.LP
|
5
|
+
.SH SYNOPSIS
|
6
|
+
.LP
|
7
|
+
.HP
|
8
|
+
\fBronin-fuzzer fuzz\fR \[lB]\fIoptions\fP\[rB] \[lB]\fITEMPLATE\fP\[rB]
|
9
|
+
.LP
|
10
|
+
.SH DESCRIPTION
|
11
|
+
.LP
|
12
|
+
.PP
|
13
|
+
Fuzzes data read from a \fIFILE\fP or from \fBSTDIN\fR\. The fuzzed data can be written
|
14
|
+
to output files, run in commands or sent to TCP\[sl]UDP services\.
|
15
|
+
.LP
|
16
|
+
.SH OPTIONS
|
17
|
+
.LP
|
18
|
+
.TP
|
19
|
+
\fB-v\fR, \fB--[no-]verbose\fR
|
20
|
+
Enable verbose output\.
|
21
|
+
.LP
|
22
|
+
.TP
|
23
|
+
\fB-q\fR, \fB--[no-]quiet\fR
|
24
|
+
Disable verbose output\.
|
25
|
+
.LP
|
26
|
+
.TP
|
27
|
+
\fB--[no-]silent\fR
|
28
|
+
Silence all output\.
|
29
|
+
.LP
|
30
|
+
.TP
|
31
|
+
\fB-i\fR, \fB--input\fR \fIFILE\fP
|
32
|
+
The input text FILE to parse\. Data will be read from \fBSTDIN\fR by default\.
|
33
|
+
.LP
|
34
|
+
.HP
|
35
|
+
\fB-r\fR, \fB--rule\fR \[lB]\fIPATTERN\fP\[or]\fI\[sl]REGEXP\[sl]\fP\[or]STRING\[rB]:\[lB]\fIMETHOD\fP\[or]\fISTRING\fP\fI*N\fP\[lB]\-\fIM\fP\[rB]\[rB]
|
36
|
+
The rule to apply to the \fIINPUT\fP\. Fuzzer rules consist of a pattern and
|
37
|
+
substitution\. Patterns may be one of the following:
|
38
|
+
.LP
|
39
|
+
.nf
|
40
|
+
* A name of a Ronin Regular Expression (ex: \`unix\[ru]path\`)
|
41
|
+
* A custom Regular Expression (ex: \`\[sl]\ed\[pl]\[sl]\`)
|
42
|
+
* A plain String (ex: \`example\.com\`)\.
|
43
|
+
|
44
|
+
Substitutions may be one of the following:
|
45
|
+
|
46
|
+
* A method from \`Ronin::Fuzzer\` (ex: \`bad\[ru]strings\`)
|
47
|
+
* A *STRING*, repeated *N* or *M* times (ex: \`A*100\-200\`)\.
|
48
|
+
.fi
|
49
|
+
.LP
|
50
|
+
.TP
|
51
|
+
\fB-o\fR, \fB--output\fR \fIPATH\fP
|
52
|
+
The output PATH to write the fuzzer to\.
|
53
|
+
.LP
|
54
|
+
.TP
|
55
|
+
\fB-c\fR, \fB--command\fR \fICOMMAND\fP
|
56
|
+
The command to run with the fuzzed data\. All ocurrences of \fB#string#\fR
|
57
|
+
will be replaced with the fuzzed data, and ocurrences of \fB#path#\fR will
|
58
|
+
be replaced with the path to the fuzzed data\.
|
59
|
+
.LP
|
60
|
+
.TP
|
61
|
+
\fB-t\fR, \fB--tcp\fR \fIHOST\fP:\fIPORT\fP
|
62
|
+
The TCP service to send the fuzzed data to\.
|
63
|
+
.LP
|
64
|
+
.TP
|
65
|
+
\fB-u\fR, \fB--udp\fR \fIHOST\fP:\fIPORT\fP
|
66
|
+
The UDP service to send the fuzzed data to\.
|
67
|
+
.LP
|
68
|
+
.TP
|
69
|
+
\fB-p\fR, \fB--pause\fR \fISECONDS\fP
|
70
|
+
Pause in between mutations\.
|
71
|
+
.LP
|
72
|
+
.SH EXAMPLES
|
73
|
+
.LP
|
74
|
+
.TP
|
75
|
+
\fBronin-fuzzer fuzz -i http_request.txt -o bad.txt -r unix_path:bad_strings\fR
|
76
|
+
Fuzzes a HTTP request, replacing every occurrence of a UNIX path, with
|
77
|
+
strings from the \fBbad_strings\fR method\.
|
78
|
+
.LP
|
79
|
+
.SH LINKS
|
80
|
+
.LP
|
81
|
+
.PP
|
82
|
+
Ronin Regular Expressions
|
83
|
+
https:\[sl]\[sl]ronin\-rb\.dev\[sl]docs\[sl]ronin\-support\[sl]Regexp\.html
|
84
|
+
.LP
|
85
|
+
.TP
|
86
|
+
\fBRonin::Fuzzer\fR
|
87
|
+
https:\[sl]\[sl]ronin\-rb\.dev\[sl]docs\[sl]ronin\-fuzzer\[sl]Ronin\[sl]Fuzzer\.html
|
88
|
+
.LP
|
89
|
+
.SH AUTHOR
|
90
|
+
.LP
|
91
|
+
.PP
|
92
|
+
Postmodern
|
93
|
+
.MT postmodern\.mod3\[at]gmail\.com
|
94
|
+
.ME
|
95
|
+
.LP
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# ronin-fuzzer-fuzz 1 "2022-01-01" Ronin Fuzzer "User Manuals"
|
2
|
+
|
3
|
+
## SYNOPSIS
|
4
|
+
|
5
|
+
`ronin-fuzzer fuzz` [*options*] [*TEMPLATE*]
|
6
|
+
|
7
|
+
## DESCRIPTION
|
8
|
+
|
9
|
+
Fuzzes data read from a *FILE* or from `STDIN`. The fuzzed data can be written
|
10
|
+
to output files, run in commands or sent to TCP/UDP services.
|
11
|
+
|
12
|
+
## OPTIONS
|
13
|
+
|
14
|
+
`-v`, `--[no-]verbose`
|
15
|
+
Enable verbose output.
|
16
|
+
|
17
|
+
`-q`, `--[no-]quiet`
|
18
|
+
Disable verbose output.
|
19
|
+
|
20
|
+
`--[no-]silent`
|
21
|
+
Silence all output.
|
22
|
+
|
23
|
+
`-i`, `--input` *FILE*
|
24
|
+
The input text FILE to parse. Data will be read from `STDIN` by default.
|
25
|
+
|
26
|
+
`-r`, `--rule` [*PATTERN*|*/REGEXP/*|STRING]:[*METHOD*|*STRING***N*[-*M*]]
|
27
|
+
The rule to apply to the *INPUT*. Fuzzer rules consist of a pattern and
|
28
|
+
substitution. Patterns may be one of the following:
|
29
|
+
|
30
|
+
* A name of a Ronin Regular Expression (ex: `unix_path`)
|
31
|
+
* A custom Regular Expression (ex: `/\d+/`)
|
32
|
+
* A plain String (ex: `example.com`).
|
33
|
+
|
34
|
+
Substitutions may be one of the following:
|
35
|
+
|
36
|
+
* A method from `Ronin::Fuzzer` (ex: `bad_strings`)
|
37
|
+
* A *STRING*, repeated *N* or *M* times (ex: `A*100-200`).
|
38
|
+
|
39
|
+
`-o`, `--output` *PATH*
|
40
|
+
The output PATH to write the fuzzer to.
|
41
|
+
|
42
|
+
`-c`, `--command` *COMMAND*
|
43
|
+
The command to run with the fuzzed data. All ocurrences of `#string#`
|
44
|
+
will be replaced with the fuzzed data, and ocurrences of `#path#` will
|
45
|
+
be replaced with the path to the fuzzed data.
|
46
|
+
|
47
|
+
`-t`, `--tcp` *HOST*:*PORT*
|
48
|
+
The TCP service to send the fuzzed data to.
|
49
|
+
|
50
|
+
`-u`, `--udp` *HOST*:*PORT*
|
51
|
+
The UDP service to send the fuzzed data to.
|
52
|
+
|
53
|
+
`-p`, `--pause` *SECONDS*
|
54
|
+
Pause in between mutations.
|
55
|
+
|
56
|
+
## EXAMPLES
|
57
|
+
|
58
|
+
`ronin-fuzzer fuzz -i http_request.txt -o bad.txt -r unix_path:bad_strings`
|
59
|
+
Fuzzes a HTTP request, replacing every occurrence of a UNIX path, with
|
60
|
+
strings from the `bad_strings` method.
|
61
|
+
|
62
|
+
## LINKS
|
63
|
+
|
64
|
+
Ronin Regular Expressions
|
65
|
+
https://ronin-rb.dev/docs/ronin-support/Regexp.html
|
66
|
+
|
67
|
+
`Ronin::Fuzzer`
|
68
|
+
https://ronin-rb.dev/docs/ronin-fuzzer/Ronin/Fuzzer.html
|
69
|
+
|
70
|
+
## AUTHOR
|
71
|
+
|
72
|
+
Postmodern <postmodern.mod3@gmail.com>
|
73
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'ronin/fuzzer/version'
|
14
|
+
Ronin::Fuzzer::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
|
+
|
25
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
26
|
+
|
27
|
+
gem.files = `git ls-files`.split($/)
|
28
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
29
|
+
gem.files += Array(gemspec['generated_files'])
|
30
|
+
|
31
|
+
gem.executables = gemspec.fetch('executables') do
|
32
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
33
|
+
end
|
34
|
+
|
35
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
36
|
+
gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
|
37
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
38
|
+
|
39
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
40
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
41
|
+
})
|
42
|
+
|
43
|
+
gem.requirements = gemspec['requirements']
|
44
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
45
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
46
|
+
gem.post_install_message = gemspec['post_install_message']
|
47
|
+
|
48
|
+
split = lambda { |string| string.split(/,\s*/) }
|
49
|
+
|
50
|
+
if gemspec['dependencies']
|
51
|
+
gemspec['dependencies'].each do |name,versions|
|
52
|
+
gem.add_dependency(name,split[versions])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if gemspec['development_dependencies']
|
57
|
+
gemspec['development_dependencies'].each do |name,versions|
|
58
|
+
gem.add_development_dependency(name,split[versions])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/fuzzing/core_ext/string'
|
3
|
+
|
4
|
+
describe String do
|
5
|
+
it "should provide String.generate" do
|
6
|
+
expect(described_class).to respond_to(:generate)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should provide String#repeating" do
|
10
|
+
expect(subject).to respond_to(:repeating)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should provide String#fuzz" do
|
14
|
+
expect(subject).to respond_to(:fuzz)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should provide String#mutate" do
|
18
|
+
expect(subject).to respond_to(:mutate)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "generate" do
|
22
|
+
subject { described_class }
|
23
|
+
|
24
|
+
it "should generate Strings from a template" do
|
25
|
+
strings = subject.generate([:numeric, 2]).to_a
|
26
|
+
|
27
|
+
expect(strings.grep(/^[0-9]{2}$/)).to eq(strings)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#repeating" do
|
32
|
+
subject { 'A' }
|
33
|
+
|
34
|
+
context "when n is an Integer" do
|
35
|
+
let(:n) { 100 }
|
36
|
+
|
37
|
+
it "should multiply the String by n" do
|
38
|
+
expect(subject.repeating(n)).to eq(subject * n)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when n is Enumerable" do
|
43
|
+
let(:n) { [128, 512, 1024] }
|
44
|
+
|
45
|
+
it "should repeat the String by each length" do
|
46
|
+
strings = subject.repeating(n).to_a
|
47
|
+
|
48
|
+
expect(strings).to eq(n.map { |length| subject * length })
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#fuzz" do
|
54
|
+
subject { "foo bar" }
|
55
|
+
|
56
|
+
it "should apply each fuzzing rule individually" do
|
57
|
+
strings = subject.fuzz(/o/ => ['O', '0'], /a/ => ['A', '@']).to_a
|
58
|
+
|
59
|
+
expect(strings).to match_array([
|
60
|
+
"fOo bar",
|
61
|
+
"f0o bar",
|
62
|
+
"foO bar",
|
63
|
+
"fo0 bar",
|
64
|
+
"foo bAr",
|
65
|
+
"foo b@r"
|
66
|
+
])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#mutate" do
|
71
|
+
subject { "foo bar" }
|
72
|
+
|
73
|
+
it "should apply every combination of mutation rules" do
|
74
|
+
strings = subject.mutate(/o/ => ['0'], /a/ => ['@']).to_a
|
75
|
+
|
76
|
+
expect(strings).to match_array([
|
77
|
+
"f0o bar",
|
78
|
+
"fo0 bar",
|
79
|
+
"f00 bar",
|
80
|
+
"foo b@r",
|
81
|
+
"f0o b@r",
|
82
|
+
"fo0 b@r",
|
83
|
+
"f00 b@r"
|
84
|
+
])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|