From 238242ecb93c760b257846a386771492986fd1ae Mon Sep 17 00:00:00 2001 From: James Lee Date: Thu, 14 Oct 2010 18:54:35 +0000 Subject: [PATCH] add latest nessus plugin patches from Zate, fixes #2704 git-svn-id: file:///home/svn/framework3/trunk@10685 4d416f70-5f16-0410-b530-b9f4589650da --- lib/msf/core/db.rb | 116 +++++---- lib/nessus/nessus-xmlrpc.rb | 212 +++++++++++---- lib/rex/parser/nessus_xml.rb | 129 ++++++++++ plugins/nessus.rb | 482 ++++++++++++++++++----------------- 4 files changed, 623 insertions(+), 316 deletions(-) create mode 100644 lib/rex/parser/nessus_xml.rb diff --git a/lib/msf/core/db.rb b/lib/msf/core/db.rb index 3c9d4f3f2e..fcfe2d879a 100644 --- a/lib/msf/core/db.rb +++ b/lib/msf/core/db.rb @@ -2,7 +2,7 @@ require 'rex/parser/nmap_xml' require 'rex/parser/nexpose_xml' require 'rex/parser/retina_xml' require 'rex/parser/netsparker_xml' - +require 'rex/parser/nessus_xml' require 'rex/socket' require 'zip' require 'uri' @@ -3540,70 +3540,96 @@ class DBManager data = args[:data] wspace = args[:wspace] || workspace bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : [] - - doc = rexmlify(data) - doc.elements.each('/NessusClientData_v2/Report/ReportHost') do |host| - # if Nessus resovled the host, its host-ip tag should be set - # otherwise, fall back to the name attribute which would - # logically need to be an IP address - begin - addr = host.elements["HostProperties/tag[@name='host-ip']"].text - rescue - addr = host.attribute("name").value - end - + + #@host = { + #'hname' => nil, + #'addr' => nil, + #'mac' => nil, + #'os' => nil, + #'ports' => [ 'port' => { 'port' => nil, + # 'svc_name' => nil, + # 'proto' => nil, + # 'severity' => nil, + # 'nasl' => nil, + # 'description' => nil, + # 'cve' => [], + # 'bid' => [], + # 'xref' => [] + # } + # ] + #} + parser = Rex::Parser::NessusXMLStreamParser.new + parser.on_found_host = Proc.new { |host| + + addr = host['addr'] || host['hname'] + next unless ipv4_validator(addr) # Catches SCAN-ERROR, among others. + if bl.include? addr next else yield(:address,addr) if block end - - os = host.elements["HostProperties/tag[@name='operating-system']"] + + + os = host['os'] + yield(:os,os) if block if os + report_note( :workspace => wspace, :host => addr, :type => 'host.os.nessus_fingerprint', :data => { - :os => os.text.to_s.strip + :os => os.to_s.strip } ) end - - hname = host.elements["HostProperties/tag[@name='host-fqdn']"] + + hname = host['hname'] + if hname report_host( :workspace => wspace, :host => addr, - :name => hname.text.to_s.strip + :name => hname.to_s.strip ) end - - mac = host.elements["HostProperties/tag[@name='mac-address']"] + + mac = host['mac'] + if mac report_host( :workspace => wspace, :host => addr, - :mac => mac.text.to_s.strip.upcase + :mac => mac.to_s.strip.upcase ) end - - host.elements.each('ReportItem') do |item| - nasl = item.attribute('pluginID').value - port = item.attribute('port').value - proto = item.attribute('protocol').value - name = item.attribute('svc_name').value - severity = item.attribute('severity').value - description = item.elements['plugin_output'] - cve = item.elements['cve'] - bid = item.elements['bid'] - xref = item.elements['xref'] - - handle_nessus_v2(wspace, addr, port, proto, name, nasl, severity, description, cve, bid, xref) - + + host['ports'].each do |item| + next if item['port'] == 0 + msf = nil + nasl = item['nasl'].to_s + port = item['port'].to_s + proto = item['proto'] || "tcp" + name = item['svc_name'] + severity = item['severity'] + description = item['description'] + cve = item['cve'] + bid = item['bid'] + xref = item['xref'] + msf = item['msf'] + + yield(:port,port) if block + + handle_nessus_v2(wspace, addr, port, proto, hname, nasl, severity, description, cve, bid, xref, msf) + end - end + yield(:end,hname) if block + } + + REXML::Document.parse_stream(data, parser) + end # @@ -3898,7 +3924,7 @@ protected # NESSUS v2 file format has a dramatically different layout # for ReportItem data # - def handle_nessus_v2(wspace,addr,port,proto,name,nasl,severity,description,cve,bid,xref) + def handle_nessus_v2(wspace,addr,port,proto,name,nasl,severity,description,cve,bid,xref,msf) report_host(:workspace => wspace, :host => addr, :state => Msf::HostState::Alive) @@ -3915,27 +3941,30 @@ protected refs = [] - cve.collect do |r| + cve.each do |r| r.to_s.gsub!(/C(VE|AN)\-/, '') refs.push('CVE-' + r.to_s) end if cve - bid.collect do |r| + bid.each do |r| refs.push('BID-' + r.to_s) end if bid - xref.collect do |r| + xref.each do |r| ref_id, ref_val = r.to_s.split(':') ref_val ? refs.push(ref_id + '-' + ref_val) : refs.push(ref_id) end if xref - + + msfref = "MSF-" << msf if msf + refs.push msfref if msfref + nss = 'NSS-' + nasl vuln = { :workspace => wspace, :host => addr, :name => nss, - :info => description ? description.text : "", + :info => description ? description : "", :refs => refs } @@ -3947,6 +3976,7 @@ protected report_vuln(vuln) end + # # Qualys report parsing/handling # diff --git a/lib/nessus/nessus-xmlrpc.rb b/lib/nessus/nessus-xmlrpc.rb index 257c5c22b4..6c2141d70b 100644 --- a/lib/nessus/nessus-xmlrpc.rb +++ b/lib/nessus/nessus-xmlrpc.rb @@ -104,7 +104,8 @@ module NessusXMLRPC begin status = docxml.root.elements['status'].text rescue - print_error("Error connecting/logging to the server!") + puts("Error connecting/logging to the server!") + return end if status == "OK" return docxml @@ -129,7 +130,7 @@ module NessusXMLRPC begin response = @https.request( request ) rescue - print_error("error connecting to server: #{@nurl} with URI: #{uri}") + puts("error connecting to server: #{@nurl} with URI: #{uri}") exit end # puts response.body @@ -188,7 +189,11 @@ module NessusXMLRPC # returns: array of uids of active scans def scan_list_uids post= { "token" => @token } + docxml = nil docxml=nessus_request('scan/list', post) + if docxml.nil? + return + end uuids=Array.new docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| uuids.push(scan.elements['uuid'].text) } return uuids @@ -199,7 +204,11 @@ module NessusXMLRPC # returns: array of hash of active scans def scan_list_hash post= { "token" => @token } + docxml = nil docxml=nessus_request('scan/list', post) + if docxml.nil? + return + end scans=Array.new docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| entry=Hash.new @@ -214,49 +223,54 @@ module NessusXMLRPC } return scans end + + def template_list_hash + post= { "token" => @token } + docxml = nessus_request('scan/list', post) + templates = Array.new + docxml.elements.each('/reply/contents/templates/template') { |template| + entry=Hash.new + entry['name']=template.elements['name'].text if template.elements['name'] + entry['pid']=template.elements['policy_id'].text if template.elements['policy_id'] + entry['rname']=template.elements['readableName'].text if template.elements['readableName'] + entry['owner']=template.elements['owner'].text if template.elements['owner'] + entry['target']=template.elements['target'].text if template.elements['target'] + templates.push(entry) + } + return templates + end # get hash of policies # # returns: array of hash of policies def policy_list_hash post= { "token" => @token } - docxml=nessus_request('policy/list', post) + docxml = nil + docxml=nessus_request('scan/list', post) + if docxml.nil? + return + end policies=Array.new - docxml.elements.each('/reply/contents/policies/policy') { |policy| + docxml.elements.each('/reply/contents/policies/policies/policy') { |policy| entry=Hash.new entry['id']=policy.elements['policyID'].text entry['name']=policy.elements['policyName'].text - entry['owner']=policy.elements['policyOwner'].text - entry['vis']=policy.elements['visibility'].text + entry['comment']=policy.elements['policyComments'].text policies.push(entry) } return policies end - # get hash of templates - # - # returns: array of hash of templates - def template_list_hash - post= { "token" => @token } - docxml=nessus_request('scan/list', post) - scans=Array.new - docxml.root.elements['contents'].elements['scans'].elements['scanList'].each_element('//scan') {|scan| - entry=Hash.new - entry['id']=scan.elements['uuid'].text if scan.elements['uuid'] - entry['name']=scan.elements['readableName'].text if scan.elements['readableName'] - entry['current']=scan.elements['completion_current'].text if scan.elements['completion_current'] - entry['total']=scan.elements['completion_total'].text if scan.elements['completion_total'] - scans.push(entry) - } - return scans - end - - # get hash of templates + # get hash of reportss # # returns: array of hash of templates def report_list_hash post= { "token" => @token } + docxml = nil docxml=nessus_request('report/list', post) + if docxml.nil? + return + end #puts docxml reports=Array.new docxml.root.elements['contents'].elements['reports'].each_element('//report') {|report| @@ -275,7 +289,11 @@ module NessusXMLRPC # returns: policyID def policy_get_id(textname) post= { "token" => @token } + docxml = nil docxml=nessus_request('policy/list', post) + if docxml.nil? + return + end docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| if policy.elements['policyName'].text == textname return policy.elements['policyID'].text @@ -289,7 +307,11 @@ module NessusXMLRPC # returns: policyID, policyName def policy_get_first post= { "token" => @token } + docxml = nil docxml=nessus_request('policy/list', post) + if docxml.nil? + return + end docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| return policy.elements['policyID'].text, policy.elements['policyName'].text } @@ -300,7 +322,11 @@ module NessusXMLRPC # returns: array of all policy uids def policy_list_uids post= { "token" => @token } + docxml = nil docxml=nessus_request('policy/list', post) + if docxml.nil? + return + end pids=Array.new docxml.root.elements['contents'].elements['policies'].each_element('//policy') { |policy| pids.push(policy.elements['policyID'].text) } @@ -310,7 +336,11 @@ module NessusXMLRPC # stop scan identified by scan_uuid def scan_stop(uuid) post= { "token" => @token, "scan_uuid" => uuid } + docxml = nil docxml=nessus_request('scan/stop', post) + if docxml.nil? + return + end return docxml end @@ -333,7 +363,11 @@ module NessusXMLRPC # pause scan identified by scan_uuid def scan_pause(uuid) post= { "token" => @token, "scan_uuid" => uuid } + docxml = nil docxml=nessus_request('scan/pause', post) + if docxml.nil? + return + end return docxml end @@ -356,7 +390,11 @@ module NessusXMLRPC # remove scan identified by uuid def scan_resume(uuid) post= { "token" => @token, "scan_uuid" => uuid } + docxml = nil docxml=nessus_request('scan/resume', post) + if docxml.nil? + return + end return docxml end # resume all active scans @@ -378,7 +416,11 @@ module NessusXMLRPC # check status of scan identified by uuid def scan_status(uuid) post= { "token" => @token, "report" => uuid } + docxml = nil docxml=nessus_request('report/list', post) + if docxml.nil? + return + end docxml.root.elements['contents'].elements['reports'].each_element('//report') { |report| if report.elements['name'].text == uuid return (report.elements['status'].text) @@ -402,7 +444,11 @@ module NessusXMLRPC # returns: XML file of report (nessus v2 format) def report_file_download(report) post= { "token" => @token, "report" => report } + file = nil file=nessus_http_request('file/report/download', post) + if file.nil? + return + end return file end @@ -411,14 +457,20 @@ module NessusXMLRPC # returns: XML file of report (nessus v1 format) def report_file1_download(report) post= { "token" => @token, "report" => report, "v1" => "true" } + file=nessus_http_request('file/report/download', post) + return file end # delete report by report ID def report_delete(id) post= { "token" => @token, "report" => id } + docxml = nil docxml=nessus_request('report/delete', post) + if docxml.nil? + return + end return docxml end @@ -427,7 +479,11 @@ module NessusXMLRPC # returns: array of names def policy_list_names post= { "token" => @token } + docxml = nil docxml=nessus_request('policy/list', post) + if docxml.nil? + return + end list = Array.new docxml.root.elements['contents'].elements['policies'].each_element('//policy') {|policy| list.push policy.elements['policyName'].text @@ -449,7 +505,11 @@ module NessusXMLRPC # scanprogressTotal def report_hosts(report_id) post= { "token" => @token, "report" => report_id } + docxml = nil docxml=nessus_request('report/hosts', post) + if docxml.nil? + return + end hosts=Array.new docxml.elements.each('/reply/contents/hostList/host') do |host| entry=Hash.new @@ -472,7 +532,11 @@ module NessusXMLRPC def report_host_ports(report_id,host) post= { "token" => @token, "report" => report_id, "hostname" => host } + docxml = nil docxml=nessus_request('report/ports', post) + if docxml.nil? + return + end ports=Array.new docxml.elements.each('/reply/contents/portList/port') do |port| entry=Hash.new @@ -495,26 +559,42 @@ module NessusXMLRPC def report_host_port_details(report_id,host,port,protocol) post= { "token" => @token, "report" => report_id, "hostname" => host, "port" => port, "protocol" => protocol } + docxml = nil docxml=nessus_request('report/details', post) + if docxml.nil? + return + end reportitems=Array.new docxml.elements.each('/reply/contents/portDetails/ReportItem') do |rpt| entry=Hash.new + cve = Array.new + bid = Array.new entry['port'] = rpt.elements['port'].text if rpt.elements['port'] entry['severity'] = rpt.elements['severity'].text if rpt.elements['severity'] entry['pluginID'] = rpt.elements['pluginID'].text if rpt.elements['pluginID'] entry['pluginName'] = rpt.elements['pluginName'].text if rpt.elements['pluginName'] entry['cvss_base_score'] = rpt.elements['data'].elements['cvss_base_score'].text if rpt.elements['data'].elements['cvss_base_score'] entry['exploit_available'] = rpt.elements['data'].elements['exploit_available'].text if rpt.elements['data'].elements['exploit_available'] - entry['cve'] = rpt.elements['data'].elements['cve'].text if rpt.elements['data'].elements['cve'] + if rpt.elements['data'].elements['cve'] + rpt.elements['data'].elements['cve'].each do |x| + cve.push rpt.elements['data'].elements['cve'].text + end + end + entry['cve'] = cve if cve entry['risk_factor'] = rpt.elements['data'].elements['risk_factor'].text if rpt.elements['data'].elements['risk_factor'] entry['cvss_vector'] = rpt.elements['data'].elements['cvss_vector'].text if rpt.elements['data'].elements['cvss_vector'] - #entry['solution'] = rpt.elements['data/solution'].text #not important right now - #entry['description'] = rpt.elements['data/description'].text #not important right now - #entry['synopsis'] = rpt.elements['data/synopsis'].text #not important right now - #entry['see_also'] = rpt.elements['data/see_also'].text # multiple of these - #entry['bid'] = rpt.elements['data/bid'].text multiple of these - #entry['xref'] = rpt.elements['data/xref'].text # multiple of these - #entry['plugin_output'] = rpt.elements['data/plugin_output'].text #not important right now + entry['solution'] = rpt.elements['data'].elements['solution'].text if rpt.elements['data'].elements['solution'] + entry['description'] = rpt.elements['data'].elements['description'].text if rpt.elements['data'].elements['description'] + entry['synopsis'] = rpt.elements['data'].elements['synopsis'].text if rpt.elements['data'].elements['synopsis'] + entry['see_also'] = rpt.elements['data'].elements['see_also'].text if rpt.elements['data'].elements['see_also'] + if rpt.elements['data'].elements['bid'] + rpt.elements['data'].elements['bid'].each do |y| + bid.push rpt.elements['data'].elements['bid'].text + end + end + entry['bid'] = bid if bid + #entry['xref'] = rpt.elements['data'].elements['xref'].text # multiple of these + entry['plugin_output'] = rpt.elements['data'].elements['plugin_output'].text if rpt.elements['data'].elements['plugin_output'] reportitems.push(entry) end return reportitems @@ -523,23 +603,31 @@ module NessusXMLRPC # get host details for particular host identified by report id # # returns: severity, current, total - def report_get_host(report_id,host) + def report_get_host(report_id,hostname) post= { "token" => @token, "report" => report_id } + docxml = nil docxml=nessus_request('report/hosts', post) - docxml.root.elements['contents'].elements['hostList'].each_element('//host') { |host| - if host.elements['hostname'].text == host + if docxml.nil? + return + end + docxml.elements.each('/reply/contents/hostList/host') do |host| + if host.elements['hostname'].text == hostname severity = host.elements['severity'].text current = host.elements['scanProgressCurrent'].text total = host.elements['scanProgressTotal'].text return severity, current, total end - } + end end # gets a list of each plugin family and the number of plugins for that family. def plugins_list post= { "token" => @token } + docxml = nil docxml=nessus_request('plugins/list', post) + if docxml.nil? + return + end plugins=Array.new docxml.root.elements['contents'].elements['pluginFamilyList'].each_element('//family') { |plugin| entry=Hash.new @@ -553,7 +641,11 @@ module NessusXMLRPC #returns a list of users, if they are an admin and their last login time. def users_list post= { "token" => @token } + docxml = nil docxml=nessus_request('users/list', post) + if docxml.nil? + return + end users=Array.new docxml.root.elements['contents'].elements['users'].each_element('//user') { |user| entry=Hash.new @@ -563,14 +655,16 @@ module NessusXMLRPC users.push(entry) } return users - - end # returns basic data about the feed type and versions. def feed post = { "token" => @token } + docxml = nil docxml = nessus_request('feed', post) + if docxml.nil? + return + end feed = docxml.root.elements['contents'].elements['feed'].text version = docxml.root.elements['contents'].elements['server_version'].text web_version = docxml.root.elements['contents'].elements['web_server_version'].text @@ -579,25 +673,41 @@ module NessusXMLRPC def user_add(user,pass) post= { "token" => @token, "login" => user, "password" => pass } + docxml = nil docxml = nessus_request('users/add', post) + if docxml.nil? + return + end return docxml end def user_del(user) post= { "token" => @token, "login" => user } + docxml = nil docxml = nessus_request('users/delete', post) + if docxml.nil? + return + end return docxml end def user_pass(user,pass) post= { "token" => @token, "login" => user, "password" => pass } + docxml = nil docxml = nessus_request('users/chpasswd', post) + if docxml.nil? + return + end return docxml end def plugin_family(fam) post = { "token" => @token, "family" => fam } + docxml = nil docxml = nessus_request('plugins/list/family', post) + if docxml.nil? + return + end family=Array.new docxml.elements.each('/reply/contents/pluginList/plugin') { |plugin| entry=Hash.new @@ -611,21 +721,31 @@ module NessusXMLRPC def policy_del(pid) post= { "token" => @token, "policy_id" => pid } + docxml = nil docxml = nessus_request('policy/delete', post) + if docxml.nil? + return + end return docxml end def report_del(rid) post= { "token" => @token, "report" => rid } + docxml = nil docxml = nessus_request('report/delete', post) + if docxml.nil? + return + end return docxml end def plugin_detail(pname) post = { "token" => @token, "fname" => pname } + docxml = nil docxml = nessus_request('plugins/description', post) - #return docxml - #details=Array.new + if docxml.nil? + return + end entry=Hash.new docxml.elements.each('reply/contents/pluginDescription') { |desc| entry['name'] = desc.elements['pluginName'].text @@ -646,14 +766,16 @@ module NessusXMLRPC entry['cvss_base_score'] = attr.elements['cvss_base_score'].text if attr.elements['cvss_base_score'] } } - - return entry end def server_prefs post= { "token" => @token } + docxml = nil docxml = nessus_request('preferences/list', post) + if docxml.nil? + return + end prefs = Array.new docxml.elements.each('/reply/contents/ServerPreferences/preference') { |pref| entry=Hash.new @@ -666,7 +788,11 @@ module NessusXMLRPC def plugin_prefs post= { "token" => @token } + docxml = nil docxml = nessus_request('plugins/preferences', post) + if docxml.nil? + return + end prefs = Array.new docxml.elements.each('/reply/contents/PluginsPreferences/item') { |pref| entry=Hash.new diff --git a/lib/rex/parser/nessus_xml.rb b/lib/rex/parser/nessus_xml.rb new file mode 100644 index 0000000000..3de56fa2b1 --- /dev/null +++ b/lib/rex/parser/nessus_xml.rb @@ -0,0 +1,129 @@ +require 'rexml/document' +require 'rex/ui' + +module Rex +module Parser + + +class NessusXMLStreamParser + + attr_accessor :on_found_host + + def initialize(&block) + reset_state + on_found_host = block if block + end + + def reset_state + @host = {'hname' => nil, 'addr' => nil, 'mac' => nil, 'os' => nil, 'ports' => [ + 'port' => {'port' => nil, 'svc_name' => nil, 'proto' => nil, 'severity' => nil, + 'nasl' => nil, 'description' => nil, 'cve' => [], 'bid' => [], 'xref' => [] } ] } + @state = :generic_state + end + + def tag_start(name, attributes) + case name + when "tag" + if attributes['name'] == "mac-address" + @state = :is_mac + end + if attributes['name'] == "host-fqdn" + @state = :is_fqdn + end + if attributes['name'] == "ip-addr" + @state = :is_ip + end + if attributes['name'] == "host-ip" + @state = :is_ip + end + if attributes['name'] == "operating-system" + @state = :is_os + end + when "ReportHost" + @host['hname'] = attributes['name'] + when "ReportItem" + @cve = Array.new + @bid = Array.new + @xref = Array.new + @x = Hash.new + @x['nasl'] = attributes['pluginID'] + @x['port'] = attributes['port'] + @x['proto'] = attributes['protocol'] + @x['svc_name'] = attributes['svc_name'] + @x['severity'] = attributes['severity'] + when "description" + @state = :is_desc + when "cve" + @state = :is_cve + when "bid" + @state = :is_bid + when "xref" + @state = :is_xref + when "solution" + @state = :is_solution + when "metasploit_name" + @state = :msf + end + end + + + + + def text(str) + case @state + when :is_fqdn + @host['hname'] = str + when :is_ip + @host['addr'] = str + when :is_os + @host['os'] = str + when :is_mac + @host['mac'] = str + when :is_desc + @x['description'] = str + when :is_cve + @cve.push str + when :is_bid + @bid.push str + when :is_xref + @xref.push str + when :msf + #this requires that the user has run nessus_exploits to build the index. Not ready for primetime. + #regex = Regexp.new(str, true, 'n') + #File.open("xindex", "r") do |m| + # while line = m.gets + # if (line.match(regex)) + # exp = line.split("|").first + # @x['msf'] = exp + # end + # end + #end + end + end + + def tag_end(name) + case name + when "ReportHost" + on_found_host.call(@host) if on_found_host + reset_state + when "ReportItem" + @x['cve'] = @cve + @x['bid'] = @bid + @x['xref'] = @xref + @host['ports'].push @x + end + @state = :generic_state + end + + # We don't need these methods, but they're necessary to keep REXML happy + # + def xmldecl(version, encoding, standalone); end + def cdata; end + def comment(str); end + def instruction(name, instruction); end + def attlist; end +end + +end +end + diff --git a/plugins/nessus.rb b/plugins/nessus.rb index 68cb6d539b..9d041c2729 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -51,17 +51,46 @@ module Msf "nessus_plugin_list" => "Displays each plugin family and the number of plugins", "nessus_plugin_prefs" => "Display Plugin Prefs", "nessus_policy_list" => "List all polciies", - #"nessus_policy_new" => "Save new policy" "nessus_policy_del" => "Delete a policy", - #"nessus_policy_dupe" => "Duplicate a policy" - #"nessus_policy_rename" => "Rename a policy" - "nessus_find_targets" => "Try to find vulnerable targets from a report" - #"nessus_report_hosts_filter" => "Get list of hosts from a report with filter", - #"nessus_report_tags" => "Not sure what this does yet" - #"nessus_report_upload" => "Upload nessusv2 report" - + "nessus_exploits" => "Generates a search index for exploits.", + "nessus_template_list" => "List all the templates on the server" } end + + def cmd_nessus_exploits + #need to expand this to index all modules. What kind of info is needed? + #find a better place to keep the indexes and a way to name them + #put in version checking: + #check if exists and is a valid readable file (read first line) + #If the version line at start of current index doesnt match rev number of msf, rebuild index + + start = Time.now + File.open("xindex", "w+") do |f| + framework.exploits.sort.each { |refname, mod| + stuff = "" + o = nil + begin + o = mod.new + rescue ::Exception + end + stuff << "#{refname}|#{o.name}|#{o.platform_to_s}|#{o.arch_to_s}" + next if not o + o.references.map do |x| + if !(x.ctx_id == "URL") + if (x.ctx_id == "MSB") + stuff << "|#{x.ctx_val}" + else + stuff << "|#{x.ctx_id}-#{x.ctx_val}" + end + end + end + stuff << "\n" + f.puts(stuff) + } + end + total = Time.now - start + print_status("It has taken : #{total} seconds to build the exploits search index") + end def cmd_nessus_logout @token = nil @@ -70,129 +99,61 @@ module Msf end def cmd_nessus_help(*args) - - case args[0] - when "test" - puts "test" - when "nessus_connect" - print_status("%redYou must do this before any other commands.%clr") - print_status("Usage: ") - print_status(" nessus_connect username:password@hostname:port ") - print_status(" Example:> nessus_connect msf:msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect username@hostname:port ") - print_status(" Example:> nessus_connect msf@192.168.1.10:8834 ok") - print_status(" OR") - print_status(" nessus_connect hostname:port ") - print_status(" Example:> nessus_connect 192.168.1.10:8834 ok") - print_status() - - print_status("%bldusername%clr and %bldpassword%clr are the ones you use to login to the nessus web front end") - print_status("%bldhostname%clr can be an ip address or a dns name of the web front end.") - print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.") - print_status("The \"ok\" on the end is important. It is a way of letting you") - print_status("know that nessus used a self signed cert and the risk that presents.") - when "nessus_report_list" - print_status("Usage: ") - print_status(" nessus_report_list") - print_status(" Example:> nessus_report_list") - print_status() - print_status("Generates a list of all reports visable to your user.") - when "nessus_report_get" - print_status("Usage: ") - print_status(" nessus_report_get ") - print_status(" Example:> nessus_report_get f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("This command pulls the provided report from the nessus server in the nessusv2 format") - print_status("and parses it the same way db_import_nessus does. After it is parsed it will be") - print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.") - when "nessus_scan_status" - print_status("Usage: ") - print_status(" nessus_scan_status") - print_status(" Example:> nessus_scan_status") - print_status() - when "nessus_server_status" - print_status("Usage: ") - print_status(" nessus_server_status") - print_status(" Example:> nessus_server_status") - print_status() - else - tbl = Rex::Ui::Text::Table.new( - 'Columns' => - [ - 'Command', - 'Help Text' - ]) - tbl << [ "Generic Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_connect", "Connect to a nessus server" ] - tbl << [ "nessus_logout", "Logout from the nessus server" ] - tbl << [ "nessus_help", "Listing of available nessus commands" ] - tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] - tbl << [ "nessus_admin", "Checks if user is an admin" ] - tbl << [ "nessus_server_feed", "Nessus Feed Type" ] - tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ] - tbl << [ "", ""] - tbl << [ "Reports Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_report_list", "List all Nessus reports" ] - tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ] - tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] - tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ] - tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ] - tbl << [ "", ""] - tbl << [ "Scan Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_scan_new", "Create new Nessus Scan" ] - tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ] - tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ] - tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ] - tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ] - tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ] - tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ] - tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ] - tbl << [ "", ""] - tbl << [ "Plugin Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ] - tbl << [ "nessus_plugin_family", "List plugins in a family" ] - tbl << [ "nessus_plugin_details", "List details of a particular plugin" ] - tbl << [ "", ""] - tbl << [ "User Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_user_list", "Show Nessus Users" ] - tbl << [ "nessus_user_add", "Add a new Nessus User" ] - tbl << [ "nessus_user_del", "Delete a Nessus User" ] - tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ] - tbl << [ "", ""] - tbl << [ "Policy Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_policy_list", "List all polciies" ] - tbl << [ "nessus_policy_del", "Delete a policy" ] - - - #tbl << [ "nessus_server_prefs", "Display Server Prefs" ] - - #tbl << [ "nessus_policy_new", "Save new policy" ] - - #tbl << [ "nessus_policy_dupe", "Duplicate a policy" ] - #tbl << [ "nessus_policy_rename", "Rename a policy" ] - #tbl << [ "nessus_report_del", "Delete a report" ] - - - - #tbl << [ "nessus_report_hosts_filter", "Get list of hosts from a report with filter" ] - - - #tbl << [ "nessus_report_tags", "Not sure what this does yet" ] - #tbl << [ "nessus_report_upload", "Upload nessusv2 report" ] - print_good("Nessus Help") - print_good("type nessus_help for help with specific commands") - $stdout.puts "\n" - $stdout.puts tbl.to_s + "\n" - - - end + tbl = Rex::Ui::Text::Table.new( + 'Columns' => + [ + 'Command', + 'Help Text' + ]) + tbl << [ "Generic Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_connect", "Connect to a nessus server" ] + tbl << [ "nessus_logout", "Logout from the nessus server" ] + tbl << [ "nessus_help", "Listing of available nessus commands" ] + tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] + tbl << [ "nessus_admin", "Checks if user is an admin" ] + tbl << [ "nessus_server_feed", "Nessus Feed Type" ] + tbl << [ "nessus_find_targets", "Try to find vulnerable targets from a report" ] + tbl << [ "nessus_server_prefs", "Display Server Prefs" ] + tbl << [ "", ""] + tbl << [ "Reports Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_report_list", "List all Nessus reports" ] + tbl << [ "nessus_report_get", "Import a report from the nessus server in Nessus v2 format" ] + tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] + tbl << [ "nessus_report_host_ports", "Get list of open ports from a host from a report" ] + tbl << [ "nessus_report_host_detail", "Detail from a report item on a host" ] + tbl << [ "", ""] + tbl << [ "Scan Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_scan_new", "Create new Nessus Scan" ] + tbl << [ "nessus_scan_status", "List all currently running Nessus scans" ] + tbl << [ "nessus_scan_pause", "Pause a Nessus Scan" ] + tbl << [ "nessus_scan_pause_all", "Pause all Nessus Scans" ] + tbl << [ "nessus_scan_stop", "Stop a Nessus Scan" ] + tbl << [ "nessus_scan_stop_all", "Stop all Nessus Scans" ] + tbl << [ "nessus_scan_resume", "Resume a Nessus Scan" ] + tbl << [ "nessus_scan_resume_all", "Resume all Nessus Scans" ] + tbl << [ "", ""] + tbl << [ "Plugin Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_plugin_list", "Displays each plugin family and the number of plugins" ] + tbl << [ "nessus_plugin_family", "List plugins in a family" ] + tbl << [ "nessus_plugin_details", "List details of a particular plugin" ] + tbl << [ "", ""] + tbl << [ "User Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_user_list", "Show Nessus Users" ] + tbl << [ "nessus_user_add", "Add a new Nessus User" ] + tbl << [ "nessus_user_del", "Delete a Nessus User" ] + tbl << [ "nessus_user_passwd", "Change Nessus Users Password" ] + tbl << [ "", ""] + tbl << [ "Policy Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_policy_list", "List all polciies" ] + tbl << [ "nessus_policy_del", "Delete a policy" ] + $stdout.puts "\n" + $stdout.puts tbl.to_s + "\n" end def cmd_nessus_server_feed(*args) @@ -220,7 +181,6 @@ module Msf $stdout.puts "\n" $stdout.puts tbl.to_s + "\n" end - end def nessus_verify_token @@ -228,7 +188,6 @@ module Msf ncusage return false end - true end @@ -238,9 +197,7 @@ module Msf print_error("No database has been configured, please use db_create/db_connect first") return false end - true - end def ncusage @@ -277,6 +234,7 @@ module Msf print_status("%bldport%clr is the standard that the nessus web front end runs on : 8834. This is NOT 1241.") print_status("The \"ok\" on the end is important. It is a way of letting you") print_status("know that nessus used a self signed cert and the risk that presents.") + return end if ! @token == '' @@ -349,7 +307,6 @@ module Msf end nessus_login - #Rex::Ui::Text::IrbShell.new(binding).run end def nessus_login @@ -382,6 +339,7 @@ module Msf print_status(" Example:> nessus_report_list") print_status() print_status("Generates a list of all reports visable to your user.") + return end if ! nessus_verify_token @@ -389,7 +347,7 @@ module Msf end list=@n.report_list_hash - + tbl = Rex::Ui::Text::Table.new( 'Columns' => [ @@ -409,6 +367,25 @@ module Msf print_status("You can:") print_status(" Get a list of hosts from the report: nessus_report_hosts ") end + + def check_scan(*args) + + case args.length + when 1 + rid = args[0] + else + print_error("No Report ID Supplied") + return + end + + scans = @n.scan_list_hash + scans.each {|scan| + if scan['id'] == rid + return true + end + } + return false + end def cmd_nessus_report_get(*args) @@ -421,6 +398,7 @@ module Msf print_status("and parses it the same way db_import_nessus does. After it is parsed it will be") print_status("available to commands such as db_hosts, db_vulns, db_services and db_autopwn.") print_status("Use: nessus_report_list to obtain a list of report id's") + return end if ! nessus_verify_token @@ -449,10 +427,50 @@ module Msf print_status(" use nessus_report_list to list all available reports for importing") return end - + + if check_scan(rid) + print_error("That scan is still running.") + return + end + content = nil content=@n.report_file_download(rid) + if content.nil? + print_error("Failed, please reauthenticate") + return + end print_status("importing " + rid) - framework.db.import({:data => content}) + framework.db.import({:data => content}) do |type,data| + case type + when :address + @count = 0 + print("%bld%blu[*]%clr %bld#{data}%clr") + $stdout.flush + when :port + print("\b") + case @count + when 0 + print("%bld%grn|") + @count += 1 + when 1 + print("%bld%grn/") + @count += 1 + when 2 + print("%bld%grn-") + @count += 1 + when 3 + print("%bld%grn/") + @count = 0 + end + $stdout.flush + when :end + print("\b Done!%clr\n") + $stdout.flush + when :os + data.gsub!(/[\n\r]/," or ") if data + print(" #{data} ") + end + end + print_good("Done") end @@ -464,6 +482,7 @@ module Msf print_status(" Example:> nessus_scan_status") print_status() print_status("Returns a list of information about currently running scans.") + return end if ! nessus_verify_token @@ -503,6 +522,52 @@ module Msf print_good(" Import Nessus report to database : nessus_report_get ") print_good(" Pause a nessus scan : nessus_scan_pause ") end + + def cmd_nessus_template_list(*args) + + if args[0] == "-h" + print_status("Usage: ") + print_status(" nessus_template_list") + print_status(" Example:> nessus_template_list") + print_status() + print_status("Returns a list of information about the server templates..") + return + end + + if ! nessus_verify_token + return + end + + list=@n.template_list_hash + + if list.empty? + print_status("No Templates Created.") + print_status("You can:") + print_status(" List of completed scans: nessus_report_list") + print_status(" Create a template: nessus_template_new ") + return + end + + tbl = Rex::Ui::Text::Table.new( + 'Columns' => + [ + 'Template ID', + 'Policy ID', + 'Name', + 'Owner', + 'Target' + ]) + + list.each {|template| + tbl << [ template['name'], template['pid'], template['rname'], template['owner'], template['target'] ] + } + print_good("Templates") + $stdout.puts "\n" + $stdout.puts tbl.to_s + "\n" + $stdout.puts "\n" + print_status("You can:") + print_good(" Import Nessus report to database : nessus_report_get ") + end def cmd_nessus_user_list(*args) @@ -512,6 +577,7 @@ module Msf print_status(" Example:> nessus_user_list") print_status() print_status("Returns a list of the users on the Nessus server and their access level.") + return end if ! nessus_verify_token @@ -549,6 +615,7 @@ module Msf print_status(" Example:> nessus_server_status") print_status() print_status("Returns some status items for the server..") + return end #Auth if ! nessus_verify_token @@ -609,6 +676,7 @@ module Msf print_status(" Example:> nessus_plugin_list") print_status() print_status("Returns a list of the plugins on the server per family.") + return end if ! nessus_verify_token @@ -635,6 +703,25 @@ module Msf $stdout.puts tbl.to_s + "\n" print_status("List plugins for a family : nessus_plugin_family ") end + + def check_policy(*args) + + case args.length + when 1 + pid = args[0] + else + print_error("No Policy ID supplied.") + return + end + + pol = @n.policy_list_hash + pol.each {|p| + if p['id'].to_i == pid + return false + end + } + return true + end def cmd_nessus_scan_new(*args) @@ -645,6 +732,7 @@ module Msf print_status() print_status("Creates a scan based on a policy id and targets.") print_status("use nessus_policy_list to list all available policies") + return end if ! nessus_verify_token @@ -662,6 +750,11 @@ module Msf print_status(" use nessus_policy_list to list all available policies") return end + + if check_policy(pid) + print_error("That policy does not exist.") + return + end print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning #{tgts}") @@ -681,6 +774,7 @@ module Msf print_status() print_status("Pauses a running scan") print_status("use nessus_scan_status to list all available scans") + return end if ! nessus_verify_token @@ -711,6 +805,7 @@ module Msf print_status() print_status("resumes a running scan") print_status("use nessus_scan_status to list all available scans") + return end if ! nessus_verify_token @@ -741,6 +836,7 @@ module Msf print_status() print_status("Returns all the hosts associated with a scan and details about their vulnerabilities") print_status("use nessus_report_list to list all available scans") + return end if ! nessus_verify_token @@ -838,6 +934,7 @@ module Msf print_status() print_status("Returns all the vulns associated with a port for a specific host") print_status("use nessus_report_host_ports to list all available ports for a host") + return end if ! nessus_verify_token @@ -888,6 +985,7 @@ module Msf print_status() print_status("Pauses all currently running scans") print_status("use nessus_scan_list to list all running scans") + return end if ! nessus_verify_token @@ -908,6 +1006,7 @@ module Msf print_status() print_status("Stops a currently running scans") print_status("use nessus_scan_list to list all running scans") + return end if ! nessus_verify_token @@ -938,6 +1037,7 @@ module Msf print_status() print_status("stops all currently running scans") print_status("use nessus_scan_list to list all running scans") + return end if ! nessus_verify_token @@ -958,6 +1058,7 @@ module Msf print_status() print_status("resumes all currently running scans") print_status("use nessus_scan_list to list all running scans") + return end if ! nessus_verify_token @@ -978,6 +1079,7 @@ module Msf print_status() print_status("Only adds non admin users. Must be an admin to add users.") print_status("use nessus_user_list to list all users") + return end if ! nessus_verify_token @@ -1065,6 +1167,7 @@ module Msf print_status() print_status("Changes the password of a user. Must be an admin to change passwords.") print_status("use nessus_user_list to list all users") + return end if ! nessus_verify_token @@ -1105,6 +1208,7 @@ module Msf print_status() print_status("Checks to see if the current user is an admin") print_status("use nessus_user_list to list all users") + return end if ! nessus_verify_token @@ -1127,6 +1231,7 @@ module Msf print_status() print_status("Returns a list of all plugins in that family.") print_status("use nessus_plugin_list to list all plugins") + return end if ! nessus_verify_token @@ -1161,75 +1266,6 @@ module Msf $stdout.puts tbl.to_s + "\n" end - def cmd_nessus_find_targets(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_find targets ") - print_status(" Example:> nessus_find_targets f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Finds targets in a scan with CVSS2 > 7 and returns some info.") - print_status("%redThis plugin is experimental%clr") - end - - #given a report ID, find hosts that are the most vulnerable. Try to match to metasploit exploits if we can. - if ! nessus_verify_token - return - end - - case args.length - when 1 - rid = args[0] - else - print_status("Usage: ") - print_status(" nessus_find_targets ") - print_status(" use nessus_report_list to list all available reports") - return - end - - #tbl = Rex::Ui::Text::Table.new( - # 'Columns' => - # [ - # 'Hostname', - # 'Severity', - # 'Sev 0', - # 'Sev 1', - # 'Sev 2', - # 'Sev 3', - # 'Current Progress', - # 'Total Progress' - # ]) - print_error("This command is still in dev, right now it (maybe) just outputs vulns from a report that are > CVSS2 7. It's slow.") - hosts=@n.report_hosts(rid) - hosts.each {|host| - #tbl << [ host['hostname'], host['severity'], host['sev0'], host['sev1'], host['sev2'], host['sev3'], host['current'], host['total'] ] - ports=@n.report_host_ports(rid, host['hostname']) - ports.each {|port| - #tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] - details=@n.report_host_port_details(rid, host['hostname'], port['portnum'].to_i, port['protocol']) - details.each {|detail| - if detail['cvss_base_score'].to_i > 7 - #match = detail['cve'] - #match = '.*' if match.nil? - #regex = Regexp.new(match, true, 'n') - #Msf::Ui::Console::CommandDispatcher::Core.show_module_set - #show_exploits(regex) - print_status("#{host['hostname']} | #{port['portnum']} | #{detail['severity']} | #{detail['pluginName']} | #{detail['cvss_base_score']} | #{detail['exploit_available']} | #{detail['cve']} | #{detail['risk_factor']}") - end - - ## need to search msf site on all BID's and CVE's and compile a list of possible plugins. maybe db_autopwn does something i can use? - # btw, the | between things looks kinda cool. lets have a party in table.rb later to see if we can add that as an option. - # - } - } - } - #print_good("Report Info") - #$stdout.puts "\n" - #$stdout.puts tbl.to_s + "\n" - - - end - def cmd_nessus_policy_list(*args) if args[0] == "-h" @@ -1238,6 +1274,7 @@ module Msf print_status(" Example:> nessus_policy_list") print_status() print_status("Lists all policies on the server") + return end if ! nessus_verify_token @@ -1249,12 +1286,11 @@ module Msf [ 'ID', 'Name', - 'Owner', - 'visability' + 'Comments' ]) list=@n.policy_list_hash list.each {|policy| - tbl << [ policy['id'], policy['name'], policy['owner'], policy['vis'] ] + tbl << [ policy['id'], policy['name'], policy['comments'] ] } print_good("Nessus Policy List") $stdout.puts "\n" @@ -1270,6 +1306,7 @@ module Msf print_status() print_status("Must be an admin to del policies.") print_status("use nessus_policy_list to list all policies") + return end if ! nessus_verify_token @@ -1317,6 +1354,7 @@ module Msf print_status() print_status("Returns details on a particular plugin.") print_status("use nessus_plugin_list to list all plugins") + return end if ! nessus_verify_token @@ -1369,6 +1407,7 @@ module Msf print_status() print_status("Must be an admin to del reports.") print_status("use nessus_report_list to list all reports") + return end if ! nessus_verify_token @@ -1417,6 +1456,7 @@ module Msf print_status(" Example:> nessus_server_prefs") print_status() print_status("Returns a long list of server prefs.") + return end if ! nessus_verify_token @@ -1452,6 +1492,7 @@ module Msf print_status(" Example:> nessus_plugin_prefs") print_status() print_status("Returns a long list of plugin prefs.") + return end if ! nessus_verify_token @@ -1525,25 +1566,6 @@ module Msf def desc "HTTP Bridge to control a Nessus 4.2 scanner." end - protected end - end -### List of requested additions. - -# refine nessus_find_targets to show vulnerabilities likely to be explitable. - -# define a import method to get those vulnerabilities imported for use by db_autopwn. end state should be most (if not all) vulns result in shell from import. Have to becareful of false postitive and false negative. -# match = '.*' if match.nil? -# regex = Regexp.new(match, true, 'n') -# show_exploits(regex, rank) - -# parse cvss_temporal_score and look at attack vectors etc. - -# convert to using Streaming xml parsing. Nmap XL Parser has the code we need to borrow from. - -# add ability to save report in nbe/nessusv1/html format. posibbly all at once. - -# look at seeing how nessus_scan_new works with ip addresses, can use a file? How about select from the db? yeah db would be cool. -