Merge branch 'master' into feature/aggregator

This commit is contained in:
Jeffrey Martin
2017-02-24 23:59:02 -06:00
167 changed files with 1184 additions and 2881 deletions
+104 -75
View File
@@ -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