Merge branch 'master' into feature/aggregator
This commit is contained in:
+104
-75
@@ -5,8 +5,7 @@
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
require 'rapid7/nexpose'
|
||||
require 'nexpose'
|
||||
|
||||
module Msf
|
||||
Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds
|
||||
@@ -81,7 +80,7 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
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 = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port}}
|
||||
config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port, 'trust_cert' => @trust_cert}}
|
||||
::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) }
|
||||
print_good("#{Nexpose_yaml} created.")
|
||||
else
|
||||
@@ -100,21 +99,21 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
@pass = lconfig['default']['password']
|
||||
@host = lconfig['default']['server']
|
||||
@port = lconfig['default']['port']
|
||||
@sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning...
|
||||
@trust_cert = lconfig['default']['trust_cert']
|
||||
unless @trust_cert
|
||||
@sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning...
|
||||
end
|
||||
nexpose_login
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if(args.length == 0 or args[0].empty? or args[0] == "-h")
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
nexpose_usage
|
||||
return
|
||||
end
|
||||
|
||||
@user = @pass = @host = @port = @sslv = nil
|
||||
@user = @pass = @host = @port = @sslv = @trust_cert = @trust_cert_file = nil
|
||||
|
||||
case args.length
|
||||
when 1,2
|
||||
@@ -122,31 +121,48 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
@user,@pass = cred.split(':', 2)
|
||||
targ ||= '127.0.0.1:3780'
|
||||
@host,@port = targ.split(':', 2)
|
||||
port ||= '3780'
|
||||
@sslv = args[1]
|
||||
@port ||= '3780'
|
||||
unless args.length == 1
|
||||
@trust_cert_file = args[1]
|
||||
if File.exists? @trust_cert_file
|
||||
@trust_cert = File.read(@trust_cert_file)
|
||||
else
|
||||
@sslv = @trust_cert_file
|
||||
end
|
||||
end
|
||||
when 4,5
|
||||
@user,@pass,@host,@port,@sslv = args
|
||||
@user,@pass,@host,@port,@trust_cert = args
|
||||
unless args.length == 4
|
||||
@trust_cert_file = @trust_cert
|
||||
if File.exists? @trust_cert_file
|
||||
@trust_cert = File.read(@trust_cert_file)
|
||||
else
|
||||
@sslv = @trust_cert_file
|
||||
end
|
||||
end
|
||||
else
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
nexpose_usage
|
||||
return
|
||||
end
|
||||
nexpose_login
|
||||
end
|
||||
|
||||
def nexpose_usage
|
||||
print_status("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm || trusted_cert_file>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm || trusted_cert_file>")
|
||||
end
|
||||
|
||||
def nexpose_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("Usage: ")
|
||||
print_status(" nexpose_connect username:password@host[:port] <ssl-confirm>")
|
||||
print_status(" -OR- ")
|
||||
print_status(" nexpose_connect username password host port <ssl-confirm>")
|
||||
nexpose_usage
|
||||
return
|
||||
end
|
||||
|
||||
if(@host != "localhost" and @host != "127.0.0.1" and @sslv != "ok")
|
||||
if(@host != "localhost" and @host != "127.0.0.1" and (@trust_cert.nil? && @sslv != "ok"))
|
||||
# consider removing this message and replacing with check on trust_store, and if trust_store is not found validate @host already has a truly trusted cert?
|
||||
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 Nexpose traffic to capture the Nexpose")
|
||||
print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
|
||||
@@ -154,7 +170,7 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
return
|
||||
end
|
||||
|
||||
# Wrap this so a duplicate session doesnt prevent a new login
|
||||
# Wrap this so a duplicate session does not prevent a new login
|
||||
begin
|
||||
cmd_nexpose_disconnect
|
||||
rescue ::Interrupt
|
||||
@@ -164,7 +180,7 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
|
||||
begin
|
||||
print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...")
|
||||
nsc = ::Nexpose::Connection.new(@host, @user, @pass, @port)
|
||||
nsc = Nexpose::Connection.new(@host, @user, @pass, @port, nil, nil, @trust_cert)
|
||||
nsc.login
|
||||
rescue ::Nexpose::APIError => e
|
||||
print_error("Connection failed: #{e.reason}")
|
||||
@@ -190,21 +206,21 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
end
|
||||
|
||||
scans.each do |scan|
|
||||
print_status(" Scan ##{scan[:scan_id]} is running on Engine ##{scan[:engine_id]} against site ##{scan[:site_id]} since #{scan[:start_time].to_s}")
|
||||
print_status(" Scan ##{scan.scan_id} is running on Engine ##{scan.engine_id} against site ##{scan.site_id} since #{scan.start_time.to_s}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_sites(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
sites = @nsc.site_listing || []
|
||||
sites = @nsc.list_sites || []
|
||||
case sites.length
|
||||
when 0
|
||||
print_status("There are currently no active sites on this Nexpose instance")
|
||||
end
|
||||
|
||||
sites.each do |site|
|
||||
print_status(" Site ##{site[:site_id]} '#{site[:name]}' Risk Factor: #{site[:risk_factor]} Risk Score: #{site[:risk_score]}")
|
||||
print_status(" Site ##{site.id} '#{site.name}' Risk Factor: #{site.risk_factor} Risk Score: #{site.risk_score}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -217,24 +233,24 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
return
|
||||
end
|
||||
|
||||
devices = @nsc.site_device_listing(site_id) || []
|
||||
devices = @nsc.list_site_devices(site_id) || []
|
||||
case devices.length
|
||||
when 0
|
||||
print_status("There are currently no devices within this site")
|
||||
end
|
||||
|
||||
devices.each do |device|
|
||||
print_status(" Host: #{device[:address]} ID: #{device[:device_id]} Risk Factor: #{device[:risk_factor]} Risk Score: #{device[:risk_score]}")
|
||||
print_status(" Host: #{device.address} ID: #{device.id} Risk Factor: #{device.risk_factor} Risk Score: #{device.risk_score}")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_nexpose_report_templates(*args)
|
||||
return if not nexpose_verify
|
||||
|
||||
res = @nsc.report_template_listing || []
|
||||
res = @nsc.list_report_templates || []
|
||||
|
||||
res.each do |report|
|
||||
print_status(" Template: #{report[:template_id]} Name: '#{report[:name]}' Description: #{report[:description]}")
|
||||
print_status(" Template: #{report.id} Name: '#{report.name}' Description: #{report.description}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -286,17 +302,12 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
report_formats = ["raw-xml-v2", "ns-xml"]
|
||||
report_format = report_formats.shift
|
||||
|
||||
report = Nexpose::ReportConfig.new(@nsc)
|
||||
report.set_name("Metasploit Export #{msfid}")
|
||||
report.set_template_id("pentest-audit")
|
||||
|
||||
report.addFilter("SiteFilter", site_id)
|
||||
report.set_generate_after_scan(0)
|
||||
report.set_storeOnServer(1)
|
||||
report = Nexpose::ReportConfig.build(@nsc, site_id, "Metasploit Export #{msfid}", "pentest-audit", report_format, true)
|
||||
report.delivery = Nexpose::Delivery.new(true)
|
||||
|
||||
begin
|
||||
report.set_format(report_format)
|
||||
report.saveReport()
|
||||
report.format = report_format
|
||||
report.save(@nsc)
|
||||
rescue ::Exception => e
|
||||
report_format = report_formats.shift
|
||||
if report_format
|
||||
@@ -306,17 +317,18 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
end
|
||||
|
||||
print_status("Generating the export data file...")
|
||||
url = nil
|
||||
while(! url)
|
||||
url = @nsc.report_last(report.config_id)
|
||||
last_report = nil
|
||||
while(! last_report)
|
||||
last_report = @nsc.last_report(report.id)
|
||||
select(nil, nil, nil, 1.0)
|
||||
end
|
||||
url = last_report.uri
|
||||
|
||||
print_status("Downloading the export data...")
|
||||
data = @nsc.download(url)
|
||||
|
||||
# Delete the temporary report ID
|
||||
@nsc.report_config_delete(report.config_id)
|
||||
@nsc.delete_report_config(report.id)
|
||||
|
||||
print_status("Importing Nexpose data...")
|
||||
process_nexpose_data(report_format, data)
|
||||
@@ -389,8 +401,10 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
when "-c"
|
||||
if (val =~ /^([^:]+):([^:]+):(.+)/)
|
||||
type, user, pass = [ $1, $2, $3 ]
|
||||
newcreds = Nexpose::AdminCredentials.new
|
||||
newcreds.setCredentials(type, nil, nil, user, pass, nil)
|
||||
msfid = Time.now.to_i
|
||||
newcreds = Nexpose::SiteCredentials.for_service("Metasploit Site Credential #{msfid}", nil, nil, nil, nil, type)
|
||||
newcreds.user_name = user
|
||||
newcreds.password = pass
|
||||
opt_credentials << newcreds
|
||||
else
|
||||
print_error("Unrecognized Nexpose scan credentials: #{val}")
|
||||
@@ -481,33 +495,24 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
msfid = Time.now.to_i
|
||||
|
||||
# Create a temporary site
|
||||
site = Nexpose::Site.new(@nsc)
|
||||
site.setSiteConfig("Metasploit-#{msfid}", "Autocreated by the Metasploit Framework")
|
||||
queue.each do |ip|
|
||||
site.site_config.addHost(Nexpose::IPRange.new(ip))
|
||||
end
|
||||
site.site_config._set_scanConfig(Nexpose::ScanConfig.new(-1, "tmp", opt_template))
|
||||
opt_credentials.each do |c|
|
||||
site.site_config.addCredentials(c)
|
||||
end
|
||||
site.saveSite()
|
||||
site = Nexpose::Site.new(nil, opt_template)
|
||||
site.name = "Metasploit-#{msfid}"
|
||||
site.description = "Autocreated by the Metasploit Framework"
|
||||
site.included_addresses = queue
|
||||
site.site_credentials = opt_credentials
|
||||
site.save(@nsc)
|
||||
|
||||
print_status(" >> Created temporary site ##{site.site_id}") if opt_verbose
|
||||
print_status(" >> Created temporary site ##{site.id}") if opt_verbose
|
||||
|
||||
report_formats = ["raw-xml-v2", "ns-xml"]
|
||||
report_format = report_formats.shift
|
||||
|
||||
report = Nexpose::ReportConfig.new(@nsc)
|
||||
report.set_name("Metasploit Export #{msfid}")
|
||||
report.set_template_id(opt_template)
|
||||
|
||||
report.addFilter("SiteFilter", site.site_id)
|
||||
report.set_generate_after_scan(1)
|
||||
report.set_storeOnServer(1)
|
||||
report = Nexpose::ReportConfig.build(@nsc, site.id, site.name, opt_template, report_format, true)
|
||||
report.delivery = Nexpose::Delivery.new(true)
|
||||
|
||||
begin
|
||||
report.set_format(report_format)
|
||||
report.saveReport()
|
||||
report.format = report_format
|
||||
report.save(@nsc, true)
|
||||
rescue ::Exception => e
|
||||
report_format = report_formats.shift
|
||||
if report_format
|
||||
@@ -516,18 +521,19 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
raise e
|
||||
end
|
||||
|
||||
print_status(" >> Created temporary report configuration ##{report.config_id}") if opt_verbose
|
||||
print_status(" >> Created temporary report configuration ##{report.id}") if opt_verbose
|
||||
|
||||
# Run the scan
|
||||
begin
|
||||
res = site.scanSite()
|
||||
res = site.scan(@nsc)
|
||||
rescue Nexpose::APIError => e
|
||||
nexpose_error_message = e.message
|
||||
nexpose_error_message.gsub!(/NexposeAPI: Action failed: /, '')
|
||||
print_error "#{nexpose_error_message}"
|
||||
return
|
||||
end
|
||||
sid = res[:scan_id]
|
||||
|
||||
sid = res.id
|
||||
|
||||
print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose
|
||||
|
||||
@@ -536,8 +542,8 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
prev = nil
|
||||
while(true)
|
||||
info = @nsc.scan_statistics(sid)
|
||||
break if info[:summary]['status'] != "running"
|
||||
stat = "Found #{info[:nodes]['live']} devices and #{info[:nodes]['dead']} unresponsive"
|
||||
break if info.status != "running"
|
||||
stat = "Found #{info.nodes.live} devices and #{info.nodes.dead} unresponsive"
|
||||
if(stat != prev)
|
||||
print_status(" >> #{stat}") if opt_verbose
|
||||
end
|
||||
@@ -548,18 +554,19 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
rescue ::Interrupt
|
||||
rep = false
|
||||
print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose
|
||||
@nsc.scan_stop(sid)
|
||||
@nsc.stop_scan(sid)
|
||||
break
|
||||
end
|
||||
|
||||
# Wait for the automatic report generation to complete
|
||||
if(rep)
|
||||
print_status(" >> Waiting on the report to generate...") if opt_verbose
|
||||
url = nil
|
||||
while(! url)
|
||||
url = @nsc.report_last(report.config_id)
|
||||
last_report = nil
|
||||
while(! last_report)
|
||||
last_report = @nsc.last_report(report.id)
|
||||
select(nil, nil, nil, 1.0)
|
||||
end
|
||||
url = last_report.uri
|
||||
|
||||
print_status(" >> Downloading the report data from Nexpose...") if opt_verbose
|
||||
data = @nsc.download(url)
|
||||
@@ -575,8 +582,18 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
end
|
||||
|
||||
if ! opt_preserve
|
||||
# Make sure the scan has finished clean up before attempting to delete the site
|
||||
while (true)
|
||||
info = @nsc.scan_statistics(sid)
|
||||
break if info.status == 'stopped' || info.status == 'finished'
|
||||
select(nil, nil, nil, 5.0)
|
||||
end
|
||||
print_status(" >> Deleting the temporary site and report...") if opt_verbose
|
||||
@nsc.site_delete(site.site_id)
|
||||
begin
|
||||
@nsc.delete_site(site.id)
|
||||
rescue ::Nexpose::APIError => e
|
||||
print_status(" >> Deletion of temporary site and report failed: #{e.inspect}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -674,3 +691,15 @@ class Plugin::Nexpose < Msf::Plugin
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Nexpose
|
||||
class IPRange
|
||||
def to_json
|
||||
if @to.present?
|
||||
"#{@from} - #{@to}".to_json
|
||||
else
|
||||
@from.to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user