# $Id$ $Revision$ require 'nessus_rest' module Msf PLUGIN_NAME = 'Nessus'.freeze PLUGIN_DESCRIPTION = 'Nessus Bridge for Metasploit'.freeze class Plugin::Nessus < Msf::Plugin def name PLUGIN_NAME end def desc PLUGIN_DESCRIPTION end class ConsoleCommandDispatcher include Msf::Ui::Console::CommandDispatcher def name PLUGIN_NAME end def xindex "#{Msf::Config.config_directory}/nessus_index" end def nessus_yaml "#{Msf::Config.config_directory}/nessus.yaml" end def msf_local Msf::Config.local_directory.to_s 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_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_scan_workspace' => 'Create a scan of all IP addresses in db_hosts for a given workspace', '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 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.") # Use Msf::Config.config_directory as the location. File.open(xindex.to_s, 'w+') do |f| # need to add version line. f.puts(Msf::Framework::Version) framework.exploits.each_module do |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 !o o.references.map do |x| if x.ctx_id != 'URL' if (x.ctx_id == 'MSB') stuff << "|#{x.ctx_val}" else stuff << "|#{x.ctx_id}-#{x.ctx_val}" end end end stuff << "\n" f.puts(stuff) end 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.to_s) # check if it's version line matches current version. File.open(xindex.to_s) do |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 else create_xindex end end def nessus_login if !((@user && !@user.empty?) && (@host && !@host.empty?) && (@port && !@port.empty? && (@port.to_i > 0)) && (@pass && !@pass.empty?)) print_status('You need to connect to a server first.') ncusage return end @url = "https://#{@host}:#{@port}/" print_status("Connecting to #{@url} as #{@user}") verify_ssl = false if @sslv == 'verify_ssl' verify_ssl = true end @n = NessusREST::Client.new(url: @url, username: @user, password: @pass, ssl_verify: verify_ssl) if @n.authenticated print_status("User #{@user} authenticated successfully.") @token = 1 else print_error('Error connecting/logging to the server!') return end end def nessus_verify_token if @token.nil? || (@token == '') ncusage return false end true 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 do |p| if p['template_uuid'] == pid return true end end return false end def nessus_verify_db if !(framework.db && framework.db.active) print_error('No database has been configured, please use db_connect first') return false end true end def check_scan(*args) case args.length when 1 scan_id = args[0] else print_error('No scan ID supplied') return end scans = @n.scan_list scans.each do |scan| if scan['scans']['id'] == scan_id && scan['scans']['status'] == 'completed' return true end end return false end def is_scan_complete(scan_id) complete = false status = @n.scan_list status['scans'].each do |scan| if scan['id'] == scan_id.to_i && (scan['status'] == 'completed' || scan['status'] == 'imported') complete = true end end complete end def cmd_nessus_help(*_args) tbl = Rex::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_scan_workspace', 'Create a scan of all IP addresses in db_hosts for a given workspace' ] 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_launch', '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 cmd_nessus_index nessus_index end def cmd_nessus_connect(*args) # Check if config file exists and load it if !args[0] if File.exist?(nessus_yaml) lconfig = YAML.load_file(nessus_yaml) @user = lconfig['default']['username'].to_s @pass = lconfig['default']['password'].to_s @host = lconfig['default']['server'].to_s @port = lconfig['default']['port'].to_s nessus_login else ncusage end return 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('%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 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 authenticating again') return end if (args.empty? || args[0].empty?) ncusage return end @user = @pass = @host = @port = @sslv = nil case args.length when 1, 2 if args[0].include? '@' cred, _split, targ = args[0].rpartition('@') @user, @pass = cred.split(':', 2) targ ||= '127.0.0.1:8834' @host, @port = targ.split(':', 2) else @host, @port = args[0].split(':', 2) end @port ||= '8834' @sslv = args[1] when 3, 4, 5 ncusage return else ncusage return end if %r{//}.match(@host) ncusage return end if !@user print_error('Missing Username') ncusage return end if !@pass print_error('Missing Password') ncusage return end if !((@user && !@user.empty?) && (@host && !@host.empty?) && (@port && !@port.empty? && (@port.to_i > 0)) && (@pass && !@pass.empty?)) ncusage return end nessus_login end 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 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_save') return end group = 'default' if ((@user && !@user.empty?) && (@host && !@host.empty?) && (@port && !@port.empty? && (@port.to_i > 0)) && (@pass && !@pass.empty?)) config = Hash.new config = { group.to_s => { 'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port } } File.open(nessus_yaml.to_s, '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_server_properties(*args) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_server_properties') print_status('Example:> nessus_server_properties -S searchterm') print_status('Returns information about the feed type and server version.') return when '-S', '--search' search_term = /#{args.shift}/nmi end end resp = @n.server_properties tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Feed', 'Type', 'Nessus Version', 'Nessus Web Version', 'Plugin Set', 'Server UUID' ] ) 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) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_server_status') print_status('Example:> nessus_server_status -S searchterm') print_status('Returns some status items for the server..') return when '-S', '--search' search_term = /#{args.shift}/nmi end end tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Status', 'Progress' ] ) list = @n.server_status tbl << [ list['progress'], list['status'] ] print_line tbl.to_s end def cmd_nessus_admin(*args) while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_admin') print_status('Example:> nessus_admin') print_status('Checks to see if the current user is an admin') print_status('Use nessus_user_list to list all users') return end 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_template_list(*args) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_template_list | ') print_status('Example:> nessus_template_list scan -S searchterm') print_status('OR') print_status('nessus_template_list policy') print_status('Returns a list of information about the scan or policy templates..') return when '-S', '--search' search_term = /#{args.shift}/nmi else type = arg end end if !nessus_verify_token return end if type.in?(['scan', 'policy']) list = @n.list_templates(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::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Name', 'Title', 'Description', 'Subscription Only', 'Cloud Only' ] ) list['templates'].each do |template| tbl << [ template['name'], template['title'], template['desc'], template['subscription_only'], template['cloud_only'] ] end print_line print_line tbl.to_s end def cmd_nessus_folder_list(*args) search_term = nil while (arg = args.shift) case arg when '-S', '--search' search_term = /#{args.shift}/nmi end end if !nessus_verify_token return end list = @n.list_folders tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'ID', 'Name', 'Type' ] ) list['folders'].each do |folder| tbl << [ folder['id'], folder['name'], folder['type'] ] end print_line print_line tbl.to_s end def cmd_nessus_scanner_list(*args) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_scanner_list') print_status('Example:> nessus_scanner_list -S searchterm') print_status('Returns information about the feed type and server version.') return when '-S', '--search' search_term = /#{args.shift}/nmi end end if !nessus_verify_token return end if !@n.is_admin return end list = @n.list_scanners tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'ID', 'Name', 'Status', 'Platform', 'Plugin Set', 'UUID' ] ) list.each do |scanner| tbl << [ scanner['id'], scanner['name'], scanner['status'], scanner['platform'], scanner['loaded_plugin_set'], scanner['uuid'] ] end print_line tbl.to_s end def cmd_nessus_report_hosts(*args) search_term = nil scan_id = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_report_hosts -S searchterm') print_status('Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.') return when '-S', '--search' search_term = /#{args.shift}/nmi else scan_id = arg end end if scan_id.nil? print_status('Usage: ') print_status('nessus_report_hosts -S searchterm') print_status('Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.') return end tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Host ID', 'Hostname', '% of Critical Findings', '% of High Findings', '% of Medium Findings', '% of Low Findings' ] ) if is_scan_complete(scan_id) details = @n.scan_details(scan_id) details['hosts'].each do |host| tbl << [ host['host_id'], host['hostname'], host['critical'], host['high'], host['medium'], host['low'] ] end 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) search_term = nil scan_id = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_report_vulns -S searchterm') print_status('Use nessus_scan_list to get a list of all the scans. Only completed scans can be reported.') return when '-S', '--search' search_term = /#{args.shift}/nmi else scan_id = arg end end if scan_id.nil? print_status('Usage: ') 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::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Plugin ID', 'Plugin Name', 'Plugin Family', 'Vulnerability Count' ] ) if is_scan_complete(scan_id) details = @n.scan_details(scan_id) details['vulnerabilities'].each do |vuln| tbl << [ vuln['plugin_id'], vuln['plugin_name'], vuln['plugin_family'], vuln['count'] ] end print_line print_line tbl.to_s else print_error('Only completed scans can be used for vulnerability reporting') end return end def cmd_nessus_report_host_details(*args) search_term = nil search_vuln = nil scan_id = nil host_id = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_report_host_details ') print_status('Example:> nessus_report_host_details 10 5 -S hostinfo -SV vulninfo') 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 when '-S', '--search' search_term = /#{args.shift}/nmi when '-SV', '--search-vuln' search_vuln = /#{args.shift}/nmi else scan_id = arg, host_id = args.shift end end if [scan_id, host_id].any?(&:nil?) 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::Text::Table.new( 'SearchTerm' => search_term, '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 do |vuln| tbl << [ vuln['plugin_name'], vuln['plugin_family'], vuln['severity'] ] end print_line tbl.to_s tbl2 = Rex::Text::Table.new( 'SearchTerm' => search_vuln, 'Columns' => [ 'Plugin Name', 'Plugin Famil', 'Severity' ] ) print_status('Compliance information') details['compliance'].each do |comp| tbl2 << [ comp['plugin_name'], comp['plugin_family'], comp['severity'] ] end 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 can 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) search_term = nil rid = nil host = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_report_host_ports ') print_status('Example:> nessus_report_host_ports 192.168.1.250 f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca -S searchterm') 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') return when '-S', '--search' search_term = /#{args.shift}/nmi else scan_id = arg end end if [host, rid].any?(&:nil?) print_status('Usage: ') print_status('nessus_report_host_ports ') print_status('Use nessus_report_list to list all available reports') return end tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Port', 'Protocol', 'Severity', 'Service Name', 'Sev 0', 'Sev 1', 'Sev 2', 'Sev 3' ] ) ports = @n.report_host_ports(rid, host) ports.each do |port| tbl << [ port['portnum'], port['protocol'], port['severity'], port['svcname'], port['sev0'], port['sev1'], port['sev2'], port['sev3'] ] end print_good('Host Info') print_good "\n" print_line tbl.to_s print_status('You can:') print_status('Get detailed scan information about a specific port: nessus_report_host_detail ') end def cmd_nessus_report_del(*args) if args[0] == '-h' print_status('nessus_report_del ') print_status('Example:> nessus_report_del f0eabba3-4065-7d54-5763-f191e98eb0f7f9f33db7e75a06ca') print_status('Must be an admin to del reports.') print_status('Use nessus_report_list to list all reports') return end if !nessus_verify_token return end 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.') 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 end def cmd_nessus_scan_list(*args) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_scan_list') print_status('Example:> nessus_scan_list -S searchterm') print_status('Returns a list of information about currently running scans.') return when '-S', '--search' search_term = /#{args.shift}/nmi end end if !nessus_verify_token return end list = @n.scan_list if list.to_s.empty? print_status('No scans performed.') return else tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Scan ID', 'Name', 'Owner', 'Started', 'Status', 'Folder' ] ) list['scans'].each do |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 end print_line tbl.to_s end end 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}") et = { 'enabled' => false, 'launch' => 'ONETIME', 'name' => scan_name, 'text_targets' => targets, 'description' => description, 'launch_now' => false } scan = @n.scan_create(uuid, et) tbl = Rex::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('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('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.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") et = { 'enabled' => false, 'launch' => 'ONETIME', 'name' => name, 'text_targets' => targets, 'description' => desc, 'launch_now' => true } scan = @n.scan_create(policy_id, et) if !scan['error'] scan = scan['scan'] print_status("Scan ID #{scan['id']} successfully created and launched") else print_error(JSON.pretty_generate(scan)) end end def cmd_nessus_db_scan_workspace(*args) if args[0] == '-h' print_status('nessus_db_scan_workspace ') print_status('Creates a scan based on all the hosts listed in db_hosts for a given workspace.') 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 4 policy_id = args[0] name = args[1] desc = args[2] new_workspace = framework.db.find_workspace(args[3]) else print_status('Usage: ') print_status('nessus_db_scan_workspace ') 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 if new_workspace.nil? print_error('That workspace does not exist.') return end framework.db.workspace = new_workspace print_status("Switched workspace: #{framework.db.workspace.name}") targets = '' framework.db.hosts.each do |host| targets << host.address targets << ',' print_status("Targets: #{targets}") end targets.chop! print_status("Creating scan from policy #{policy_id}, called \"#{name}\" and scanning all hosts in #{framework.db.workspace.name}") et = { 'enabled' => false, 'launch' => 'ONETIME', 'name' => name, 'text_targets' => targets, 'description' => desc, 'launch_now' => false } scan = @n.scan_create(policy_id, et) 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('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('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') status = {} 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...') loop do status = @n.scan_export_status(scan_id, file_id) print_status('Export status: ' + status['status']) if status['status'] == 'ready' break end sleep(1) break unless (status['status'] == 'loading') end if status['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 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('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 do |scan| if scan['status'] == 'running' scan_ids << scan['id'] end end if !scan_ids.empty? scan_ids.each do |scan_id| @n.scan_pause(scan_id) end 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('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('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 do |scan| if scan['status'] == 'running' || scan['status'] == 'paused' scan_ids << scan['id'] end end if !scan_ids.empty? scan_ids.each do |scan_id| @n.scan_stop(scan_id) end 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('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('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 do |scan| if scan['status'] == 'paused' scan_ids << scan['id'] end end if !scan_ids.empty? scan_ids.each do |scan_id| @n.scan_resume(scan_id) end print_status('All scans have been resumed') else print_error('No running scans to be resumed') end end def cmd_nessus_scan_details(*args) valid_categories = ['info', 'hosts', 'vulnerabilities', 'history'] search_term = nil scan_id = nil category = nil while (arg = args.shift) case arg when '-h', '--help' print_status('Usage: ') print_status('nessus_scan_details -S searchterm') 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 when '-S', '--search' search_term = /#{args.shift}/nmi else scan_id = arg if args[0].in?(valid_categories) category = args.shift else print_error('Invalid category. The available categories are info, hosts, vulnerabilities, and history') return end end end if !nessus_verify_token return end details = @n.scan_details(scan_id) if category == 'info' tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, '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::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Host ID', 'Hostname', '% of Critical Findings', '% of High Findings', '% of Medium Findings', '% of Low Findings' ] ) details['hosts'].each do |host| tbl << [ host['host_id'], host['hostname'], host['critical'], host['high'], host['medium'], host['low'] ] end elsif category == 'vulnerabilities' tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Plugin ID', 'Plugin Name', 'Plugin Family', 'Count' ] ) details['vulnerabilities'].each do |vuln| tbl << [ vuln['plugin_id'], vuln['plugin_name'], vuln['plugin_family'], vuln['count'] ] end elsif category == 'history' tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'History ID', 'Status', 'Creation Date', 'Last Modification Date' ] ) details['history'].each do |hist| tbl << [ hist['history_id'], hist['status'], hist['creation_date'], hist['modification_date'] ] end 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) status = {} 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...') loop do status = @n.scan_export_status(scan_id, file_id) print_status('Export status: ' + status['status']) if status['status'] == 'ready' break end sleep(1) break unless (status['status'] == 'loading') end if status['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 = {} loop do status = @n.scan_export_status(scan_id, file_id) print_status('Export status: ' + status['status']) if status['status'] == 'ready' break end sleep(1) break unless (status['status'] == 'loading') end if status['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) search_term = nil family_id = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_plugin_list -S searchterm') print_status('Example:> nessus_plugin_list 10') 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 when '-S', '--search' search_term = /#{args.shift}/nmi else family_id = arg end end if family_id.nil? 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::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Plugin ID', 'Plugin Name' ] ) list = @n.list_plugins(family_id) list['plugins'].each do |plugin| tbl << [ plugin['id'], plugin['name'] ] end print_line print_good("Plugin Family Name: #{list['name']}") print_line print_line tbl.to_s end def cmd_nessus_family_list(*args) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_family_list') print_status('Example:> nessus_family_list -S searchterm') print_status('Returns a list of all the plugin families along with their corresponding family IDs and plugin count.') return when '-S', '--search' search_term = /#{args.shift}/nmi end end list = @n.list_families tbl = Rex::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'Family ID', 'Family Name', 'Number of Plugins' ] ) list['families'].each do |family| tbl << [ family['id'], family['name'], family['count'] ] end print_line print_line tbl.to_s end def cmd_nessus_plugin_details(*args) search_term = nil plugin_id = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_plugin_details ') print_status('Example:> nessus_plugin_details 10264 -S searchterm') 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 when '-S', '--search' search_term = /#{args.shift}/nmi else plugin_id = arg end end if !nessus_verify_token return end if plugin_id.nil? 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::Text::Table.new( 'SearchTerm' => search_term, '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 do |attrib| tbl << [ attrib['attribute_name'], attrib['attribute_value'] ] end 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) scan_id = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_user_list') print_status('Example:> nessus_user_list -S searchterm') print_status('Returns a list of the users on the Nessus server and their access level.') return when '-S', '--search' search_term = /#{args.shift}/nmi end 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::Text::Table.new( 'SearchTerm' => search_term, 'Columns' => [ 'ID', 'Name', 'Username', 'Type', 'Email', 'Permissions' ] ) list['users'].each do |user| tbl << [ user['id'], user['name'], user['username'], user['type'], user['email'], user['permissions'] ] end 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('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 occurred 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 occurred while changing the user password') end end def cmd_nessus_policy_list(*args) search_term = nil while (arg = args.shift) case arg when '-h', '--help' print_status('nessus_policy_list') print_status('Example:> nessus_policy_list -S searchterm') print_status('Lists all policies on the server') return when '-S', '--search' search_term = /#{args.shift}/nmi end end if !nessus_verify_token return end list = @n.list_policies unless list['policies'] print_error('No policies found') return end tbl = Rex::Text::Table.new( 'Columns' => [ 'Policy ID', 'Name', 'Policy UUID' ] ) list['policies'].each do |policy| tbl << [ policy['id'], policy['name'], policy['template_uuid'] ] end 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('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 occurred by deleting the user account having user ID #{user_id}.") end end end def initialize(framework, opts) super add_console_dispatcher(ConsoleCommandDispatcher) print_status(PLUGIN_DESCRIPTION) print_status('Type %bldnessus_help%clr for a command listing') end def cleanup remove_console_dispatcher('Nessus') end end end