# # Web assessment for the metasploit framework # Efrain Torres - et[ ] metasploit.com 2010 # # $Id$ # $Revision$ # require 'rabal/tree' require 'rexml/document' require 'tempfile' require 'rubygems' require 'active_record' module Msf # # Constants # WMAPVersion = "0.9" WMAPAuthor = "ET et [ ] metasploit.com" WMAPBanner = "\n========[ WMAP v#{WMAPVersion} ]=========\n= #{WMAPAuthor} =\n==============================" WMAP_PATH = '/' WMAP_CHECK = true WMAP_RUNEXPL = true WMAP_EXITIFSESS = true WMAP_SHOW = 2**0 WMAP_EXPL = 2**1 PROXY_CMDLINE = "../ratproxy/ratproxy" PROXY_DEFAULTOPTS = " -a -v " + File.join( ENV.fetch('HOME'), '.msf3') + " -b sqlite3.db" CRAWLER_CMDLINE = "ruby " + File.join(Msf::Config.install_root,"tools", "msfcrawler.rb") CRAWLER_DEFAULTOPTS = "" # Command line does not allow multiline strings so we pass headers header:valueSEPARATOR STR_HEADER_SEPARATOR = '&' # Exclude files can be modified by setting datastore['WMAP_EXCLUDE_FILE'] WMAP_EXCLUDE_FILE = '.*\.(gif|jpg|png*)$' RUN_WMAP_SERVER = true RUN_WMAP_DIR_FILE = true RUN_WMAP_QUERY = true RUN_WMAP_BODY = true RUN_WMAP_HEADERS = true RUN_WMAP_UNIQUE_QUERY = true RUN_WMAP_GENERIC = true class Plugin::Wmap < Msf::Plugin class WmapCommandDispatcher include Msf::Ui::Console::CommandDispatcher def name "Wmap" end # # The initial command set # def commands { "wmap_website" => "List website structure", "wmap_targets" => "Targets in the database", "wmap_sql" => "Query the database", "wmap_run" => "Automatically test/exploit everything", "wmap_proxy" => "Run mitm proxy", "wmap_crawl" => "Crawl website", "wmap_attack" => "Crawl and Test", } end def cmd_wmap_attack(*args) aurl = args.shift puri = URI.parse(val) tssl = (puri.scheme == "https") ? true : false if (puri.host.nil? or puri.host.empty?) print_error( "Error: target http(s)://target/path") else crawldefaultopts = "" rundefaultopts = "" crawlopts = crawldefaultopts + " -t " + aurl + " " + args.join(" ") runopts = rundefaultopts + " -t " + aurl + " " + args.join(" ") #print_status("Crawling") #cmd_wmap_crawl(crawlopts) print_status("Reloading targets") cmd_wmap_targets("-r") print_status("Selecting target") tid = -1 framework.db.each_target do |tgt| if tgt.host == puri.host and tgt.port.to_i == puri.port.to_i tid = tgt.id print_status("Target ID: #{tid}") end end seltgt = framework.db.get_target(tid) if seltgt == nil print_error("Target id not found.") else seltgt.selected = 1 seltgt.save print_status("Testing") cmd_wmap_run("") end end end def cmd_wmap_website(*args) print_status("Website structure") if selected_host == nil print_error("Target not selected.") else print_status("#{selected_host}:#{selected_port} SSL:#{selected_ssl}") print_tree(load_tree) end print_status("Done.") end def cmd_wmap_targets(*args) # Default behavior to handle hosts names in the db as RHOSTS only # accepts IP addresses accept_hostnames = true resolv_hosts = false args.push("-h") if args.length == 0 while (arg = args.shift) case arg when '-a' target_url = args.shift if target_url == nil print_error("URI required (http://@host)") return else puri = uri_parse(target_url) scheme, authority, path, query = puri[2], puri[4], puri[5], puri[7] if(not authority) print_error("URI required (http://@host)") return end uri_ssl= 0 if scheme == 'https' uri_ssl = 1 end uri_auth = authority.split(':') uri_host = uri_auth[0] uri_port = 80 if uri_auth[1] uri_port = uri_auth[1] end uri_path = path if path == nil or path == '' uri_path = '/' end if Rex::Socket.dotted_ip?(uri_host) hip = uri_host framework.db.create_target(hip, uri_port, uri_ssl, 0) print_status("Added target #{hip} #{uri_port} #{uri_ssl}") framework.db.create_request(hip,uri_port,uri_ssl,'GET',uri_path,'',query,'','','','') print_status("Added request #{uri_path} #{query}") else if accept_hostnames framework.db.create_target(uri_host, uri_port, uri_ssl, 0) print_status("Added target #{uri_host} #{uri_port} #{uri_ssl}") framework.db.create_request(uri_host,uri_port,uri_ssl,'GET',uri_path,'',query,'','','','') print_status("Added request #{uri_host} #{query}") else print_error("RHOSTS only accepts IP addresses: #{req.host}") if resolv_hosts hip = Rex::Socket.resolv_to_dotted(req.host) framework.db.create_target(hip, uri_port, uri_ssl, 0) print_status("Added target #{hip} #{uri_port} #{uri_ssl}") framework.db.create_request(hip,uri_port,uri_ssl,'GET',uri_path, '',query,'','','','') print_status("Added request #{uri_path} #{query}") end end end end when '-p' print_status(" Id. Host\t\t\t\t\tPort\tSSL") framework.db.each_target do |tgt| if tgt.ssl == 1 usessl = "[*]" else usessl = "" end maxcols = 35 cols = maxcols - tgt.host.length thost = "#{tgt.host.to_s[0..maxcols]}"+(" "*cols) if tgt.selected == 1 print_status("=> #{tgt.id}. #{thost}\t#{tgt.port}\t#{usessl}") else print_status(" #{tgt.id}. #{thost}\t#{tgt.port}\t#{usessl}") end end print_status("Done.") when '-r' framework.db.delete_all_targets framework.db.each_distinct_target do |req| if Rex::Socket.dotted_ip?(req.host) framework.db.create_target(req.host, req.port, req.ssl, 0) print_status("Added. #{req.host} #{req.port} #{req.ssl}") else if accept_hostnames framework.db.create_target(req.host, req.port, req.ssl, 0) print_status("Added host #{req.host}") else print_error("RHOSTS only accepts IP addresses: #{req.host}") if resolv_hosts hip = Rex::Socket.resolv_to_dotted(req.host) framework.db.create_target(hip, req.port, req.ssl, 0) print_status("Added host #{req.host} resolved as #{hip}.") end end end end when '-s' framework.db.each_target do |tgt| tgt.selected = 0 tgt.save end seltgt = framework.db.get_target(args.shift) if seltgt == nil print_error("Target id not found.") else seltgt.selected = 1 seltgt.save end when '-h' print_status("Usage: wmap_targets [options]") print_line("\t-h Display this help text") print_line("\t-c [url] Crawl website (msfcrawler)") print_line("\t-p Print all available targets") print_line("\t-r Reload targets table") print_line("\t-s [id] Select target for testing") print_line("\t-a [url] Add new target") print_line("") return end end end def cmd_wmap_sql(*args) qsql = args.join(" ") args.push("-h") if args.length == 0 while (arg = args.shift) case arg when '-h' print_status("Usage: wmap_sql [sql query]") print_line("\t-h Display this help text") print_line("") return end end print_line("SQL: #{qsql}") begin res =framework.db.sql_query(qsql) res.each do |o| line = '' o.each do |k, v| if v line << v end line << '|' end print_line(line) end rescue ::Exception print_error("SQL Error #{$!}") return end end # # A copy of the shotgun approach to website exploitation # def cmd_wmap_run(*args) stamp = Time.now.to_f mode = 0 eprofile = [] using_p = false args.push("-h") if args.length == 0 while (arg = args.shift) case arg when '-t' mode |= WMAP_SHOW when '-e' mode |= WMAP_EXPL profile = args.shift if profile print_status("Using profile #{profile}.") begin File.open(profile).each do |str| if not str.include? '#' # Not a comment modname = str.strip if not modname.empty? eprofile << modname end end using_p = true end rescue print_error("Profile not found or invalid.") return end else print_status("Using ALL wmap enabled modules.") end when '-h' print_status("Usage: wmap_run [options]") print_line("\t-h Display this help text") print_line("\t-t Show all matching exploit modules") print_line("\t-e [profile] Launch profile test modules against all matched targets.") print_line("\t No profile runs all enabled modules.") print_line("") return end end if selected_host == nil print_error("Target not selected.") return end # WMAP_DIR, WMAP_FILE matches = {} # WMAP_SERVER matches1 = {} # WMAP_QUERY matches2 = {} # WMAP_BODY matches3 = {} # WMAP_HEADERS matches4 = {} # WMAP_UNIQUE_QUERY matches5 = {} # WMAP_GENERIC matches10 = {} # EXPLOIT OPTIONS opt_str = nil bg = false jobify = false [ [ framework.auxiliary, 'auxiliary' ], [framework.exploits, 'exploit' ] ].each do |mtype| # Scan all exploit modules for matching references mtype[0].each_module do |n,m| e = m.new # Only include wmap_enabled plugins if e.respond_to?("wmap_enabled") penabled = e.wmap_enabled if penabled if not using_p or eprofile.include? n.split('/').last # # First run the WMAP_SERVER plugins # case e.wmap_type when :WMAP_SERVER if RUN_WMAP_SERVER matches1[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end when :WMAP_QUERY if RUN_WMAP_QUERY matches2[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end when :WMAP_BODY if RUN_WMAP_BODY matches3[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end when :WMAP_HEADERS if RUN_WMAP_HEADERS matches4[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end when :WMAP_UNIQUE_QUERY if RUN_WMAP_UNIQUE_QUERY matches5[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end when :WMAP_GENERIC if RUN_WMAP_GENERIC matches10[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end when :WMAP_DIR, :WMAP_FILE if RUN_WMAP_DIR_FILE matches[[selected_host,selected_port,selected_ssl,mtype[1]+'/'+n]]=true end else # Black Hole end end end end end end # # Handle modules that need to be run before all tests, once usually again the web server. # :WMAP_SERVER # idx = 0 matches1.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # For exploits # payload = mod.datastore['PAYLOAD'] encoder = mod.datastore['ENCODER'] target = mod.datastore['TARGET'] nop = mod.datastore['NOP'] # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins that only need to be # launched once. # wtype = mod.wmap_type if wtype == :WMAP_SERVER print_status("Launching #{xref[3]} #{wtype} against #{xref[0]}:#{xref[1]}") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) if session stat = '[*]' if (session == Msf::Exploit::CheckCode::Vulnerable) stat = '[+]' end print_line(stat + ' ' + session[1]) # # Exploit if WMAP_RUNEXPL # if (session == Msf::Exploit::CheckCode::Vulnerable) and WMAP_RUNEXPL print_status("Exploiting...") begin session = mod.exploit_simple( 'Encoder' => encoder, 'Payload' => payload, 'Target' => target, 'Nop' => nop, 'OptionStr' => opt_str, 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => jobify) rescue ::Interrupt raise $! rescue ::Exception => e print_error("Exploit failed: #{e.class} #{e}") if(e.class.to_s != 'Msf::OptionValidateError') print_error("Call stack:") e.backtrace.each do |line| break if line =~ /lib.msf.base.simple/ print_error(" #{line}") end end end # If we were given a session, let's see what we can do with it if (session) # If we aren't told to run in the background and the session can be # interacted with, start interacting with it by issuing the session # interaction command. if (bg == false and session.interactive?) print_line driver.run_single("sessions -q -i #{session.sid}") # Otherwise, log that we created a session else print_status("Session #{session.sid} created in the background.") end # If we ran the exploit as a job, indicate such so the user doesn't # wonder what's up. if WMAP_EXITIFSESS return end elsif (jobify) print_status("Exploit running as background job.") # Worst case, the exploit ran but we got no session, bummer. else print_status("Exploit completed, but no session was created.") end end else print_error("Check failed: The state could not be determined.") end rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{xref[3]}: #{$!}") end end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end # # Handle modules to be run at every path/file # WMAP_DIR, WMAP_FILE # idx = 0 matches.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins that only need to be # launched once. # wtype = mod.wmap_type # #Here is where the fun begins # test_tree = load_tree() test_tree.each do |node| testpath = Pathname.new(node.current_path) strpath = testpath.cleanpath(false).to_s # # Fixing paths # if node.is_leaf? and not node.is_root? # # Later we can add here more checks to see if its a file # else if node.is_root? strpath = "/" else strpath = strpath.chomp + "/" end end strpath = strpath.gsub("//", "/") #print_status("Testing path: #{strpath}") # # Launch plugin depending module type. # Module type depends on main input type. # Code may be the same but it depend on final # versions of plugins # case wtype when :WMAP_FILE if node.is_leaf? and not node.is_root? # # Check if an exclusion regex has been defined # if self.framework.datastore['WMAP_EXCLUDE_FILE'] excludefilestr = self.framework.datastore['WMAP_EXCLUDE_FILE'] else excludefilestr = WMAP_EXCLUDE_FILE end if not strpath.match(excludefilestr) mod.datastore['PATH'] = strpath print_status("Launching #{xref[3]} #{wtype} #{strpath} against #{xref[0]}:#{xref[1]}...") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{name}: #{$!}") end end end end when :WMAP_DIR if not node.is_leaf? or node.is_root? mod.datastore['PATH'] = strpath print_status("Launching #{xref[3]} #{wtype} #{strpath} against #{xref[0]}:#{xref[1]}...") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{name}: #{$!}") end end end end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end # # Run modules for each request to play with URI with UNIQUE query parameters. # WMAP_UNIQUE_QUERY # idx = 0 matches5.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins for each request that have a distinct # GET/POST URI QUERY string. # wtype = mod.wmap_type utest_query = {} framework.db.each_request_target_with_query do |req| # # Only test unique query strings by comparing signature to previous tested signatures 'path,p1,p2,pn' # if (utest_query.has_key?(mod.signature(req.path,req.query)) == false) # # Weird bug req.method doesnt work # collides with some method named 'method' # column table renamed to 'meth'. # mod.datastore['METHOD'] = req.meth.upcase mod.datastore['PATH'] = req.path mod.datastore['QUERY'] = req.query mod.datastore['HEADER_SEP'] = STR_HEADER_SEPARATOR mod.datastore['HEADERS'] = headers_to_s(req.headers,STR_HEADER_SEPARATOR) mod.datastore['COOKIE'] = header(req.headers,"Cookie") mod.datastore['DATA'] = req.body # # TODO: Add method, headers, etc. # if wtype == :WMAP_UNIQUE_QUERY print_status("Launching #{xref[3]} #{wtype} against #{xref[0]}:#{xref[1]}") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{xref[3]}: #{$!}") end end end # # Unique query tested, actually the value does not matter # # print_status("#{mod.signature(req.path,req.query)}") utest_query[mod.signature(req.path,req.query)]=1 end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end # # Run modules for each request to play with URI query parameters. # This approach will reduce the complexity of the Tree used before # and will make this shotgun implementation much simple. # WMAP_QUERY # idx = 0 matches2.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins for each request that have a distinct # GET/POST URI QUERY string. # wtype = mod.wmap_type framework.db.each_request_target_with_query do |req| # # Weird bug req.method doesnt work # collides with some method named 'method' # column table renamed to 'meth'. # mod.datastore['METHOD'] = req.meth.upcase mod.datastore['PATH'] = req.path mod.datastore['QUERY'] = req.query mod.datastore['HEADER_SEP'] = STR_HEADER_SEPARATOR mod.datastore['HEADERS'] = headers_to_s(req.headers,STR_HEADER_SEPARATOR) mod.datastore['COOKIE'] = header(req.headers,"Cookie") mod.datastore['DATA'] = req.body # # TODO: Add method, headers, etc. # if wtype == :WMAP_QUERY print_status("Launching #{xref[3]} #{wtype} against #{xref[0]}:#{xref[1]}") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{xref[3]}: #{$!}") end end end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end # # Run modules for each request to play with request bodies. # WMAP_BODY # idx = 0 matches3.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins for each request for all headers # This can be improved alot . Later versions # Should only tests on unique requests. # wtype = mod.wmap_type framework.db.each_request_target_with_body do |req| # # Weird bug req.method doesnt work # collides with some method named 'method' # column table renamed to 'meth'. # mod.datastore['METHOD'] = req.meth.upcase mod.datastore['PATH'] = req.path mod.datastore['QUERY'] = req.query mod.datastore['HEADER_SEP'] = STR_HEADER_SEPARATOR mod.datastore['HEADERS'] = headers_to_s(req.headers,STR_HEADER_SEPARATOR) mod.datastore['COOKIE'] = header(req.headers,"Cookie") mod.datastore['DATA'] = req.body # # TODO: Add method, headers, etc. # if wtype == :WMAP_BODY print_status("Launching #{xref[3]} #{wtype} against #{xref[0]}:#{xref[1]}") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{xref[3]}: #{$!}") end end end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end # # Run modules for each request to play with request headers. # WMAP_HEADERS # idx = 0 matches4.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins for each request for all headers # This can be improved alot . Later versions # Should only tests on unique requests. # wtype = mod.wmap_type framework.db.each_request_target_with_headers do |req| # # Weird bug req.method doesnt work # collides with some method named 'method' # column table renamed to 'meth'. # mod.datastore['METHOD'] = req.meth.upcase mod.datastore['PATH'] = req.path mod.datastore['QUERY'] = req.query mod.datastore['HEADER_SEP'] = STR_HEADER_SEPARATOR mod.datastore['HEADERS'] = headers_to_s(req.headers,STR_HEADER_SEPARATOR) mod.datastore['COOKIE'] = header(req.headers,"Cookie") mod.datastore['DATA'] = req.body # # TODO: Add method, headers, etc. # if wtype == :WMAP_HEADERS print_status("Launching #{xref[3]} #{wtype} against #{xref[0]}:#{xref[1]}") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{xref[3]}: #{$!}") end end end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end # # Handle modules that need to be after all tests, once. # Good place to have modules that analize the test results and/or # launch exploits. # :WMAP_GENERIC # idx = 0 matches10.each_key do |xref| idx += 1 begin mod = nil #Carefull with the references on this one if ((mod = framework.modules.create(xref[3])) == nil) print_status("Failed to initialize #{xref[3]}") next end if (mode & WMAP_SHOW != 0) print_status("Loaded #{xref[3]} ...") end # # The code is just a proof-of-concept and will be expanded in the future # if (mode & WMAP_EXPL != 0) # # For modules to have access to the global datastore # i.e. set -g DOMAIN test.com # self.framework.datastore.each do |gkey,gval| mod.datastore[gkey]=gval end # # Parameters passed in hash xref # mod.datastore['RHOST'] = xref[0] mod.datastore['RHOSTS'] = xref[0] mod.datastore['RPORT'] = xref[1].to_s mod.datastore['SSL'] = xref[2].to_s # # Run the plugins that only need to be # launched once. # wtype = mod.wmap_type if wtype == :WMAP_GENERIC print_status("Launching #{xref[3]} #{wtype} against #{xref[0]}:#{xref[1]}") # To run check function for modules that are exploits if mod.respond_to?("check") and WMAP_CHECK begin session = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during check launch from #{xref[3]}: #{$!}") end else begin session = mod.run_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output, 'RunAsJob' => false) rescue ::Exception print_status(" >> Exception during launch from #{xref[3]}: #{$!}") end end end end rescue ::Exception print_status(" >> Exception from #{xref[3]}: #{$!}") end end if (mode & WMAP_SHOW != 0) print_status("Analysis completed in #{(Time.now.to_f - stamp)} seconds.") print_status("Done.") end # EOM end # # Run msf proxy # def cmd_wmap_proxy(*args) cmdline = PROXY_CMDLINE proxyopts = PROXY_DEFAULTOPTS + " " + args.join(" ") tpid = 0 proxypid = Process.fork if proxypid.nil? exec cmdline + " " + proxyopts else tpid = proxypid Process.detach(proxypid) end print_status("Cmd: #{cmdline}") print_status("Options: #{proxyopts}") print_status("Executing proxy. pid: #{tpid}") print_status("Done.") end # # Run msf crawler # def cmd_wmap_crawl(*args) cmdline = CRAWLER_CMDLINE crawlopts = CRAWLER_DEFAULTOPTS + " " + args.join(" ") tpid = 0 crawlpid = Process.fork if crawlpid.nil? exec cmdline + " " + crawlopts else tpid = crawlpid Process.detach(crawlpid) end print_status("Cmd: #{cmdline}") print_status("Options: #{crawlopts}") print_status("Crawler. pid: #{tpid}") print_status("Done.") end # # Load website structure into a tree # def load_tree wtree = Tree.new("ROOT_TREE") if selected_host == nil print_error("Target not selected") else framework.db.each_request_target do |req| tarray = req.path.to_s.split(WMAP_PATH) tarray.delete("") tpath = Pathname.new(WMAP_PATH) tarray.each do |df| wtree.add_at_path(tpath.to_s,df) tpath = tpath + Pathname.new(df.to_s) end end end return wtree end # # Print Tree structure. Ugly # def print_tree(tree) if tree.is_leaf? and tree.depth > 0 print_line(("|\t"*(tree.depth-1))+"+------"+tree.name) else print_line(("|\t"*tree.depth)+tree.name) end tree.children.each_pair do |name,child| print_tree(child) end end # # Method to parse URIs Regex RFC3986 # def uri_parse(uri) if uri == '' print_error("URI required") return end regexstr = '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' regexurl = Regexp.new(regexstr, false) ret = regexurl.match(uri) return ret end # # Method to transfor headers to string # def headers_to_s(nheaders,hsep) hstr = "" nheaders.each_line do |lh| if hstr.empty? hstr = lh else hstr = lh + hsep + hstr end end return hstr end # # Method to grab a specific header # def header(nheaders,hname) nheaders.each_line do |lh| n,v = lh.split(": ") if n == hname return v end end return hstr end # # Selected target # def selected_host framework.db.selected_host end def selected_port framework.db.selected_port end def selected_ssl framework.db.selected_ssl end end def initialize(framework, opts) super add_console_dispatcher(WmapCommandDispatcher) print_status("#{WMAPBanner}") end def cleanup remove_console_dispatcher('Wmap') end def name "wmap" end def desc "Web assessment plugin" end protected end end