Class: Ronin::Fuzzer::CLI::Commands::Fuzz

Inherits:
Ronin::Fuzzer::CLI::Command show all
Includes:
Core::CLI::Logging
Defined in:
lib/ronin/fuzzer/cli/commands/fuzz.rb

Overview

Performs basic fuzzing of files, commands or TCP/UDP services.

Usage

ronin-fuzzer fuzz [options]

Options

-v, --[no-]verbose               Enable verbose output.
-q, --[no-]quiet                 Disable verbose output.
    --[no-]silent                Silence all output.
-r [[PATTERN|/REGEXP/]:[METHOD|STRING*N[-M]]],
    --rule                       Adds a fuzzing rule.
-i, --input [FILE]               Input file to fuzz.
-o, --output [FILE]              Output file path.
-c [PROGRAM [OPTIONS|#string#|#path#] ...],
    --command                    Template command to run.
-t, --tcp [HOST:PORT]            TCP service to fuzz.
-u, --udp [HOST:PORT]            UDP service to fuzz.
-p, --pause [SECONDS]            Pause in between mutations.

Examples

ronin-fuzzer fuzz -i request.txt -r unix_path:bad_strings -o bad.txt

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**kwargs) ⇒ Fuzz

Initializes the ronin-fuzzer fuzz command.

Parameters:

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments.



192
193
194
195
196
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 192

def initialize(**kwargs)
  super(**kwargs)

  @rules = []
end

Instance Attribute Details

#commandString? (readonly)

The command template to execute.

Returns:



169
170
171
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 169

def command
  @command
end

#hostString? (readonly)

The host name to send fuzzing data to.

Returns:



174
175
176
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 174

def host
  @host
end

#mode:output, ... (readonly)

The execution mode to run the fuzzer in.

Returns:

  • (:output, :command, :tcp, :udp)


149
150
151
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 149

def mode
  @mode
end

#outputString? (readonly)

The output file template.

Returns:



154
155
156
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 154

def output
  @output
end

#output_extString? (readonly)

The output file extension.

Returns:



159
160
161
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 159

def output_ext
  @output_ext
end

#output_nameString? (readonly)

The output file name.

Returns:



164
165
166
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 164

def output_name
  @output_name
end

#portInteger? (readonly)

The port to send fuzzing data to.

Returns:

  • (Integer, nil)


179
180
181
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 179

def port
  @port
end

#rulesArray<(Regexp, Enumerator)> (readonly)

The fuzzing rules.

Returns:

  • (Array<(Regexp, Enumerator)>)


184
185
186
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 184

def rules
  @rules
end

Instance Method Details

#fuzz_command(string, index) ⇒ Object

Runs the fuzzed string in a command.

Parameters:

  • string (String)

    The fuzzed string.

  • index (Integer)

    The iteration number.



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 268

def fuzz_command(string,index)
  Tempfile.open("ronin-fuzzer-#{index}") do |tempfile|
    tempfile.write(string)
    tempfile.flush

    arguments = @command.map do |argument|
      if argument.include?('#path#')
        argument.sub('#path#',tempfile.path)
      elsif argument.include?('#string#')
        argument.sub('#string#',string)
      else
        argument
      end
    end

    log_info "Running command #{index}: #{arguments.join(' ')} ..."

    # run the command as it's own process
    unless system(*arguments)
      status = $?

      if status.coredump?
        # jack pot!
        log_error "Process ##{status.pid} coredumped!"
      else
        # process errored out
        log_warning "Process ##{status.pid} exited with status #{status.exitstatus}"
      end
    end
  end
end

#fuzz_file(string, index) ⇒ Object

Writes the fuzzed string to a file.

Parameters:

  • string (String)

    The fuzzed string.

  • index (Integer)

    The fuzzing iteration number.



249
250
251
252
253
254
255
256
257
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 249

def fuzz_file(string,index)
  path = output_path(index)

  log_info "Creating file ##{index}: #{path} ..."

  File.open(path,'wb') do |file|
    file.write string
  end
end

#fuzz_network(string, index) ⇒ Object

Sends the fuzzed string to a TCP/UDP Service.

Parameters:

  • string (String)

    The fuzzed string.

  • index (Integer)

    The iteration number.



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 309

def fuzz_network(string,index)
  log_debug "Connecting to #{@host}:#{@port} ..."

  socket = case @mode
           when :tcp then TCPSocket.new(@host,@port)
           when :udp then UDPSocket.new(@host,@port)
           end

  log_info "Sending message ##{index}: #{string.inspect} ..."
  socket.write(string)
  socket.flush

  log_debug "Disconnecting from #{@host}:#{@port} ..."
  socket.close
end

#output_path(index) ⇒ String

Creates a new output path for the given index.

Parameters:

  • index (Integer)

    The index number of the fuzzing iteration.

Returns:

  • (String)

    The new output path.



236
237
238
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 236

def output_path(index)
  "#{@output_name}-#{index}#{@output_ext}"
end

#parse_host_port(value) ⇒ (String, Integer)

Parses the host and port from the value.

Parameters:

  • value (String)

    The value to parse.

Returns:

  • ((String, Integer))

    The parsed host and port.



349
350
351
352
353
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 349

def parse_host_port(value)
  host, port = value.split(':',2)

  return host, port.to_i
end

#parse_pattern(string) ⇒ Regexp, String

Parses a fuzz pattern.

Parameters:

  • string (String)

    The string to parse.

Returns:

  • (Regexp, String)

    The parsed pattern.



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 395

def parse_pattern(string)
  case string
  when /^\/.+\/$/
    Regexp.new(string[1..-2])
  when /^[a-z][a-z_]+$/
    const = string.upcase

    if Support::Text::Patterns.const_defined?(const,false)
      Support::Text::Patterns.const_get(const,false)
    else
      string
    end
  else
    string
  end
end

#parse_rule(value) ⇒ (Regexp, Enumerator)

Parses a fuzzing rule.

Parameters:

  • value (String)

    The fuzzing rule.

Returns:

  • ((Regexp, Enumerator))

    The fuzzing pattern and list of substitutions.



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 364

def parse_rule(value)
  if value.start_with?('/')
    unless (index = value.rindex('/:'))
      raise(OptionParser::InvalidArgument,"argument must be of the form /REGEXP/:REPLACE, but was: #{value}")
    end

    regexp       = parse_pattern(value[1...index])
    substitution = parse_substitution(value[index+2..])

    return [regexp, substitution]
  else
    unless (index = value.rindex(':'))
      raise(OptionParser::InvalidArgument,"argument must be of the form STRING:STYLE but was: #{value}")
    end

    pattern      = parse_pattern(value[0...index])
    substitution = parse_substitution(value[index+1..])

    return [pattern, substitution]
  end
end

#parse_substitution(string) ⇒ Enumerator

Parses a fuzz substitution Enumerator.

Parameters:

  • string (String)

    The string to parse.

Returns:

  • (Enumerator)

    The parsed substitution Enumerator.



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 421

def parse_substitution(string)
  if string.include?('*')
    string, lengths = string.split('*',2)

    lengths = if lengths.include?('-')
                min, max = lengths.split('-',2)

                (min.to_i .. max.to_i)
              else
                lengths.to_i
              end

    Fuzzing::Repeater.new(lengths).each(string)
  else
    Fuzzing[string]
  end
end

Prints the fuzzed string to STDOUT.

Parameters:

  • string (String)

    The fuzzed string.

  • index (Integer)

    The iteration number.



334
335
336
337
338
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 334

def print_fuzz(string,index)
  log_debug "String ##{index} ..."

  puts string
end

#runObject

Runs the ronin-fuzzer fuzz command.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/ronin/fuzzer/cli/commands/fuzz.rb', line 201

def run
  if @rules.empty?
    print_error "Must specify at least one fuzzing rule"
    exit(-1)
  end

  data = if options[:input] then File.read(options[:input])
         else                    $stdin.read
         end

  method = case @mode
           when :output    then method(:fuzz_file)
           when :command   then method(:fuzz_command)
           when :tcp, :udp then method(:fuzz_network)
           else                 method(:print_fuzz)
           end

  fuzzer = Fuzzing::Fuzzer.new(@rules)

  fuzzer.each(data).each_with_index do |string,index|
    method.call(string,index + 1)

    sleep(@pause) if @pause
  end
end