diff --git a/lib/nessus/nessus-xmlrpc.rb b/lib/nessus/nessus-xmlrpc.rb index c6ac670869..58e48e15c0 100644 --- a/lib/nessus/nessus-xmlrpc.rb +++ b/lib/nessus/nessus-xmlrpc.rb @@ -1,811 +1,308 @@ -# -# = nessus-xmlrpc.rb: communicate with Nessus(4.2+) over XML RPC interface -# -# Author:: Vlatko Kosturjak -# -# (C) Vlatko Kosturjak, Kost. Distributed under GPL and BSD license (dual). -# -# == What is this library? -# -# This library is used for communication with Nessus over XML RPC interface. -# You can start, stop, pause and resume scan. Watch progress and status of scan, -# download report, etc. -# -# == Requirements -# -# Required libraries are standard Ruby libraries: uri, net/https and rexml/document. -# -# == Usage: -# -# require 'nessus-xmlrpc' -# n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); -# if n.logged_in -# id,name = n.policy_get_first -# puts "using policy ID: " + id + " with name: " + name -# uid=n.scan_new(id,"textxmlrpc","127.0.0.1") -# puts "status: " + n.scan_status(uid) -# while not n.scan_finished(uid) -# sleep 10 -# end -# content=n.report_file_download(uid) -# File.open('report.xml', 'w') {|f| f.write(content) } -# end +require 'net/http' -require 'uri' -require 'net/https' -require 'rexml/document' - -# NessusXMLRPC module -# -# Usage: -# -# require 'nessus-xmlrpc' -# n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); -# if n.logged_in -# id,name = n.policy_get_first -# uid=n.scan_new(id,"textxmlrpc","127.0.0.1") -# puts "status: " + n.scan_status(uid) -# end -# -# Check NessusXMLRPCrexml for description of methods implemented -# (for both NessusXMLRPCnokogiri and NessusXMLRPCrexml). - -module NessusXMLRPC - - # Class which uses standard REXML to parse nessus XML RPC replies. - class NessusXMLRPC - # initialize object: try to connect to Nessus Scanner using URL, user and password - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - def initialize(url,user,password) - if url == '' - @nurl="https://localhost:8834/" - else - if url =~ /\/$/ - @nurl=url - else - @nurl=url + "/" - end - end - @token='' - #login(user,password) +module Nessus + class Client + class << self + @connection + @token end - - # checks if we're logged in correctly - # - # returns: true if logged in, false if not - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # puts "Logged in" - # else - # puts "Error" - # end - - def logged_in - if @token == '' - return false + + def initialize(host, username = nil, password = nil, ssl_option = nil) + uri = URI.parse(host) + @connection = Net::HTTP.new(uri.host, uri.port) + @connection.use_ssl = true + if ssl_option == "ssl_verify" + @connection.verify_mode = OpenSSL::SSL::VERIFY_PEER else + @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + + yield @connection if block_given? + authenticate(username, password) if username && password + end + + def authenticate(username, password) + payload = { + :username => username, + :password => password, + :json => 1 + } + res = http_post(:uri=>"/session", :data=>payload) + if res['token'] + @token = "token=#{res['token']}" return true - end - end - - # send standard Nessus XML request and check - # - # returns: rexml/document root - def nessus_request(uri, post_data) - body=nessus_http_request(uri, post_data) - # puts response.body - docxml = REXML::Document.new(body) - begin - status = docxml.root.elements['status'].text - rescue - puts("Error connecting/logging to the server!") - return - end - if status == "OK" - return docxml else - return '' + false end end - # send standard Nessus HTTP request and check - # - # returns: body of response - def nessus_http_request(uri, post_data) - url = URI.parse(@nurl + uri) - request = Net::HTTP::Post.new( url.path ) - request.set_form_data( post_data ) - if not defined? @https - @https = Net::HTTP.new( url.host, url.port ) - @https.use_ssl = true - @https.verify_mode = OpenSSL::SSL::VERIFY_NONE + def x_cookie + {'X-Cookie'=>@token} + end + + alias_method :login, :authenticate + + def authenticated + if (@token && @token.include?('token=')) + return true + else + return false end - # puts request - begin - response = @https.request( request ) - rescue - puts("error connecting to server: #{@nurl} with URI: #{uri}") - exit - end - # puts response.body - return response.body + end + + def get_server_properties + http_get(:uri=>"/server/properties", :fields=>x_cookie) end - # login with user & password and sets object-wide @token, @name and @admin - def login(user, password) - post = { "login" => user, "password" => password } - docxml=nessus_request('login', post) - if docxml == '' - @token='' - else - @token = docxml.root.elements['contents'].elements['token'].text - @name = docxml.root.elements['contents'].elements['user'].elements['name'].text - @admin = docxml.root.elements['contents'].elements['user'].elements['admin'].text - # puts "Got token:" + @token - return @token - end + def user_add(username, password, permissions, type) + payload = { + :username => username, + :password => password, + :permissions => permissions, + :type => type, + :json => 1 + } + http_post(:uri=>"/users", :fields=>x_cookie, :data=>payload) + end + def user_delete(user_id) + res = http_delete(:uri=>"/users/#{user_id}", :fields=>x_cookie) + return res.code end - - #checks to see if the user is an admin + + def user_chpasswd(user_id, password) + payload = { + :password => password, + :json => 1 + } + res = http_put(:uri=>"/users/#{user_id}/chpasswd", :data=>payload, :fields=>x_cookie) + return res.code + end + + def user_logout + res = http_delete(:uri=>"/session", :fields=>x_cookie) + return res.code + end + + def list_policies + http_get(:uri=>"/policies", :fields=>x_cookie) + end + + def list_users + http_get(:uri=>"/users", :fields=>x_cookie) + end + + def list_folders + http_get(:uri=>"/folders", :fields=>x_cookie) + end + + def list_scanners + http_get(:uri=>"/scanners", :fields=>x_cookie) + end + + def list_families + http_get(:uri=>"/plugins/families", :fields=>x_cookie) + end + + def list_plugins(family_id) + http_get(:uri=>"/plugins/families/#{family_id}", :fields=>x_cookie) + end + + def list_template(type) + res = http_get(:uri=>"/editor/#{type}/templates", :fields=>x_cookie) + end + + def plugin_details(plugin_id) + http_get(:uri=>"/plugins/plugin/#{plugin_id}", :fields=>x_cookie) + end + def is_admin - if @admin == "TRUE" - return true - end - return false - end - - # initiate new scan with policy id, descriptive name and list of targets - # - # returns: uuid of scan - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # id,name = n.policy_get_first - # puts "using policy ID: " + id + " with name: " + name - # uid=n.scan_new(id,"textxmlrpc","127.0.0.1") - # end - def scan_new(policy_id,scan_name,target) - post= { "token" => @token, "policy_id" => policy_id, "scan_name" => scan_name, "target" => target } - docxml=nessus_request('scan/new', post) - if docxml == '' - return '' - else - uuid=docxml.root.elements['contents'].elements['scan'].elements['uuid'].text - return uuid - end - end - - # get uids of scans - # - # 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 - end - - # get hash of active scan data - # - # 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 - entry['id']=scan.elements['uuid'].text if scan.elements['uuid'] - entry['name']=scan.elements['readableName'].text if scan.elements['readableName'] - entry['owner']=scan.elements['owner'].text if scan.elements['owner'] - entry['start']=scan.elements['start_time'].text if scan.elements['start_time'] - entry['status']=scan.elements['status'].text if scan.elements['status'] - 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 - - 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 = nil - docxml=nessus_request('scan/list', post) - if docxml.nil? - return - end - policies=Array.new - docxml.elements.each('/reply/contents/policies/policies/policy') { |policy| - entry=Hash.new - entry['id']=policy.elements['policyID'].text if policy.elements['policyID'] - entry['name']=policy.elements['policyName'].text if policy.elements['policyName'] - entry['comment']=policy.elements['policyComments'].text if policy.elements['policyComments'] - policies.push(entry) - } - return policies - end - - # 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| - entry=Hash.new - entry['id']=report.elements['name'].text if report.elements['name'] - entry['name']=report.elements['readableName'].text if report.elements['readableName'] - entry['status']=report.elements['status'].text if report.elements['status'] - entry['timestamp']=report.elements['timestamp'].text if report.elements['timestamp'] - reports.push(entry) - } - return reports - end - - # get policy by textname and return policyID - # - # 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 - end - } - return '' - end - - # get first policy from server and returns: policyID, policyName - # - # 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 - } - end - - # get list of policy IDs - # - # 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) } - return pids - end - - # 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 - - # stop all active scans - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # n.scan_stop_all - # end - def scan_stop_all - b=scan_list_uids - b.each {|uuid| - scan_stop(uuid) - } - return b - end - - # 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 - - # pause all active scans - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # n.scan_pause_all - # end - def scan_pause_all - b=scan_list_uids - b.each {|uuid| - scan_pause(uuid) - } - return b - end - - # 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 - # - # Usage: - # - # n=NessusXMLRPC::NessusXMLRPC.new('https://localhost:8834','user','pass'); - # if n.logged_in - # n.scan_resume_all - # end - def scan_resume_all - b=scan_list_uids - b.each {|uuid| - scan_resume(uuid) - } - return b - end - - # 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) - end - } - return '' - end - - # check if scan is finished (completed to be exact) identified by uuid - def scan_finished(uuid) - status=scan_status(uuid) - if status == "completed" + res = http_get(:uri=>"/session", :fields=>x_cookie) + if res['permissions'] == 128 return true else return false end end - - # get report by reportID and return XML file - # - # 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 + + def server_properties + http_get(:uri=>"/server/properties", :fields=>x_cookie) end - # get report by reportID and return XML file (version 1) - # - # 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 + def scan_create(uuid, name, description, targets) + payload = { + :uuid => uuid, + :settings => { + :name => name, + :description => description, + :text_targets => targets + }, + :json => 1 + }.to_json + http_post(:uri=>"/scans", :body=>payload, :fields=>x_cookie, :ctype=>'application/json') end - # get list of names of policies - # - # 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 - } - return list + def scan_launch(scan_id) + http_post(:uri=>"/scans/#{scan_id}/launch", :fields=>x_cookie) end - # get data for each host for a particular report - # - # - # returns: array of hashes: - # hostname - # severity - # severityCount0 - # severityCount1 - # severityCount2 - # severityCount3 - # scanProgressCurrent - # 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 - entry['hostname'] = host.elements['hostname'].text if host.elements['hostname'] - entry['severity'] = host.elements['severity'].text if host.elements['severity'] - sevs=Array.new - host.elements.each('severityCount/item') do |item| - sevs.push item.elements['count'].text if item.elements['count'] - end - entry['sev0'] = sevs[0] if sevs[0] - entry['sev1'] = sevs[1] if sevs[1] - entry['sev2'] = sevs[2] if sevs[2] - entry['sev3'] = sevs[3] if sevs[3] - entry['current'] = host.elements['scanProgressCurrent'].text if host.elements['scanProgressCurrent'] - entry['total'] = host.elements['scanProgressTotal'].text if host.elements['scanProgressTotal'] - hosts.push(entry) - end - return hosts - end - - 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 - entry['portnum'] = port.elements['portNum'].text if port.elements['portNum'] - entry['protocol'] = port.elements['protocol'].text if port.elements['protocol'] - entry['severity'] = port.elements['severity'].text if port.elements['severity'] - entry['svcname'] = port.elements['svcName'].text if port.elements['svcName'] - sevs=Array.new - port.elements.each('severityCount/item') do |item| - sevs.push item.elements['count'].text if item.elements['count'] - end - entry['sev0'] = sevs[0] if sevs[0] - entry['sev1'] = sevs[1] if sevs[1] - entry['sev2'] = sevs[2] if sevs[2] - entry['sev3'] = sevs[3] if sevs[3] - ports.push(entry) - end - return ports - end - - 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'] - 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'].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 + def server_status + http_get(:uri=>"/server/status", :fields=>x_cookie) end - # get host details for particular host identified by report id - # - # returns: severity, current, total - def report_get_host(report_id,hostname) - post= { "token" => @token, "report" => report_id } - docxml = nil - docxml=nessus_request('report/hosts', post) - 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 + def scan_list + http_get(:uri=>"/scans", :fields=>x_cookie) 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 - entry['name']=plugin.elements['familyName'].text - entry['num']=plugin.elements['numFamilyMembers'].text - plugins.push(entry) - } - return plugins - end - - #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 - entry['name']=user.elements['name'].text - entry['admin']=user.elements['admin'].text - entry['lastlogin']=user.elements['lastlogin'].text - 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 - return feed, version, web_version - end - - 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 - entry['filename'] = plugin.elements['pluginFileName'].text if plugin.elements['pluginFileName'] - entry['id'] = plugin.elements['pluginID'].text if plugin.elements['pluginID'] - entry['name'] = plugin.elements['pluginName'].text if plugin.elements['pluginName'] - family.push(entry) - } - return family - end - - 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) - if docxml.nil? - return - end - entry=Hash.new - docxml.elements.each('reply/contents/pluginDescription') { |desc| - entry['name'] = desc.elements['pluginName'].text - entry['id'] = desc.elements['pluginID'].text - entry['family'] = desc.elements['pluginFamily'].text - desc.elements.each('pluginAttributes') { |attr| - entry['exploit_ease'] = attr.elements['exploitability_ease'].text if attr.elements['exploitability_ease'] - entry['cvss_temporal_vector'] = attr.elements['cvss_temporal_vector'].text if attr.elements['cvss_temporal_vector'] - entry['solution'] = attr.elements['solution'].text if attr.elements['solution'] - entry['cvss_temporal_score'] = attr.elements['cvss_temporal_score'].text if attr.elements['cvss_temporal_score'] - entry['risk_factor'] = attr.elements['risk_factor'].text if attr.elements['risk_factor'] - entry['description'] = attr.elements['description'].text if attr.elements['description'] - entry['plugin_publication_date'] = attr.elements['plugin_publication_date'].text if attr.elements['plugin_publication_date'] - entry['cvss_vector'] = attr.elements['cvss_vector'].text if attr.elements['cvss_vector'] - entry['synopsis'] = attr.elements['synopsis'].text if attr.elements['synopsis'] - entry['exploit_available'] = attr.elements['exploit_available'].text if attr.elements['exploit_available'] - entry['plugin_modification_date'] = attr.elements['plugin_modification_date'].text if attr.elements['plugin_modification_date'] - 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 - entry['name'] = pref.elements['name'].text if pref.elements['name'] - entry['value']= pref.elements['value'].text if pref.elements['value'] - prefs.push(entry) - } - return prefs - end - - 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 - entry['fullname'] = pref.elements['fullName'].text if pref.elements['fullName'] - entry['pluginname'] = pref.elements['pluginName'].text if pref.elements['pluginName'] - entry['prefname'] = pref.elements['preferenceName'].text if pref.elements['preferenceName'] - entry['preftype'] = pref.elements['preferenceType'].text if pref.elements['preferenceType'] - entry['prefvalues'] = pref.elements['preferenceValues'].text if pref.elements['preferenceValues'] - prefs.push(entry) - } - return prefs - end - end # end of NessusXMLRPC::Class -end # of Module + def scan_details(scan_id) + http_get(:uri=>"/scans/#{scan_id}", :fields=>x_cookie) + end + def scan_pause(scan_id) + http_post(:uri=>"/scans/#{scan_id}/pause", :fields=>x_cookie) + end + + def scan_resume(scan_id) + http_post(:uri=>"/scans/#{scan_id}/resume", :fields=>x_cookie) + end + + def scan_stop(scan_id) + http_post(:uri=>"/scans/#{scan_id}/stop", :fields=>x_cookie) + end + + def scan_export(scan_id, format) + payload = { + :format => format + }.to_json + http_post(:uri=>"/scans/#{scan_id}/export", :body=>payload, :ctype=>'application/json', :fields=>x_cookie) + end + + def scan_export_status(scan_id, file_id) + request = Net::HTTP::Get.new("/scans/#{scan_id}/export/#{file_id}/status") + request.add_field("X-Cookie", @token) + res = @connection.request(request) + if res.code == "200" + return "ready" + else + res = JSON.parse(res.body) + return res + end + end + + def policy_delete(policy_id) + res = http_delete(:uri=>"/policies/#{policy_id}", :fields=>x_cookie) + return res.code + end + + def host_detail(scan_id, host_id) + res = http_get(:uri=>"/scans/#{scan_id}/hosts/#{host_id}", :fields=>x_cookie) + end + + def report_download(scan_id, file_id) + res = http_get(:uri=>"/scans/#{scan_id}/export/#{file_id}/download", :raw_content=> true, :fields=>x_cookie) + end + + private + + def http_put(opts={}) + uri = opts[:uri] + data = opts[:data] + fields = opts[:fields] || {} + res = nil + + req = Net::HTTP::Put.new(uri) + req.set_form_data(data) unless data.blank? + fields.each_pair do |name, value| + req.add_field(name, value) + end + + begin + res = @connection.request(req) + rescue URI::InvalidURIError + return res + end + + res + end + + def http_delete(opts={}) + uri = opts[:uri] + fields = opts[:fields] || {} + res = nil + + req = Net::HTTP::Delete.new(uri) + + fields.each_pair do |name, value| + req.add_field(name, value) + end + + begin + res = @connection.request(req) + rescue URI::InvalidURIError + return res + end + + res + end + + def http_get(opts={}) + uri = opts[:uri] + fields = opts[:fields] || {} + raw_content = opts[:raw_content] || false + json = {} + + req = Net::HTTP::Get.new(uri) + fields.each_pair do |name, value| + req.add_field(name, value) + end + + begin + res = @connection.request(req) + rescue URI::InvalidURIError + return json + end + if !raw_content + parse_json(res.body) + else + res.body + end + end + + def http_post(opts={}) + uri = opts[:uri] + data = opts[:data] + fields = opts[:fields] || {} + body = opts[:body] + ctype = opts[:ctype] + json = {} + + req = Net::HTTP::Post.new(uri) + req.set_form_data(data) unless data.blank? + req.body = body unless body.blank? + req['Content-Type'] = ctype unless ctype.blank? + fields.each_pair do |name, value| + req.add_field(name, value) + end + + begin + res = @connection.request(req) + rescue URI::InvalidURIError + return json + end + + parse_json(res.body) + end + + def parse_json(body) + buf = {} + + begin + buf = JSON.parse(body) + rescue JSON::ParserError + end + + buf + end + + end +end diff --git a/plugins/nessus.rb b/plugins/nessus.rb index fceab3f44f..19cabdee94 100644 --- a/plugins/nessus.rb +++ b/plugins/nessus.rb @@ -1,30 +1,190 @@ -# $Id$ -# $Revision$ - +# $Id$ $Revision$ require 'nessus/nessus-xmlrpc' require 'rex/parser/nessus_xml' module Msf + + PLUGIN_NAME = 'Nessus' + PLUGIN_DESCRIPTION = 'Nessus Bridge for Metasploit' + class Plugin::Nessus < Msf::Plugin - #creates the index of exploit details to make searching for exploits much faster. - def create_xindex - start = Time.now - print_status("Creating Exploit Search Index - (#{@xindex}) - this wont take long.") + def name + PLUGIN_NAME + end + + class ConsoleCommandDispatcher + include Msf::Ui::Console::CommandDispatcher + + def name + PLUGIN_NAME + end + + def xindex + "#{Msf::Config.get_config_root}/nessus_index" + end + + def nessus_yaml + "#{Msf::Config.get_config_root}/nessus.yaml" + end + + def msf_local + "#{Msf::Config.local_directory}" + end + + def cmd_nessus_index + nessus_index + end + + def commands + { + "nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port ", + "nessus_admin" => "Checks if user is an admin", + "nessus_help" => "Get help on all commands", + "nessus_logout" => "Terminate the session", + "nessus_server_status" => "Check the status of your Nessus server", + "nessus_server_properties" => "Nessus server properties such as feed type, version, plugin set and server UUID", + "nessus_scanner_list" => "List all the scanners configured on the Nessus server", + "nessus_report_download" => "Download a report from the nessus server in either Nessus, HTML, PDF, CSV, or DB format", + "nessus_report_vulns" => "Get list of vulns from a report", + "nessus_report_hosts" => "Get list of hosts from a report", + "nessus_report_host_details" => "Get detailed information from a report item on a host", + "nessus_scan_list" => "List of currently running Nessus scans", + "nessus_scan_new" => "Create a new Nessus scan", + "nessus_scan_launch" => "Launch a previously added scan", + "nessus_scan_pause" => "Pause a running Nessus scan", + "nessus_scan_pause_all" => "Pause all running Nessus scans", + "nessus_scan_stop" => "Stop a running or paused Nessus scan", + "nessus_scan_stop_all" => "Stop all running or paused Nessus scans", + "nessus_scan_resume" => "Resume a paused Nessus scan", + "nessus_scan_resume_all" => "Resume all paused Nessus scans", + "nessus_scan_details" => "Return detailed information of a given scan", + "nessus_scan_export" => "Export a scan result in either Nessus, HTML, PDF, CSV, or DB format", + "nessus_scan_export_status" => "Check the status of scan export", + "nessus_user_list" => "List of Nessus users", + "nessus_user_add" => "Add a new Nessus user", + "nessus_user_del" => "Delete a Nessus user", + "nessus_user_passwd" => "Change Nessus Users Password", + "nessus_plugin_details" => "List details of a particular plugin", + "nessus_plugin_list" => "Display plugin details in a particular plugin family", + "nessus_policy_list" => "List all polciies", + "nessus_policy_del" => "Delete a policy", + "nessus_index" => "Manually generates a search index for exploits", + "nessus_template_list" => "List all the templates on the server", + "nessus_db_scan" => "Create a scan of all IP addresses in db_hosts", + "nessus_db_import" => "Import Nessus scan to the Metasploit connected database", + "nessus_save" => "Save credentials of the logged in user to nessus.yml", + "nessus_folder_list" => "List folders configured on the Nessus server", + "nessus_scanner_list" => "List the configured scanners on the Nessus server", + "nessus_family_list" => "List all the plugin families along with their corresponding family IDs and plugin count" + } + end + + def cmd_nessus_help(*args) + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Command", + "Help Text" + ], + 'SortIndex' => -1 + ) + tbl << [ "Generic Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_connect", "Connect to a Nessus server" ] + tbl << [ "nessus_logout", "Logout from the Nessus server" ] + tbl << [ "nessus_login", "Login into the connected Nesssus server with a different username and password"] + tbl << [ "nessus_save", "Save credentials of the logged in user to nessus.yml"] + tbl << [ "nessus_help", "Listing of available nessus commands" ] + tbl << [ "nessus_server_properties", "Nessus server properties such as feed type, version, plugin set and server UUID." ] + tbl << [ "nessus_server_status", "Check the status of your Nessus Server" ] + tbl << [ "nessus_admin", "Checks if user is an admin" ] + tbl << [ "nessus_template_list", "List scan or policy templates" ] + tbl << [ "nessus_folder_list", "List all configured folders on the Nessus server" ] + tbl << [ "nessus_scanner_list", "List all the scanners configured on the Nessus server" ] + tbl << [ "Nessus Database Commands", "" ] + tbl << [ "-----------------", "-----------------" ] + tbl << [ "nessus_db_scan", "Create a scan of all IP addresses in db_hosts" ] + tbl << [ "nessus_db_import", "Import Nessus scan to the Metasploit connected database" ] + tbl << [ "", ""] + tbl << [ "Reports Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_report_hosts", "Get list of hosts from a report" ] + tbl << [ "nessus_report_vulns", "Get list of vulns from a report" ] + tbl << [ "nessus_report_host_details", "Get detailed information from a report item on a host" ] + tbl << [ "", ""] + tbl << [ "Scan Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_scan_list", "List of all current Nessus scans" ] + tbl << [ "nessus_scan_new", "Create a new Nessus Scan" ] + tbl << [ "nessus_scan_lauch", "Launch a newly created scan. New scans need to be manually launched through this command" ] + tbl << [ "nessus_scan_pause", "Pause a running Nessus scan" ] + tbl << [ "nessus_scan_pause_all", "Pause all running Nessus scans" ] + tbl << [ "nessus_scan_stop", "Stop a running or paused Nessus scan" ] + tbl << [ "nessus_scan_stop_all", "Stop all running or paused Nessus scans" ] + tbl << [ "nessus_scan_resume", "Resume a pasued Nessus scan" ] + tbl << [ "nessus_scan_resume_all", "Resume all paused Nessus scans" ] + tbl << [ "nessus_scan_details", "Return detailed information of a given scan" ] + tbl << [ "nessus_scan_export", "Export a scan result in either Nessus, HTML, PDF, CSV, or DB format" ] + tbl << [ "nessus_scan_export_status", "Check the status of an exported scan" ] + tbl << [ "", ""] + tbl << [ "Plugin Commands", "" ] + tbl << [ "-----------------", "-----------------"] + tbl << [ "nessus_plugin_list", "List all plugins in a particular plugin family." ] + tbl << [ "nessus_family_list", "List all the plugin families along with their corresponding family IDs and plugin count." ] + 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" ] + print_line "" + print_line tbl.to_s + print_line "" + end + + def ncusage + 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") + print_status("OR") + print_status("nessus_connect username@hostname:port ssl_verify") + print_status("Example:> nessus_connect msf@192.168.1.10:8834 ssl_verify") + print_status("OR") + print_status("nessus_connect hostname:port ssl_verify") + print_status("Example:> nessus_connect 192.168.1.10:8834 ssl_verify") + print_status("OR") + print_status("nessus_connect") + print_status("Example:> nessus_connect") + print_status("This only works after you have saved creds with nessus_save") + return + end + + #creates the index of exploit details to make searching for exploits much faster. + def create_xindex + start = Time.now + print_status("Creating Exploit Search Index - (#{xindex}) - this won't take long.") count = 0 - # use Msf::Config.get_config_root as the location. - File.open("#{@xindex}", "w+") do |f| + #Use Msf::Config.get_config_root as the location. + File.open("#{xindex}", "w+") do |f| #need to add version line. f.puts(Msf::Framework::RepoRevision) 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 + 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") @@ -40,250 +200,22 @@ module Msf end total = Time.now - start print_status("It has taken : #{total} seconds to build the exploits search index") - end - - def nessus_index - if File.exist?("#{@xindex}") - #check if it's version line matches current version. - File.open("#{@xindex}") {|f| - line = f.readline - line.chomp! - if line.to_i == Msf::Framework::RepoRevision - print_good("Exploit Index - (#{@xindex}) - is valid.") - else - create_xindex - end - } - else - create_xindex end - end - - class ConsoleCommandDispatcher - include Msf::Ui::Console::CommandDispatcher - - def name - "Nessus" - end - - def commands - { - "nessus_connect" => "Connect to a nessus server: nconnect username:password@hostname:port .", - "nessus_admin" => "Checks if user is an admin.", - "nessus_help" => "Get help on all commands.", - "nessus_logout" => "Terminate the session.", - "nessus_server_status" => "Check the status of your Nessus Server.", - "nessus_server_feed" => "Nessus Feed Type.", - "nessus_server_prefs" => "Display Server Prefs.", - "nessus_report_list" => "List all Nessus reports.", - "nessus_report_get" => "Import a report from the nessus server in Nessus v2 format.", - "nessus_report_del" => "Delete a report.", - "nessus_report_vulns" => "Get list of vulns from a report.", - "nessus_report_hosts" => "Get list of hosts from a report.", - "nessus_report_host_ports" => "Get list of open ports from a host from a report.", - "nessus_report_host_detail" => "Detail from a report item on a host.", - "nessus_scan_status" => "List all currently running Nessus scans.", - "nessus_scan_new" => "Create new Nessus Scan.", - "nessus_scan_pause" => "Pause a Nessus Scan.", - "nessus_scan_pause_all" => "Pause all Nessus Scans.", - "nessus_scan_stop" => "Stop a Nessus Scan.", - "nessus_scan_stop_all" => "Stop all Nessus Scans.", - "nessus_scan_resume" => "Resume a Nessus Scan.", - "nessus_scan_resume_all" => "Resume all Nessus Scans.", - "nessus_user_list" => "Show Nessus Users.", - "nessus_user_add" => "Add a new Nessus User.", - "nessus_user_del" => "Delete a Nessus User.", - "nessus_user_passwd" => "Change Nessus Users Password.", - "nessus_plugin_family" => "List plugins in a family.", - "nessus_plugin_details" => "List details of a particular plugin.", - "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_del" => "Delete a policy.", - "nessus_index" => "Manually generates a search index for exploits.", - "nessus_template_list" => "List all the templates on the server.", - "nessus_db_scan" => "Create a scan of all ips in db_hosts.", - "nessus_save" => "Save username/passowrd/server/port details." + + def nessus_index + if File.exist?("#{xindex}") + #check if it's version line matches current version. + File.open("#{xindex}") { |f| + line = f.readline + line.chomp! + if line.to_i == Msf::Framework::RepoRevision + print_good("Exploit Index - (#{xindex}) - is valid.") + else + create_xindex + end } - end - - def cmd_nessus_index - Msf::Plugin::Nessus.nessus_index - end - - def cmd_nessus_save(*args) - #if we are logged in, save session details to nessus.yaml - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_save") - return - end - - if args[0] - print_status("Usage: ") - print_status(" nessus_save") - return - end - - group = "default" - - if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) - config = Hash.new - config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} - File.open("#{@nessus_yaml}", "w+") do |f| - f.puts YAML.dump(config) - end - print_good("#{@nessus_yaml} created.") - else - print_error("Missing username/password/server/port - relogin and then try again.") - return - end - end - - def cmd_nessus_db_scan(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_db_scan ") - print_status(" Example:> nessus_db_scan 1 \"My Scan\"") - print_status() - print_status("Creates a scan based on all the hosts listed in db_hosts.") - print_status("use nessus_policy_list to list all available policies") - return - end - - if ! nessus_verify_token - return - end - - case args.length - when 2 - pid = args[0].to_i - name = args[1] - else - print_status("Usage: ") - print_status(" nessus_db_scan ") - 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 - - tgts = "" - framework.db.hosts(framework.db.workspace).each do |host| - tgts << host.address - tgts << "," - end - - tgts.chop! - - print_status("Creating scan from policy number #{pid}, called \"#{name}\" and scanning all hosts in workspace") - - scan = @n.scan_new(pid, name, tgts) - - if scan - print_status("Scan started. uid is #{scan}") - end - - end - - def cmd_nessus_logout - @token = nil - print_status("Logged out") - system("rm #{@nessus_yaml}") - print_good("#{@nessus_yaml} removed.") - return - end - - def cmd_nessus_help(*args) - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - "Command", - "Help Text" - ], - 'SortIndex' => -1 - ) - tbl << [ "Generic Commands", "" ] - tbl << [ "-----------------", "-----------------"] - tbl << [ "nessus_connect", "Connect to a nessus server" ] - tbl << [ "nessus_save", "Save nessus login info between sessions" ] - 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_vulns", "Get list of vulns from a report" ] - 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" ] - print_status "" - print_line tbl.to_s - print_status "" - end - - def cmd_nessus_server_feed(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_feed") - print_status(" Example:> nessus_server_feed") - print_status() - print_status("Returns information about the feed type and server version.") - return - end - - if nessus_verify_token - @feed, @version, @web_version = @n.feed - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Feed', - 'Nessus Version', - 'Nessus Web Version' - ]) - tbl << [@feed, @version, @web_version] - print_good("Nessus Status") - print_good "\n" - print_line tbl.to_s + create_xindex end end @@ -295,39 +227,11 @@ module Msf true end - def nessus_verify_db - - if ! (framework.db and framework.db.active) - print_error("No database has been configured, please use db_create/db_connect first") - return false - end - true - end - - def ncusage - 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(" OR") - print_status(" nessus_connect") - print_status(" Example:> nessus_connect") - print_status("This only works after you have saved creds with nessus_save") - return - end - def cmd_nessus_connect(*args) # Check if config file exists and load it - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" - if ! args[0] - if File.exist?("#{@nessus_yaml}") - lconfig = YAML.load_file("#{@nessus_yaml}") + if !args[0] + if File.exist?(nessus_yaml) + lconfig = YAML.load_file(nessus_yaml) @user = lconfig['default']['username'] @pass = lconfig['default']['password'] @host = lconfig['default']['server'] @@ -339,43 +243,29 @@ module Msf return end end - + if args[0] == "-h" 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(" OR") - print_status(" nessus_connect") - print_status(" Example:> nessus_connect") - print_status("This only works after you have saved creds with nessus_save") - print_status() + print_status("nessus_connect username:password@hostname:port ") 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.") + print_status("%bldhostname%clr can be an IP address or a DNS name of the Nessus server.") + print_status("%bldport%clr is the RPC port that the Nessus web front end runs on. By default it is TCP port 8834.") + print_status("The \"ssl_verify\" to verify the SSL certificate used by the Nessus front end. By default the server") + print_status("use a self signed certificate, therefore, users should use ssl_ignore.") return end - - if ! @token == '' - print_error("You are already authenticated. Call nessus_logout before authing again") + + if !@token == '' + print_error("You are already authenticated. Call nessus_logout before authenticating again") return end - if(args.length == 0 or args[0].empty?) ncusage return end - + @user = @pass = @host = @port = @sslv = nil - case args.length when 1,2 if args[0].include? "@" @@ -390,7 +280,6 @@ module Msf @port ||= '8834' @sslv = args[1] end - when 3,4,5 ncusage return @@ -398,656 +287,433 @@ module Msf ncusage return end - if /\/\//.match(@host) ncusage return end - - if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok") - print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker") - print_error(" with the ability to man-in-the-middle the Nessus traffic to capture the Nessus") - print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'") - print_error(" as an additional parameter to this command.") - return - end - - if ! @user + if !@user print_error("Missing Username") ncusage return end - - if ! @pass + if !@pass print_error("Missing Password") ncusage return end - - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + if !((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) ncusage return end nessus_login end - def nessus_login + def cmd_nessus_logout + logout = @n.user_logout + status = logout.to_s + if status == "200" + print_good("User account logged out successfully") + @token = "" + elsif status == "403" + print_status("No user session to logout") + else + print_error("There was some problem in logging out the user #{@user}") + end + return + end - if ! ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + def nessus_login + if !((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) print_status("You need to connect to a server first.") ncusage return end - @url = "https://#{@host}:#{@port}/" print_status("Connecting to #{@url} as #{@user}") - @n=NessusXMLRPC::NessusXMLRPC.new(@url,@user,@pass) - @token=@n.login(@user,@pass) - if @n.logged_in - print_status("Authenticated") + @n = Nessus::Client.new(@url, @user, @pass,@sslv) + if @n.authenticated + print_status("User #{@user} authenticated successfully.") + @token = 1 else print_error("Error connecting/logging to the server!") return end end - def cmd_nessus_report_list(*args) - + def cmd_nessus_save(*args) + #if we are logged in, save session details to nessus.yaml if args[0] == "-h" + print_status(" nessus_save") + return + end + if args[0] 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.") + print_status("nessus_save") return end - - if ! nessus_verify_token - return - end - - list=@n.report_list_hash - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'ID', - 'Name', - 'Status', - 'Date' - ]) - - list.each {|report| - t = Time.at(report['timestamp'].to_i) - tbl << [ report['id'], report['name'], report['status'], t.strftime("%H:%M %b %d %Y") ] - } - print_good("Nessus Report List") - print_good "\n" - print_line tbl.to_s + "\n" - 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 + group = "default" + if ((@user and @user.length > 0) and (@host and @host.length > 0) and (@port and @port.length > 0 and @port.to_i > 0) and (@pass and @pass.length > 0)) + config = Hash.new + config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}} + File.open("#{nessus_yaml}", "w+") do |f| + f.puts YAML.dump(config) end - } - return false - end - - def cmd_nessus_report_get(*args) - - if args[0] == "-h" - 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.") - print_status("Use: nessus_report_list to obtain a list of report id's") - return - end - - if ! nessus_verify_token - return - end - - if ! nessus_verify_db - return - end - - if(args.length == 0 or args[0].empty? or args[0] == "-h") - print_status("Usage: ") - print_status(" nessus_report_get ") - print_status(" use nessus_report_list to list all available reports for importing") - return - end - - rid = nil - - case args.length - when 1 - rid = args[0] + print_good("#{nessus_yaml} created.") else - print_status("Usage: ") - print_status(" nessus_report_get ") - print_status(" use nessus_report_list to list all available reports for importing") + print_error("Missing username/password/server/port - relogin and then try again.") 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}) do |type,data| - case type - when :address - print_line("%bld%blu[*]%clr %bld#{data}%clr") - end - end - print_good("Done") end - def cmd_nessus_scan_status(*args) - + def cmd_nessus_server_properties(*args) if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_status") - print_status(" Example:> nessus_scan_status") + print_status("nessus_server_feed") + print_status("Example:> nessus_server_feed") print_status() - print_status("Returns a list of information about currently running scans.") + print_status("Returns information about the feed type and server version.") return end - - if ! nessus_verify_token - return - end - - list=@n.scan_list_hash - if list.empty? - print_status("No Scans Running.") - print_status("You can:") - print_status(" List of completed scans: nessus_report_list") - print_status(" Create a scan: nessus_scan_new ") - return - end - + resp = @n.server_properties tbl = Rex::Ui::Text::Table.new( 'Columns' => [ - 'Scan ID', - 'Name', - 'Owner', - 'Started', - 'Status', - 'Current Hosts', - 'Total Hosts' + 'Feed', + 'Type', + 'Nessus Version', + 'Nessus Web Version', + 'Plugin Set', + 'Server UUID' ]) - - list.each {|scan| - t = Time.at(scan['start'].to_i) - tbl << [ scan['id'], scan['name'], scan['owner'], t.strftime("%H:%M %b %d %Y"), scan['status'], scan['current'], scan['total'] ] - } - print_good("Running Scans") - print_good "\n" - print_line tbl.to_s - print_good "\n" - print_status("You can:") - 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") - print_good "\n" - print_line tbl.to_s + "\n" - print_good "\n" - print_status("You can:") - print_good(" Import Nessus report to database : nessus_report_get ") - end - - def cmd_nessus_user_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_list") - 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 - return - end - - if ! @n.is_admin - print_status("Your Nessus user is not an admin") - end - - list=@n.users_list - print_good("There are #{list.length} users") - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Is Admin?', - 'Last Login' - ]) - - list.each {|user| - t = Time.at(user['lastlogin'].to_i) - tbl << [ user['name'], user['admin'], t.strftime("%H:%M %b %d %Y") ] - } - print_good("Nessus users") - print_good "\n" + tbl << [ resp["feed"], resp["nessus_type"], resp["server_version"], resp["nessus_ui_version"], resp["loaded_plugin_set"], resp["server_uuid"] ] print_line tbl.to_s end def cmd_nessus_server_status(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_status") - print_status(" Example:> nessus_server_status") + print_status("nessus_server_status") + print_status("Example:> nessus_server_status") print_status() print_status("Returns some status items for the server..") return end - #Auth - if ! nessus_verify_token - return - end - - #Check if we are an admin - if ! @n.is_admin - print_status("You need to be an admin for this.") - return - end - - #Versions - cmd_nessus_server_feed - tbl = Rex::Ui::Text::Table.new( 'Columns' => [ - 'Users', - 'Policies', - 'Running Scans', - 'Reports', - 'Plugins' + 'Status', + 'Progress' ]) - #Count how many users the server has. - list=@n.users_list - users = list.length - - #Count how many policies - list=@n.policy_list_hash - policies = list.length - - #Count how many running scans - list=@n.scan_list_uids - scans = list.length - - #Count how many reports are available - list=@n.report_list_hash - reports = list.length - - #Count how many plugins - list=@n.plugins_list - total = Array.new - list.each {|plugin| - total.push(plugin['num'].to_i) - } - plugins = total.sum - tbl << [users, policies, scans, reports, plugins] - print_good "\n" + list = @n.server_status + tbl << [ list["progress"], list["status"] ] print_line tbl.to_s end - def cmd_nessus_plugin_list(*args) - + def cmd_nessus_admin(*args) if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_list") - print_status(" Example:> nessus_plugin_list") + print_status("nessus_admin") + print_status("Example:> nessus_admin") print_status() - print_status("Returns a list of the plugins on the server per family.") + 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 + if !nessus_verify_token return end + if !@n.is_admin + print_error("Your Nessus user is not an admin") + else + print_good("Your Nessus user is an admin") + end + end + def cmd_nessus_template_list(*args) + if args[0] == "-h" + print_status("nessus_template_list | ") + print_status("Example:> nessus_template_list scan") + print_status("OR") + print_status("nessus_template_list policy") + print_status() + print_status("Returns a list of information about the scan or policy templates..") + return + end + if !nessus_verify_token + return + end + case args.length + when 1 + type = args[0] + else + print_status("Usage: ") + print_status("nessus_template_list | ") + print_status("Example:> nessus_template_list scan") + print_status("OR") + print_status("nessus_template_list policy") + print_status() + print_status("Returns a list of information about the scan or policy templates..") + return + end + if type.in?(['scan', 'policy']) + list=@n.list_template(type) + else + print_error("Only scan and policy are valid templates") + return + end + if list.empty? + print_status("No templates created") + return + end tbl = Rex::Ui::Text::Table.new( 'Columns' => [ - 'Family Name', - 'Total Plugins' + 'Name', + 'Title', + 'Description', + 'Subscription Only', + 'Cloud Only' ]) - list=@n.plugins_list - total = Array.new - list.each {|plugin| - total.push(plugin['num'].to_i) - tbl << [ plugin['name'], plugin['num'] ] + list["templates"].each { |template| + tbl << [ template["name"], template["title"], template["desc"], template["subscription_only"], template["cloud_only"] ] } - plugins = total.sum - tbl << [ '', ''] - tbl << [ 'Total Plugins', plugins ] - print_good("Plugins By Family") - print_good "\n" + print_line print_line tbl.to_s - 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.") + def cmd_nessus_folder_list + if !nessus_verify_token return end - - pol = @n.policy_list_hash - pol.each {|p| - if p['id'].to_i == pid - return false - end + list = @n.list_folders + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "ID", + "Name", + "Type" + ]) + list["folders"].each { |folder| + tbl << [ folder["id"], folder["name"], folder["type"] ] } - return true + print_line + print_line tbl.to_s end - def cmd_nessus_scan_new(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_new ") - print_status(" Example:> nessus_scan_new 1 \"My Scan\" 192.168.1.250") - 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") + def cmd_nessus_scanner_list + if !nessus_verify_token return end - - if ! nessus_verify_token + if !@n.is_admin return end - - case args.length - when 3 - pid = args[0].to_i - name = args[1] - tgts = args[2] - else - print_status("Usage: ") - print_status(" nessus_scan_new ") - 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}") - - scan = @n.scan_new(pid, name, tgts) - - if scan - print_status("Scan started. uid is #{scan}") - end + list = @n.list_scanners + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "ID", + "Name", + "Status", + "Platform", + "Plugin Set", + "UUID" + ]) + list.each { |scanner| + tbl << [ scanner["id"], scanner["name"], scanner["status"], scanner["platform"], scanner["loaded_plugin_set"], scanner["uuid"] ] + } + print_line tbl.to_s end - def cmd_nessus_scan_pause(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_pause ") - print_status(" Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - 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 - return - end - + def check_scan(*args) case args.length when 1 - sid = args[0] + scan_id = args[0] else - print_status("Usage: ") - print_status(" nessus_scan_pause ") - print_status(" use nessus_scan_status to list all available scans") + print_error("No scan ID supplied") return end - - pause = @n.scan_pause(sid) - - print_status("#{sid} has been paused") - end - - def cmd_nessus_scan_resume(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_resume ") - print_status(" Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("resumes a running scan") - print_status("use nessus_scan_status to list all available scans") - return + scans = @n.scan_list + scans.each { |scan| + if scan["scans"]["id"] == scan_id && scan["scans"]["status"] == "completed" + return true end - - if ! nessus_verify_token - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_resume ") - print_status(" use nessus_scan_status to list all available scans") - return - end - - resume = @n.scan_resume(sid) - - print_status("#{sid} has been resumed") + } + return false end def cmd_nessus_report_hosts(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_hosts ") - print_status(" Example:> nessus_report_hosts f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - 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") + print_status("nessus_report_hosts ") + print_status("Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.") return end - - if ! nessus_verify_token - return - end - case args.length when 1 - rid = args[0] + scan_id = args[0] + scan_id = scan_id else print_status("Usage: ") - print_status(" nessus_report_hosts ") - print_status(" use nessus_report_list to list all available reports") + print_status("nessus_report_hosts ") + print_status("Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.") return end - tbl = Rex::Ui::Text::Table.new( 'Columns' => [ - 'Hostname', - 'Severity', - 'Sev 0', - 'Sev 1', - 'Sev 2', - 'Sev 3', - 'Current Progress', - 'Total Progress' + "Host ID", + "Hostname", + "% of Critical Findings", + "% of High Findings", + "% of Medium Findings", + "% of Low Findings" ]) - 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'] ] - } - print_good("Report Info") - print_good "\n" - print_line tbl.to_s - print_status("You can:") - print_status(" Get information from a particular host: nessus_report_host_ports ") + if is_scan_complete(scan_id) + details = @n.scan_details(scan_id) + details["hosts"].each { |host| + tbl << [ host["host_id"], host["hostname"], host["critical"], host["high"], host["medium"], host["low"] ] + } + print_line + print_line tbl.to_s + else + print_error("Only completed scans can be used for host reporting") + return + end end def cmd_nessus_report_vulns(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_vulns ") - print_status(" Example:> nessus_report_vulns f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - print_status() - print_status("Returns all the vulns associated with a scan and details about hosts and their vulnerabilities") - print_status("use nessus_report_list to list all available scans") + print_status("nessus_report_vulns ") + print_status("Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.") return end - - if ! nessus_verify_token - return - end - case args.length when 1 - rid = args[0] + scan_id = args[0] + scan_id = scan_id.to_i else print_status("Usage: ") - print_status(" nessus_report_vulns ") - print_status(" use nessus_report_vulns to list all available reports") + print_status("nessus_report_vulns ") + print_status("Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.") return end - tbl = Rex::Ui::Text::Table.new( 'Columns' => [ - 'Hostname', - 'Port', - 'Proto', - 'Sev', - 'PluginID', - 'Plugin Name' + "Plugin ID", + "Plugin Name", + "Plugin Family", + "Vulnerability Count" ]) - print_status("Grabbing all vulns for report #{rid}") - hosts=@n.report_hosts(rid) - hosts.each do |host| - ports=@n.report_host_ports(rid, host['hostname']) - ports.each do |port| - details=@n.report_host_port_details(rid, host['hostname'], port['portnum'], port['protocol']) - details.each do |detail| - tbl << [host['hostname'], - port['portnum'], - port['protocol'], - detail['severity'], - detail['pluginID'], - detail['pluginName'] - ] - end - end + if is_scan_complete(scan_id) + details = @n.scan_details(scan_id) + details["vulnerabilities"].each { |vuln| + tbl << [ vuln["plugin_id"], vuln["plugin_name"], vuln["plugin_family"], vuln["count"] ] + } + print_line + print_line tbl.to_s + return + else + print_error("Only completed scans can be used for vulnerability reporting") + return end - print_good("Report Info") + end + + def cmd_nessus_report_host_details(*args) + if args[0] == "-h" + print_status("nessus_report_host_details ") + print_status("Example:> nessus_report_host_details 10 5") + print_status("Use nessus_scan_list to get list of all scans. Only completed scans can be used for reporting.") + print_status("Use nessus_report_hosts to get a list of all the hosts along with their corresponding host IDs.") + return + end + if !nessus_verify_token + return + end + case args.length + when 2 + scan_id = args[0] + host_id = args[1] + else + print_status("Usage: ") + print_status("nessus_report_host_detail ") + print_status("Example:> nessus_report_host_detail 10 5") + print_status("Use nessus_scan_list to get list of all scans. Only completed scans can be used for reporting.") + print_status("Use nessus_report_hosts to get a list of all the hosts along with their corresponding host IDs.") + return + end + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Plugin Name', + 'Plugin Famil', + 'Severity' + ]) + details=@n.host_detail(scan_id, host_id) print_line + print_status("Host information") + print_line("IP Address: #{details['info']['host-ip']}") + print_line("Hostname: #{details['info']['host-name']}") + print_line("Operating System: #{details['info']['operating-system']}") + print_line + print_status("Vulnerability information") + details["vulnerabilities"].each { |vuln| + tbl << [ vuln["plugin_name"], vuln["plugin_family"], vuln["severity"] ] + } print_line tbl.to_s - print_status("You can:") - print_status(" Get information from a particular host: nessus_report_host_ports ") + tbl2 = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Plugin Name', + 'Plugin Famil', + 'Severity' + ]) + print_status("Compliance information") + details["compliance"].each { |comp| + tbl2 << [ comp["plugin_name"], comp["plugin_family"], comp["severity"] ] + } + print_line tbl2.to_s + end + + def cmd_nessus_report_download(*args) + if args[0] == "-h" + print_status("nessus_scan_report_download ") + print_status("Use nessus_scan_export_status to check the export status.") + print_status("Use nessus_scan_list -c to list all completed scans along with their corresponding scan IDs") + return + end + if !nessus_verify_token + return + end + case args.length + when 2 + scan_id = args[0] + file_id = args[1] + if is_scan_complete(scan_id) + report = @n.report_download(scan_id, file_id) + File.open("#{msf_local}/#{scan_id}-#{file_id}","w+") do |f| + f.puts report + print_status("Report downloaded to #{msf_local} directory") + end + else + print_error("Only completed scans ca be downloaded") + end + else + print_status("Usage: ") + print_status("nessus_scan_report_download ") + print_status("Use nessus_scan_export_status to check the export status.") + print_status("Use nessus_scan_list -c to list all completed scans along with their corresponding scan IDs") + end end def cmd_nessus_report_host_ports(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_ports ") - print_status(" Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status("nessus_report_host_ports ") + print_status("Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") print_status() print_status("Returns all the ports associated with a host and details about their vulnerabilities") - print_status("use nessus_report_hosts to list all available hosts for a report") + print_status("Use nessus_report_hosts to list all available hosts for a report") end - - if ! nessus_verify_token + if !nessus_verify_token return end - case args.length when 2 host = args[0] rid = args[1] else print_status("Usage: ") - print_status(" nessus_report_host_ports ") - print_status(" use nessus_report_list to list all available reports") + print_status("nessus_report_host_ports ") + print_status("Use nessus_report_list to list all available reports") return end - tbl = Rex::Ui::Text::Table.new( 'Columns' => [ 'Port', @@ -1060,624 +726,954 @@ module Msf 'Sev 3' ]) ports=@n.report_host_ports(rid, host) - ports.each {|port| - tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] + ports.each { |port| + tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] } print_good("Host Info") print_good "\n" print_line tbl.to_s print_status("You can:") - print_status(" Get detailed scan infromation about a specfic port: nessus_report_host_detail ") - end - - def cmd_nessus_report_host_detail(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_host_detail ") - print_status(" Example:> nessus_report_host_ports 192.168.1.250 445 tcp f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - 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 - return - end - - case args.length - when 4 - host = args[0] - port = args[1] - prot = args[2] - rid = args[3] - else - print_status("Usage: ") - print_status(" nessus_report_host_detail ") - print_status(" use nessus_report_host_ports to list all available ports") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Port', - 'Severity', - 'PluginID', - 'Plugin Name', - 'CVSS2', - 'Exploit?', - 'CVE', - 'Risk Factor', - 'CVSS Vector' - ]) - details=@n.report_host_port_details(rid, host, port, prot) - details.each {|detail| - tbl << [ - detail['port'], - detail['severity'], - detail['pluginID'], - detail['pluginName'], - detail['cvss_base_score'] || 'none', - detail['exploit_available'] || '.', - detail['cve'] || '.', - detail['risk_factor'] || '.', - detail['cvss_vector'] || '.' - ] - } - print_good("Port Info") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_scan_pause_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_pause_all") - print_status(" Example:> nessus_scan_pause_all") - 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 - return - end - - pause = @n.scan_pause_all - - print_status("All scans have been paused") - end - - def cmd_nessus_scan_stop(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_stop ") - print_status(" Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") - 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 - return - end - - case args.length - when 1 - sid = args[0] - else - print_status("Usage: ") - print_status(" nessus_scan_stop ") - print_status(" use nessus_scan_status to list all available scans") - return - end - - pause = @n.scan_stop(sid) - - print_status("#{sid} has been stopped") - end - - def cmd_nessus_scan_stop_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_stop_all") - print_status(" Example:> nessus_scan_stop_all") - 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 - return - end - - pause = @n.scan_stop_all - - print_status("All scans have been stopped") - end - - def cmd_nessus_scan_resume_all(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_scan_resume_all") - print_status(" Example:> nessus_scan_resume_all") - 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 - return - end - - pause = @n.scan_resume_all - - print_status("All scans have been resumed") - end - - def cmd_nessus_user_add(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_add ") - print_status(" Example:> nessus_user_add msf 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 - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 2 - user = args[0] - pass = args[1] - else - print_status("Usage: ") - print_status(" nessus_user_add ") - print_status(" Only adds non admin users") - return - end - - u = @n.users_list - u.each { |stuff| - if stuff['name'] == user - print_error("That user exists") - return - end - } - add = @n.user_add(user,pass) - status = add.root.elements['status'].text if add - if status == "OK" - print_good("#{user} has been added") - else - print_error("#{user} was not added") - end - end - - def cmd_nessus_user_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_del ") - print_status(" Example:> nessus_user_del msf") - print_status() - print_status("Only dels non admin users. Must be an admin to del users.") - print_status("use nessus_user_list to list all users") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - user = args[0] - else - print_status("Usage: ") - print_status(" nessus_user_del ") - print_status(" Only dels non admin users") - return - end - - del = @n.user_del(user) - status = del.root.elements['status'].text - if status == "OK" - print_good("#{user} has been deleted") - else - print_error("#{user} was not deleted") - end - end - - def cmd_nessus_user_passwd(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_user_passwd ") - print_status(" Example:> nessus_user_passwd msf newpassword") - 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 - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 2 - user = args[0] - pass = args[1] - else - print_status("Usage: ") - print_status(" nessus_user_passwd ") - print_status(" User list from nessus_user_list") - return - end - - pass = @n.user_pass(user,pass) - status = pass.root.elements['status'].text - if status == "OK" - print_good("#{user}'s password has been changed") - else - print_error("#{user}'s password has not been changed") - end - end - - def cmd_nessus_admin(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_admin") - print_status(" Example:> nessus_admin") - 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 - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - else - print_good("Your Nessus user is an admin") - end - end - - def cmd_nessus_plugin_family(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_family ") - print_status(" Example:> nessus_plugin_family \"Windows : Microsoft Bulletins\" ") - 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 - return - end - - case args.length - when 1 - fam = args[0] - else - print_status("Usage: ") - print_status(" nessus_plugin_family ") - print_status(" list all plugins from a Family from nessus_plugin_list") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Plugin ID', - 'Plugin Name', - 'Plugin File Name' - ]) - - family = @n.plugin_family(fam) - - family.each {|plugin| - tbl << [ plugin['id'], plugin['name'], plugin['filename'] ] - } - print_good("#{fam} Info") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_policy_list(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_policy_list") - print_status(" Example:> nessus_policy_list") - print_status() - print_status("Lists all policies on the server") - return - end - - if ! nessus_verify_token - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'ID', - 'Name', - 'Comments' - ]) - list=@n.policy_list_hash - list.each {|policy| - tbl << [ policy['id'], policy['name'], policy['comments'] ] - } - print_good("Nessus Policy List") - print_good "\n" - print_line tbl.to_s - end - - def cmd_nessus_policy_del(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_policy_del ") - print_status(" Example:> nessus_policy_del 1") - 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 - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - case args.length - when 1 - pid = args[0] - else - print_status("Usage: ") - print_status(" nessus_policy_del ") - print_status(" nessus_policy_list to find the id.") - return - end - - - del = @n.policy_del(pid) - status = del.root.elements['status'].text - if status == "OK" - print_good("Policy number #{pid} has been deleted") - else - print_error("Policy number #{pid} was not deleted") - end - - end - - def cmd_nessus_plugin_details(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_details ") - print_status(" Example:> nessus_plugin_details ping_host.nasl ") - 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 - return - end - - case args.length - when 1 - pname = args[0] - else - print_status("Usage: ") - print_status(" nessus_policy_del ") - print_status(" nessus_plugin_list and then nessus_plugin_family to find the plugin file name.") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - '', - '' - ]) - - entry = @n.plugin_detail(pname) - print_good("Plugin Details for #{entry['name']}") - tbl << [ "Plugin ID", entry['id'] ] - tbl << [ "Plugin Family", entry['family'] ] - tbl << [ "CVSS Base Score", entry['cvss_base_score'] ] - tbl << [ "CVSS Vector", entry['cvss_vector'] ] - tbl << [ "CVSS Temporal Score", entry['cvss_temporal_score'] ] - tbl << [ "CVSS Temporal Vector", entry['cvss_temporal_vector'] ] - tbl << [ "Risk Factor", entry['risk_factor'] ] - tbl << [ "Exploit Available", entry['exploit_available'] ] - tbl << [ "Exploitability Ease", entry['exploit_ease'] ] - tbl << [ "Synopsis", entry['synopsis'] ] - tbl << [ "Description", entry['description'] ] - tbl << [ "Solution", entry['solution'] ] - tbl << [ "Plugin Pub Date", entry['plugin_publication_date'] ] - tbl << [ "Plugin Modification Date", entry['plugin_modification_date'] ] - print_good "\n" - print_line tbl.to_s + print_status("Get detailed scan infromation about a specfic port: nessus_report_host_detail ") end def cmd_nessus_report_del(*args) - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_report_del ") - print_status(" Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status("nessus_report_del ") + print_status("Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") print_status() print_status("Must be an admin to del reports.") - print_status("use nessus_report_list to list all reports") + print_status("Use nessus_report_list to list all reports") return end - - if ! nessus_verify_token + if !nessus_verify_token return end - - if ! @n.is_admin + if !@n.is_admin print_error("Your Nessus user is not an admin") return end - case args.length when 1 rid = args[0] else print_status("Usage: ") - print_status(" nessus_report_del ") - print_status(" nessus_report_list to find the id.") + print_status("nessus_report_del ") + print_status("nessus_report_list to find the id.") return end - - - del = @n.report_del(rid) - status = del.root.elements['status'].text - if status == "OK" - print_good("Report #{rid} has been deleted") - else - print_error("Report #{rid} was not deleted") - end + del = @n.report_del(rid) + status = del.root.elements['status'].text + if status == "OK" + print_good("Report #{rid} has been deleted") + else + print_error("Report #{rid} was not deleted") end - - def cmd_nessus_server_prefs(*args) - - if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_server_prefs") - print_status(" Example:> nessus_server_prefs") - print_status() - print_status("Returns a long list of server prefs.") - return - end - - if ! nessus_verify_token - return - end - - if ! @n.is_admin - print_error("Your Nessus user is not an admin") - return - end - - tbl = Rex::Ui::Text::Table.new( - 'Columns' => [ - 'Name', - 'Value' - ]) - prefs = @n.server_prefs - prefs.each {|pref| - tbl << [ pref['name'], pref['value'] ] - } - print_good("Nessus Server Pref List") - print_good "\n" - print_line tbl.to_s + "\n" - end - def cmd_nessus_plugin_prefs(*args) - + def cmd_nessus_scan_list(*args) if args[0] == "-h" - print_status("Usage: ") - print_status(" nessus_plugin_prefs") - print_status(" Example:> nessus_plugin_prefs") + print_status("nessus_scan_list") + print_status("Example:> nessus_scan_list") print_status() - print_status("Returns a long list of plugin prefs.") + print_status("Returns a list of information about currently running scans.") return end - - if ! nessus_verify_token + if !nessus_verify_token return end + list=@n.scan_list + if list.to_s.empty? + print_status("No scans performed.") + return + else + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Scan ID', + 'Name', + 'Owner', + 'Started', + 'Status', + 'Folder' + ]) + + list["scans"].each { |scan| + if args[0] == "-r" + if scan["status"] == "running" + tbl << [ scan["id"], scan["name"], scan["owner"], scan["starttime"], scan["status"], scan["folder_id"] ] + end + elsif args[0] == "-p" + if scan["status"] == "paused" + tbl << [ scan["id"], scan["name"], scan["owner"], scan["starttime"], scan["status"], scan["folder_id"] ] + end + elsif args[0] == "-c" + if scan["status"] == "completed" + tbl << [ scan["id"], scan["name"], scan["owner"], scan["starttime"], scan["status"], scan["folder_id"] ] + end + elsif args[0] == "-a" + if scan["status"] == "canceled" + tbl << [ scan["id"], scan["name"], scan["owner"], scan["starttime"], scan["status"], scan["folder_id"] ] + end + else + tbl << [ scan["id"], scan["name"], scan["owner"], scan["starttime"], scan["status"], scan["folder_id"] ] + end + } + print_line tbl.to_s + end + end - if ! @n.is_admin + def cmd_nessus_scan_new(*args) + if args[0] == "-h" + print_status("nessus_scan_new ") + print_status("Use nessus_policy_list to list all available policies with their corresponding UUIDs") + return + end + if !nessus_verify_token + return + end + case args.length + when 4 + uuid = args[0] + scan_name = args[1] + description = args[2] + targets = args[3] + else + print_status("Usage: ") + print_status("nessus_scan_new ") + print_status("Use nessus_policy_list to list all available policies with their corresponding UUIDs") + return + end + if valid_policy(uuid) + print_status("Creating scan from policy number #{uuid}, called #{scan_name} - #{description} and scanning #{targets}") + scan = @n.scan_create(uuid, scan_name, description, targets) + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Scan ID", + "Scanner ID", + "Policy ID", + "Targets", + "Owner" + ]) + print_status("New scan added") + tbl << [ scan["scan"]["id"], scan["scan"]["scanner_id"], scan["scan"]["policy_id"], scan["scan"]["custom_targets"], scan["scan"]["owner"] ] + print_status("Use nessus_scan_launch #{scan['scan']['id']} to launch the scan") + print_line tbl.to_s + else + print_error("The policy does not exist") + end + end + + def cmd_nessus_scan_launch(*args) + if args[0] == "-h" + print_status("nessus_scan_launch ") + print_status("Use nessus_scan_list to list all the availabla scans with their corresponding scan IDs") + end + if !nessus_verify_token + return + end + case args.length + when 1 + scan_id = args[0] + else + print_status("Usage: ") + print_status("nessus_scan_launch ") + print_status("Use nessus_scan_list to list all the availabla scans with their corresponding scan IDs") + return + end + launch = @n.scan_launch(scan_id) + print_good("Scan ID #{scan_id} successfully launched. The Scan UUID is #{launch['scan_uuid']}") + end + + def cmd_nessus_scan_pause(*args) + if args[0] == "-h" + print_status("nessus_scan_pause ") + print_status("Example:> nessus_scan_pause f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("Pauses a running scan") + print_status("Use nessus_scan_list to list all available scans") + return + end + if !nessus_verify_token + return + end + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status("nessus_scan_pause ") + print_status("Use nessus_scan_list to list all available scans") + return + end + pause = @n.scan_pause(sid) + if pause["error"] + print_error "Invalid scan ID" + else + print_status("#{sid} has been paused") + end + end + + def cmd_nessus_db_scan(*args) + if args[0] == "-h" + print_status("nessus_db_scan ") + print_status() + print_status("Creates a scan based on all the hosts listed in db_hosts.") + print_status("Use nessus_policy_list to list all available policies with their corresponding policy IDs") + return + end + if !nessus_verify_db + return + end + if !nessus_verify_token + return + end + case args.length + when 3 + policy_id = args[0] + name = args[1] + desc = args[3] + else + print_status("Usage: ") + print_status("nessus_db_scan ") + print_status("Use nessus_policy_list to list all available policies with their corresponding policy IDs") + return + end + if !valid_policy(policy_id) + print_error("That policy does not exist.") + return + end + targets = "" + framework.db.hosts(framework.db.workspace).each do |host| + targets << host.address + targets << "," + end + targets.chop! + print_status("Creating scan from policy #{policy_id}, called \"#{name}\" and scanning all hosts in all the workspaces") + scan = @n.scan_create(policy_id, name, desc, targets) + if !scan["error"] + scan = scan["scan"] + print_status("Scan ID #{scan['id']} successfully created") + print_status("Run nessus_scan_launch #{scan['id']} to launch the scan") + else + print_error(JSON.pretty_generate(scan)) + end + end + + def cmd_nessus_db_import(*args) + if args[0] == "-h" + print_status("nessus_db_import ") + print_status("Example:> nessus_db_import 500") + print_status() + print_status("Use nessus_scan_list -c to list all completed scans") + end + if !nessus_verify_db + return + end + if !nessus_verify_token + return + end + case args.length + when 1 + scan_id = args[0] + else + print_status("Usage: ") + print_status("nessus_db_import ") + print_status("Example:> nessus_db_import 500") + print_status() + print_status("Use nessus_scan_list -c to list all completed scans") + end + if is_scan_complete(scan_id) + print_status("Exporting scan ID #{scan_id} is Nessus format...") + export = @n.scan_export(scan_id, 'nessus') + if export["file"] + file_id = export["file"] + print_good("The export file ID for scan ID #{scan_id} is #{file_id}") + print_status("Checking export status...") + status = @n.scan_export_status(scan_id, file_id) + if status == "ready" + print_status("The status of scan ID #{scan_id} export is ready") + select(nil, nil, nil, 5) + report = @n.report_download(scan_id, file_id) + print_status("Importing scan results to the database...") + framework.db.import({:data => report}) do |type,data| + case type + when :address + print_status("Importing data of #{data}") + end + end + print_good("Done") + else + print_error("There was some problem in exporting the scan. The error message is #{status}") + end + else + print_error(export) + end + else + print_error("Only completed scans could be used for import") + end + + end + + def is_scan_complete(scan_id) + complete = false + status = @n.scan_list + status["scans"].each { |scan| + if scan["id"] == scan_id.to_i && scan["status"] == "completed" + complete = true + end + } + complete + end + + def cmd_nessus_scan_pause_all(*args) + scan_ids = Array.new + if args[0] == "-h" + print_status("nessus_scan_pause_all") + print_status("Example:> nessus_scan_pause_all") + 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 + return + end + list = @n.scan_list + list["scans"].each { |scan| + if scan["status"] == "running" + scan_ids << scan["id"] + end + } + if scan_ids.length > 0 + scan_ids.each { |scan_id| + @n.scan_pause(scan_id) + } + print_status("All scans have been paused") + else + print_error("No running scans") + end + end + + def cmd_nessus_scan_stop(*args) + if args[0] == "-h" + print_status("nessus_scan_stop ") + print_status("Example:> nessus_scan_stop f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + 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 + return + end + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status("nessus_scan_stop ") + print_status("Use nessus_scan_list to list all available scans") + return + end + stop = @n.scan_stop(sid) + if stop["error"] + print_error "Invalid scan ID" + else + print_status("#{sid} has been stopped") + end + end + + def cmd_nessus_scan_stop_all(*args) + scan_ids = Array.new + if args[0] == "-h" + print_status("nessus_scan_stop_all") + print_status("Example:> nessus_scan_stop_all") + 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 + return + end + list = @n.scan_list + list["scans"].each { |scan| + if scan["status"] == "running" || scan["status"] == "paused" + scan_ids << scan["id"] + end + } + if scan_ids.length > 0 + scan_ids.each { |scan_id| + @n.scan_stop(scan_id) + } + print_status("All scans have been stopped") + else + print_error("No running or paused scans to be stopped") + end + end + + def cmd_nessus_scan_resume(*args) + if args[0] == "-h" + print_status("nessus_scan_resume ") + print_status("Example:> nessus_scan_resume f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca") + print_status() + print_status("resumes a running scan") + print_status("Use nessus_scan_list to list all available scans") + return + end + if !nessus_verify_token + return + end + case args.length + when 1 + sid = args[0] + else + print_status("Usage: ") + print_status("nessus_scan_resume ") + print_status("Use nessus_scan_list to list all available scans") + return + end + resume = @n.scan_resume(sid) + if resume["error"] + print_error "Invalid scan ID" + else + print_status("#{sid} has been resumed") + end + end + + def cmd_nessus_scan_resume_all(*args) + scan_ids = Array.new + if args[0] == "-h" + print_status("nessus_scan_resume_all") + print_status("Example:> nessus_scan_resume_all") + 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 + return + end + list = @n.scan_list + list["scans"].each { |scan| + if scan["status"] == "paused" + scan_ids << scan["id"] + end + } + if scan_ids.length > 0 + scan_ids.each { |scan_id| + @n.scan_resume(scan_id) + } + print_status("All scans have been resumed") + else + print_error("No running scans to be resumed") + end + end + + def cmd_nessus_scan_details(*args) + if args[0] == "-h" + print_status("nessus_scan_details ") + print_status("Availble categories are info, hosts, vulnerabilities, and history") + print_status("Use nessus_scan_list to list all available scans with their corresponding scan IDs") + return + end + if !nessus_verify_token + return + end + case args.length + when 2 + scan_id = args[0] + category = args[1] + if category.in?(['info', 'hosts', 'vulnerabilities', 'history']) + category = args[1] + else + print_error("Invalid category. The available categories are info, hosts, vulnerabilities, and history") + return + end + else + print_status("Usage: ") + print_status("nessus_scan_details ") + print_status("Availble categories are info, hosts, vulnerabilities, and history") + print_status("Use nessus_scan_list to list all available scans with their corresponding scan IDs") + return + end + details = @n.scan_details(scan_id) + if category == "info" + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Status", + "Policy", + "Scan Name", + "Scan Targets", + "Scan Start Time", + "Scan End Time" + ]) + tbl << [ details["info"]["status"], details["info"]["policy"], details["info"]["name"], details["info"]["targets"], details["info"]["scan_start"], details["info"]["scan_end"] ] + elsif category == "hosts" + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Host ID", + "Hostname", + "% of Critical Findings", + "% of High Findings", + "% of Medium Findings", + "% of Low Findings" + ]) + details["hosts"].each { |host| + tbl << [ host["host_id"], host["hostname"], host["critical"], host["high"], host["medium"], host["low"] ] + } + elsif category == "vulnerabilities" + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "Plugin ID", + "Plugin Name", + "Plugin Family", + "Count" + ]) + details["vulnerabilities"].each { |vuln| + tbl << [ vuln["plugin_id"], vuln["plugin_name"], vuln["plugin_family"], vuln["count"] ] + } + elsif category == "history" + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + "History ID", + "Status", + "Creation Date", + "Last Modification Date" + ]) + details["history"].each { |hist| + tbl << [ hist["history_id"], hist["status"], hist["creation_date"], hist["modification_date"] ] + } + end + print_line tbl.to_s + end + + def cmd_nessus_scan_export(*args) + if args[0] == "-h" + print_status("nessus_scan_export ") + print_status("The available export formats are Nessus, HTML, PDF, CSV, or DB") + print_status("Use nessus_scan_list to list all available scans with their corresponding scan IDs") + return + end + if !nessus_verify_token + return + end + case args.length + when 2 + scan_id = args[0] + format = args[1].downcase + else + print_status("Usage: ") + print_status("nessus_scan_export ") + print_status("The available export formats are Nessus, HTML, PDF, CSV, or DB") + print_status("Use nessus_scan_list to list all available scans with their corresponding scan IDs") + return + end + if format.in?(['nessus','html','pdf','csv','db']) + export = @n.scan_export(scan_id, format) + if export["file"] + file_id = export["file"] + print_good("The export file ID for scan ID #{scan_id} is #{file_id}") + print_status("Checking export status...") + status = @n.scan_export_status(scan_id, file_id) + if status == "ready" + print_good("The status of scan ID #{scan_id} export is ready") + else + print_error("There was some problem in exporting the scan. The error message is #{status}") + end + else + print_error(export) + end + else + print_error("Invalid export format. The available export formats are Nessus, HTML, PDF, CSV, or DB") + return + end + end + + def cmd_nessus_scan_export_status(*args) + if args[0] == "-h" + print_status("nessus_scan_export_status ") + print_status("Use nessus_scan_export to export a scan and get its file ID") + end + if !nessus_verify_token + return + end + case args.length + when 2 + scan_id = args[0] + file_id = args[1] + status = @n.scan_export_status(scan_id, file_id) + if status == "ready" + print_status("The status of scan ID #{scan_id} export is ready") + else + print_error("There was some problem in exporting the scan. The error message is #{status}") + end + else + print_status("Usage: ") + print_status("nessus_scan_export_status ") + print_status("Use nessus_scan_export to export a scan and get its file ID") + end + end + + def cmd_nessus_plugin_list(*args) + if args[0] == "-h" + print_status("nessus_plugin_list ") + print_status("Example:> nessus_plugin_list 10") + print_status() + print_status("Returns a list of all plugins in that family.") + print_status("Use nessus_family_list to display all the plugin families along with their corresponding family IDs") + return + end + if !nessus_verify_token + return + end + case args.length + when 1 + family_id = args[0] + else + print_status("Usage: ") + print_status("nessus_plugin_list ") + print_status("Use nessus_family_list to display all the plugin families along with their corresponding family IDs") + return + end + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Plugin ID', + 'Plugin Name' + ]) + list = @n.list_plugins(family_id) + list["plugins"].each { |plugin| + tbl << [ plugin["id"], plugin["name"] ] + } + print_line + print_good("Plugin Family Name: #{list['name']}") + print_line + print_line tbl.to_s + end + + def cmd_nessus_family_list(*args) + if args[0] == "-h" + print_status("nessus_family_list") + print_status("Example:> nessus_family_list") + print_status() + print_status("Returns a list of all the plugin families along with their corresponding family IDs and plugin count.") + return + end + list = @n.list_families + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Family ID', + 'Family Name', + 'Number of Plugins' + ]) + list.each { |family| + tbl << [ family["id"], family["name"], family["count"] ] + } + print_line + print_line tbl.to_s + end + + def cmd_nessus_plugin_details(*args) + if args[0] == "-h" + print_status("nessus_plugin_details ") + print_status("Example:> nessus_plugin_details 10264") + print_status() + print_status("Returns details on a particular plugin.") + print_status("Use nessus_plugin_list to list all plugins and their corresponding plugin IDs belonging to a particular plugin family.") + return + end + if !nessus_verify_token + return + end + case args.length + when 1 + plugin_id = args[0] + else + print_status("Usage: ") + print_status("nessus_plugin_details ") + print_status("Use nessus_plugin_list to list all plugins and their corresponding plugin IDs belonging to a particular plugin family.") + return + end + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'Reference', + 'Value' + ]) + begin + list = @n.plugin_details(plugin_id) + rescue ::Exception => e + if e.message =~ /unexpected token/ + print_error("No plugin info found") + return + else + raise e + end + end + list["attributes"].each { |attrib| + tbl << [ attrib["attribute_name"], attrib["attribute_value"] ] + } + print_line + print_good("Plugin Name: #{list['name']}") + print_good("Plugin Family: #{list['family_name']}") + print_line + print_line tbl.to_s + end + + def cmd_nessus_user_list(*args) + if args[0] == "-h" + print_status("nessus_user_list") + 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 + return + end + if !@n.is_admin + print_status("Your Nessus user is not an admin") + end + list=@n.list_users + tbl = Rex::Ui::Text::Table.new( + 'Columns' => [ + 'ID', + 'Name', + 'Username', + 'Type', + 'Email', + 'Permissions' + ]) + list["users"].each { |user| + tbl << [ user["id"], user["name"], user["username"], user["type"], user["email"], user["permissions"] ] + } + print_line + print_line tbl.to_s + end + + def cmd_nessus_user_add(*args) + if args[0] == "-h" + print_status("nessus_user_add ") + print_status("Permissions are 32, 64, and 128") + print_status("Type can be either local or LDAP") + print_status("Example:> nessus_user_add msf msf 16 local") + print_status("You need to be an admin in order to add accounts") + print_status("Use nessus_user_list to list all users") + return + end + if !nessus_verify_token + return + end + if !@n.is_admin print_error("Your Nessus user is not an admin") return end + case args.length + when 4 + user = args[0] + pass = args[1] + permissions = args[2] + type = args[3] + else + print_status("Usage") + print_status("nessus_user_add ") + return + end + add = @n.user_add(user,pass,permissions,type) + if add["id"] + print_good("#{user} created successfully") + else + print_error(add.to_s) + end + end + + def cmd_nessus_user_del(*args) + if args[0] == "-h" + print_status("nessus_user_del ") + print_status("Example:> nessus_user_del 10") + print_status() + print_status("This command can only delete non admin users. You must be an admin to delete users.") + print_status("Use nessus_user_list to list all users with their corresponding user IDs") + return + end + if !nessus_verify_token + return + end + if !@n.is_admin + print_error("Your Nessus user is not an admin") + return + end + case args.length + when 1 + user_id = args[0] + else + print_status("Usage: ") + print_status("nessus_user_del ") + print_status("This command can only delete non admin users") + return + end + del = @n.user_delete(user_id) + status = del.to_s + if status == "200" + print_good("User account having user ID #{user_id} deleted successfully") + elsif status == "403" + print_error("You do not have permission to delete the user account having user ID #{user_id}") + elsif status == "404" + print_error("User account having user ID #{user_id} does not exist") + elsif status == "409" + print_error("You cannot delete your own account") + elsif status == "500" + print_error("The server failed to delete the user account having user ID #{user_id}") + else + print_error("Unknown problem occured by deleting the user account having user ID #{user_id}.") + end + end + + def cmd_nessus_user_passwd(*args) + if args[0] == "-h" + print_status("nessus_user_passwd ") + print_status("Example:> nessus_user_passwd 10 mynewpassword") + print_status("Changes the password of a user. You must be an admin to change passwords.") + print_status("Use nessus_user_list to list all users with their corresponding user IDs") + return + end + if !nessus_verify_token + return + end + if !@n.is_admin + print_error("Your Nessus user is not an admin") + return + end + case args.length + when 2 + user_id = args[0] + pass = args[1] + else + print_status("Usage: ") + print_status("nessus_user_passwd ") + print_status("Use nessus_user_list to list all users with their corresponding user IDs") + return + end + pass = @n.user_chpasswd(user_id,pass) + status = pass.to_s + if status == "200" + print_good("Password of account having user ID #{user_id} changed successfully") + elsif status == "400" + print_error("Password is too short") + elsif status == "403" + print_error("You do not have the permission to change password for the user having user ID #{user_id}") + elsif status == "404" + print_error("User having user ID #{user_id} does not exist") + elsif status == "500" + print_error("Nessus server failed to changed the user password") + else + print_error("Unknown problem occured while changing the user password") + end + end + + def cmd_nessus_policy_list(*args) + if args[0] == "-h" + print_status("nessus_policy_list") + print_status("Example:> nessus_policy_list") + print_status() + print_status("Lists all policies on the server") + return + end + if !nessus_verify_token + return + end + list=@n.list_policies + + unless list["policies"] + print_error("No policies found") + return + end tbl = Rex::Ui::Text::Table.new( 'Columns' => [ + 'Policy ID', 'Name', - 'Value', - 'Type' + 'Policy UUID' ]) - prefs = @n.plugin_prefs - prefs.each {|pref| - tbl << [ pref['prefname'], pref['prefvalues'], pref['preftype'] ] + list["policies"].each { |policy| + tbl << [ policy["id"], policy["name"], policy["template_uuid"] ] } - print_good("Nessus Plugins Pref List") - print_good "\n" print_line tbl.to_s end + + def cmd_nessus_policy_del(*args) + if args[0] == "-h" + print_status("nessus_policy_del ") + print_status("Example:> nessus_policy_del 1") + print_status() + print_status("You must be an admin to delete policies.") + print_status("Use nessus_policy_list to list all policies with their corresponding policy IDs") + return + end + if !nessus_verify_token + return + end + if !@n.is_admin + print_error("Your Nessus user is not an admin") + return + end + case args.length + when 1 + policy_id = args[0] + else + print_status("Usage: ") + print_status("nessus_policy_del ") + print_status("Use nessus_policy_list to list all the policies with their corresponding policy IDs") + return + end + del = @n.policy_delete(policy_id) + status = del.to_s + if status == "200" + print_good("Policy ID #{policy_id} successfully deleted") + elsif status == "403" + print_error("You do not have permission to delete policy ID #{policy_id}") + elsif status == "404" + print_error("Policy ID #{policy_id} does not exist") + elsif status == "405" + print_error("Policy ID #{policy_id} is currently in use and cannot be deleted") + else + print_error("Unknown problem occured by deleting the user account having user ID #{user_id}.") + end + end + + def valid_policy(*args) + case args.length + when 1 + pid = args[0] + else + print_error("No Policy ID supplied.") + return + end + pol = @n.list_policies + pol["policies"].each { |p| + if p["template_uuid"] == pid + return true + end + } + return false + end + + def nessus_verify_db + if !(framework.db and framework.db.active) + print_error("No database has been configured, please use db_create/db_connect first") + return false + end + true + end end def initialize(framework, opts) super - add_console_dispatcher(ConsoleCommandDispatcher) - @nbver = "1.1" # Nessus Plugin Version. Increments each time we commit to msf - @xindex = "#{Msf::Config.get_config_root}/nessus_index" # location of the exploit index file used to speed up searching for valid exploits. - @nessus_yaml = "#{Msf::Config.get_config_root}/nessus.yaml" #location of the nessus.yml containing saved nessus creds - print_status("Nessus Bridge for Metasploit #{@nbver}") - print_good("Type %bldnessus_help%clr for a command listing") - #nessus_index + print_status(PLUGIN_DESCRIPTION) + print_status("Type %bldnessus_help%clr for a command listing") end def cleanup remove_console_dispatcher('Nessus') end - - def name - "nessus" - end - - def desc - "Nessus Bridge for Metasploit #{@nbver}" - end - protected end end