Path: blob/master/modules/auxiliary/scanner/http/brute_dirs.rb
21546 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'enumerable'67class MetasploitModule < Msf::Auxiliary8include Msf::Exploit::Remote::HttpClient9include Msf::Auxiliary::WmapScanDir10include Msf::Auxiliary::Scanner11include Msf::Auxiliary::Report1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'HTTP Directory Brute Force Scanner',18'Description' => %q{19This module identifies the existence of interesting directories by brute forcing the name20in a given directory path.21},22'Author' => [ 'et' ],23'License' => BSD_LICENSE,24'Notes' => {25'Reliability' => UNKNOWN_RELIABILITY,26'Stability' => UNKNOWN_STABILITY,27'SideEffects' => UNKNOWN_SIDE_EFFECTS28}29)30)3132register_options(33[34OptString.new('PATH', [ true, "The path to identify directories", '/']),35OptString.new('FORMAT', [ true, "The expected directory format (a alpha, d digit, A upperalpha)", 'a,aa,aaa']),36OptInt.new('TIMEOUT', [true, 'The socket connect/read timeout in seconds', 20]),37OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]),38OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]),39]40)4142register_advanced_options(43[44OptInt.new('ErrorCode', [ true, "The expected http code for non existent directories", 404]),45OptPath.new('HTTP404Sigs', [46false, "Path of 404 signatures to use",47File.join(Msf::Config.data_directory, "wmap", "wmap_404s.txt")48]),49OptBool.new('NoDetailMessages', [ false, "Do not display detailed test messages", true ]),50OptInt.new('TestThreads', [ true, "Number of test threads", 25])51]52)53end5455def wmap_enabled56true57end5859def run_host(ip)60conn = false6162timeout = datastore['TIMEOUT']6364delay_value = datastore['DELAY'].to_i65if delay_value < 066raise Msf::OptionValidateError.new(['DELAY'])67end6869jitter_value = datastore['JITTER'].to_i70if jitter_value < 071raise Msf::OptionValidateError.new(['JITTER'])72end7374tpath = normalize_uri(datastore['PATH'])75if tpath[-1, 1] != '/'76tpath += '/'77end7879vhost = datastore['VHOST'] || datastore['RHOST']8081dm = datastore['NoDetailMessages']8283# You may add more extensions in the extens array84extens = ["/"]8586# You may add multiple formats in the array87forma = []88forma = datastore['FORMAT'].split(',')8990ecode = datastore['ErrorCode'].to_i91extens.each do |exte|92#93# Detect error code94#95ecode = datastore['ErrorCode'].to_i96begin97randdir = Rex::Text.rand_text_alpha(5).chomp98randdir << exte99res = send_request_cgi({100'uri' => tpath + randdir,101'method' => 'GET',102'ctype' => 'text/html'103}, timeout)104105return if not res106107tcode = res.code.to_i108109# Look for a string we can signature on as well110if (tcode >= 200 and tcode <= 299)111emesg = nil112File.open(datastore['HTTP404Sigs'], 'rb').each do |str|113if (res.body.index(str))114emesg = str115break116end117end118119if (not emesg)120print_status("Using first 256 bytes of the response as 404 string")121emesg = res.body[0, 256]122else123print_status("Using custom 404 string of '#{emesg}'")124end125else126ecode = tcode127print_status("Using code '#{ecode}' as not found.")128end129rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout130conn = false131rescue ::Timeout::Error, ::Errno::EPIPE132end133134forma.each do |f|135numb = []136f.scan(/./) { |c|137case c138when 'a'139numb << ('a'..'z')140when 'd'141numb << ('0'..'9')142when 'A'143numb << ('A'..'Z')144# These dont actually work145# when 'N'146# numb << ('A'..'Z')+('0'..'9')147# when 'n'148# numb << ('a'..'z')+('0'..'9')149else150print_error("Format string error")151return152end153}154155# exte.scan(/./) { |c|156# numb << "#{c}"157# }158159Enumerable.cart(*numb).each { |testd|160strdir = testd.join161162begin163teststr = tpath + strdir164teststr << exte165166# Add the delay based on JITTER and DELAY if needs be167add_delay_jitter(delay_value, jitter_value)168169vprint_status("Try... #{wmap_base_url}#{teststr} (#{vhost})")170171res = send_request_cgi({172'uri' => teststr,173'method' => 'GET',174'ctype' => 'text/plain'175}, timeout)176177if (not res or ((res.code.to_i == ecode) or (emesg and res.body.index(emesg))))178if dm == false179print_status("NOT Found #{wmap_base_url}#{teststr} #{res.code.to_i}")180# blah181end182else183if res.code.to_i == 400 and ecode != 400184print_error("Server returned an error code. #{wmap_base_url}#{teststr} #{res.code.to_i}")185else186print_good("Found #{wmap_base_url}#{teststr} #{res.code.to_i}")187188report_web_vuln({189:host => rhost,190:port => rport,191:vhost => vhost,192:ssl => ssl,193:path => "#{teststr}",194:method => 'GET',195:pname => "",196:proof => "Res code: #{res.code.to_s}",197:risk => 0,198:confidence => 100,199:category => 'directory',200:description => 'Directory found.',201:name => 'directory'202})203204end205end206rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout207rescue ::Timeout::Error, ::Errno::EPIPE208end209}210end211end212end213end214215216