#!/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