#!/usr/bin/ruby require 'net/http' require 'net/https' require 'uri' require 'optparse' require 'ostruct' require 'thread' # generic options parser class Options def self.parse(name, args) options = OpenStruct.new options.verbose = false options.debug = false options.headers = { "User-Agent" => "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" } options.ignore_codes = [] options.rmethod = "GET" options.threads = 5 options.timeout = 5 options.data = "" opts = OptionParser.new do |opts| opts.banner = "#{File.basename(name)} ( http://spoofed.org )" opts.banner += "\nUsage: #{name} [uri] [wordlist]" opts.on_tail("--help", "Show help") do puts opts exit end opts.on("-H [HEADER]", "Append this header to all requests. May be called multiple time") do |o| if (o =~ /([^:]*):\s+?(.*)/) options.headers[$1] = $2 else options.headers[o] = "" end end opts.on("-p [POSTDATA]", "Use this HTTP POST data") do |o| options.data = o end opts.on("-m [METHOD]", "Use this HTTP method") do |o| options.rmethod = o end opts.on("-i [CODES]", "Ignore these HTTP codes") do |o| o.split(",").each do |c| options.ignore_codes << c.to_i end end opts.on("-t [THREADS]", "Number of threads (default: 5") do |o| options.threads = o.to_i end opts.on("--timeout [TIMEOUT]", "Timeout (default: 5") do |o| options.timeout = o.to_i end opts.on("-v", "--verbose", "Be verbose") do |o| options.verbose = o end opts.on("-d", "--debug", "Show debug output") do |o| options.debug = o end end options.help = opts.help begin opts.parse!(args) rescue OptionParser::ParseError => e puts "#{e}\n\n#{opts}" exit(1) end options end end def request(uri, headers, data) uri = valid_uri(uri) or return nil begin #http = Net::HTTP.new(uri.host, uri.port, proxy, proxyport) http = Net::HTTP.new(uri.host, uri.port) http.open_timeout = @options.timeout http.read_timeout = @options.timeout * 2 req = uri.query.nil? ? uri.path : "#{uri.path}?#{uri.query}" if (uri.scheme == "https") http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE end method = @options.rmethod.downcase case method when "get" response = http.send "request_#{method}", req, headers when "head" response = http.send "request_#{method}", req, headers when "post" response = http.send "request_#{method}", req, data, headers else raise "Invalid HTTP method #{method}" end rescue Exception => e puts "Connection to #{uri} failed! -- #{e}" return nil end response end def fuzz(uri, word) return if word.nil? puts "Trying '#{word}'" if (@options.debug) fuzzurl = uri.gsub(/FUZZ/, word) fuzzdata = @options.data.gsub(/FUZZ/, word) fuzzheaders = "" response = request(fuzzurl, fuzzheaders, fuzzdata) return if response.nil? unless (@options.ignore_codes.include?(response.code.to_i)) if (@options.data.empty?) puts "#{fuzzurl} -> #{response.code}" if (@options.verbose) response.each_header { |k,v| puts "#{k}: #{v}" } end else puts "#{fuzzurl} -> #{response.code} (with #{fuzzdata})" end puts response if (@options.debug) end end # Is this URI valid? Essentially just calls URI.parse() def valid_uri(uri) begin link = URI.parse("#{uri}") unless (link.scheme =~ /https?/ || link.scheme.nil?) return false end link.path = "/" if link.path.empty? link.fragment = nil rescue URI::Error => e return false end link end @options = Options.parse($0, ARGV) unless (ARGV.size == 2) puts @options.help exit(1) end uri = ARGV[0] wordlist = ARGV[1] words = {} puts "Fuzzing #{uri} with #{wordlist}" if (@options.verbose) begin File.open(wordlist, "r") do |file| while (line = file.gets) line.chomp! line = line.nil? ? "" : line words[line] = 1 unless (words.include?(line)) end end rescue Exception => e puts "Couldn't open wordlist #{wordlist}: #{e}" end threads = [] lock = Mutex.new (1..@options.threads).each do |n| threads << Thread.new { word = "" until words.empty? word = lock.synchronize { words.shift[0] } fuzz(uri, word) end } end threads.each do |t| t.join end