#!/usr/bin/env ruby # frozen_string_literal: true require 'pwn' require 'optparse' require 'json' opts = {} OptionParser.new do |options| options.banner = "USAGE: #{$PROGRAM_NAME} [opts] " options.on('-dDIR', '--dir-path=DIR', '') do |d| opts[:dir_path] = d end options.on('-tHOST', '--target-host=HOST', '') do |t| opts[:target] = t end options.on('-pPORT', '--port=PORT', '') do |p| opts[:port] = p end options.on('-PPROTOCOL', '--protocol=PROTOCOL', '') do |p| opts[:protocol] = p end options.on('-S', '--[no-]secure', '") do |c| opts[:fuzz_delimeter] = c end options.on('-rREQUEST', '--request-format=REQUEST', "") do |r| opts[:request] = r end options.on('-fFILE', '--fuzz-file=FILE', '') do |f| opts[:fuzz_file] = f end options.on('-eENC', '--payload-encoding=ENC', '') do |e| opts[:encoding] = e end options.on('-DDEPTH', '--encoding-depth=DEPTH', '') do |d| opts[:encoding_depth] = d end options.on('-cENC', '--char-encoding=ENC', '') do |c| opts[:char_encoding] = c end options.on('-TFLOAT', '--response-timeout=FLOAT', '') do |f| opts[:response_timeout] = f end options.on('-lFLOAT', '--request-rate-limit=FLOAT', '') do |l| opts[:request_rate_limit] = l end options.on('-mINT', '--max-threads=INT', '') do |i| opts[:max_threads] = i end options.on('-s', '--[no-]start-reporting-server', '') do |s| opts[:start_reporting_server] = s end end.parse! if opts.empty? puts `#{$PROGRAM_NAME} --help` exit 1 end pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.fetch('PWN_PROVIDER') dir_path = opts[:dir_path].to_s.scrub target = opts[:target] port = opts[:port] protocol = opts[:protocol] tls = opts[:tls] fuzz_delimeter = opts[:fuzz_delimeter] request = opts[:request] fuzz_file = opts[:fuzz_file].to_s.strip.scrub.chomp if File.exist?(opts[:fuzz_file].to_s.strip.scrub.chomp) encoding = opts[:encoding] encoding_depth = opts[:encoding_depth] opts[:char_encoding].nil? ? char_encoding = 'UTF-8' : char_encoding = opts[:char_encoding] response_timeout = opts[:response_timeout] request_rate_limit = opts[:request_rate_limit] opts[:max_threads].nil? ? max_threads = 1 : max_threads = opts[:max_threads].to_i start_reporting_server = opts[:start_reporting_server] results_hash = { data: [] } results_arr = [] mutex = Mutex.new slice = max_threads * 3 File.open(fuzz_file, "rb:#{char_encoding}") do |file| file.each_slice(slice) do |payload_batch_arr| PWN::Plugins::ThreadPool.fill(enumerable_array: payload_batch_arr, max_threads: max_threads) do |payload| socket_fuzz_results_arr = PWN::Plugins::Fuzz.socket( target: target, port: port, protocol: protocol, tls: tls, fuzz_delimeter: fuzz_delimeter, request: request.to_s.b, payload: payload.to_s.b.chomp, encoding: encoding, encoding_depth: encoding_depth, char_encoding: char_encoding, response_timeout: response_timeout, request_rate_limit: request_rate_limit ) socket_fuzz_results_arr.each do |hash_line| mutex.synchronize do results_hash[:data].push(hash_line) end end end end end # Generate HTML Report print "#{$PROGRAM_NAME} Generating Report..." PWN::Reports::Fuzz.generate( dir_path: dir_path, results_hash: results_hash, char_encoding: char_encoding ) puts 'complete.' # Start Simple HTTP Server (If Requested) if start_reporting_server listen_port = 3333 if pwn_provider == 'docker' listen_ip = '0.0.0.0' else listen_ip = '127.0.0.1' end puts "For Scan Results Navigate to: http://127.0.0.1:#{listen_port}/pwn_fuzz_net_app_proto.html" simple_http_server_params = "-i #{listen_ip} -p #{listen_port}" Dir.chdir(dir_path) system( 'pwn_simple_http_server', '-i', listen_ip, '-p', listen_port.to_s ) end