Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nexpose.rb 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. #
  2. # $Id$
  3. #
  4. # This plugin provides integration with Rapid7 Nexpose
  5. #
  6. # $Revision$
  7. #
  8. require 'nexpose'
  9. module Msf
  10. Nexpose_yaml = "#{Msf::Config.get_config_root}/nexpose.yaml" #location of the nexpose.yml containing saved nexpose creds
  11. class Plugin::Nexpose < Msf::Plugin
  12. class NexposeCommandDispatcher
  13. include Msf::Ui::Console::CommandDispatcher
  14. def name
  15. "Nexpose"
  16. end
  17. def commands
  18. {
  19. 'nexpose_connect' => "Connect to a running Nexpose instance ( user:pass@host[:port] )",
  20. 'nexpose_save' => "Save credentials to a Nexpose instance",
  21. 'nexpose_activity' => "Display any active scan jobs on the Nexpose instance",
  22. 'nexpose_scan' => "Launch a Nexpose scan against a specific IP range and import the results",
  23. 'nexpose_discover' => "Launch a scan but only perform host and minimal service discovery",
  24. 'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks",
  25. 'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)",
  26. 'nexpose_disconnect' => "Disconnect from an active Nexpose instance",
  27. 'nexpose_sites' => "List all defined sites",
  28. 'nexpose_site_devices' => "List all discovered devices within a site",
  29. 'nexpose_site_import' => "Import data from the specified site ID",
  30. 'nexpose_report_templates' => "List all available report templates",
  31. 'nexpose_command' => "Execute a console command on the Nexpose instance",
  32. 'nexpose_sysinfo' => "Display detailed system information about the Nexpose instance",
  33. # TODO:
  34. # nexpose_stop_scan
  35. }
  36. end
  37. def nexpose_verify_db
  38. if ! (framework.db and framework.db.usable and framework.db.active)
  39. print_error("No database has been configured, please use db_create/db_connect first")
  40. return false
  41. end
  42. true
  43. end
  44. def nexpose_verify
  45. return false if not nexpose_verify_db
  46. if ! @nsc
  47. print_error("No active Nexpose instance has been configured, please use 'nexpose_connect'")
  48. return false
  49. end
  50. true
  51. end
  52. def cmd_nexpose_save(*args)
  53. #if we are logged in, save session details to nexpose.yaml
  54. if args[0] == "-h"
  55. print_status("Usage: ")
  56. print_status(" nexpose_save")
  57. return
  58. end
  59. if args[0]
  60. print_status("Usage: ")
  61. print_status(" nexpose_save")
  62. return
  63. end
  64. group = "default"
  65. 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))
  66. config = {"#{group}" => {'username' => @user, 'password' => @pass, 'server' => @host, 'port' => @port, 'trust_cert' => @trust_cert}}
  67. ::File.open("#{Nexpose_yaml}", "wb") { |f| f.puts YAML.dump(config) }
  68. print_good("#{Nexpose_yaml} created.")
  69. else
  70. print_error("Missing username/password/server/port - relogin and then try again.")
  71. return
  72. end
  73. end
  74. def cmd_nexpose_connect(*args)
  75. return if not nexpose_verify_db
  76. if ! args[0]
  77. if ::File.readable?("#{Nexpose_yaml}")
  78. lconfig = YAML.load_file("#{Nexpose_yaml}")
  79. @user = lconfig['default']['username']
  80. @pass = lconfig['default']['password']
  81. @host = lconfig['default']['server']
  82. @port = lconfig['default']['port']
  83. @trust_cert = lconfig['default']['trust_cert']
  84. unless @trust_cert
  85. @sslv = "ok" # TODO: Not super-thrilled about bypassing the SSL warning...
  86. end
  87. nexpose_login
  88. return
  89. end
  90. end
  91. if(args.length == 0 or args[0].empty? or args[0] == "-h")
  92. nexpose_usage
  93. return
  94. end
  95. @user = @pass = @host = @port = @sslv = @trust_cert = @trust_cert_file = nil
  96. case args.length
  97. when 1,2
  98. cred,targ = args[0].split('@', 2)
  99. @user,@pass = cred.split(':', 2)
  100. targ ||= '127.0.0.1:3780'
  101. @host,@port = targ.split(':', 2)
  102. @port ||= '3780'
  103. unless args.length == 1
  104. @trust_cert_file = args[1]
  105. if File.exists? @trust_cert_file
  106. @trust_cert = File.read(@trust_cert_file)
  107. else
  108. @sslv = @trust_cert_file
  109. end
  110. end
  111. when 4,5
  112. @user,@pass,@host,@port,@trust_cert = args
  113. unless args.length == 4
  114. @trust_cert_file = @trust_cert
  115. if File.exists? @trust_cert_file
  116. @trust_cert = File.read(@trust_cert_file)
  117. else
  118. @sslv = @trust_cert_file
  119. end
  120. end
  121. else
  122. nexpose_usage
  123. return
  124. end
  125. nexpose_login
  126. end
  127. def nexpose_usage
  128. print_status("Usage: ")
  129. print_status(" nexpose_connect username:password@host[:port] <ssl-confirm || trusted_cert_file>")
  130. print_status(" -OR- ")
  131. print_status(" nexpose_connect username password host port <ssl-confirm || trusted_cert_file>")
  132. end
  133. def nexpose_login
  134. 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))
  135. nexpose_usage
  136. return
  137. end
  138. if(@host != "localhost" and @host != "127.0.0.1" and (@trust_cert.nil? && @sslv != "ok"))
  139. # 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?
  140. print_error("Warning: SSL connections are not verified in this release, it is possible for an attacker")
  141. print_error(" with the ability to man-in-the-middle the Nexpose traffic to capture the Nexpose")
  142. print_error(" credentials. If you are running this on a trusted network, please pass in 'ok'")
  143. print_error(" as an additional parameter to this command.")
  144. return
  145. end
  146. # Wrap this so a duplicate session does not prevent a new login
  147. begin
  148. cmd_nexpose_disconnect
  149. rescue ::Interrupt
  150. raise $!
  151. rescue ::Exception
  152. end
  153. begin
  154. print_status("Connecting to Nexpose instance at #{@host}:#{@port} with username #{@user}...")
  155. nsc = Nexpose::Connection.new(@host, @user, @pass, @port, nil, nil, @trust_cert)
  156. nsc.login
  157. rescue ::Nexpose::APIError => e
  158. print_error("Connection failed: #{e.reason}")
  159. return
  160. end
  161. @nsc = nsc
  162. nexpose_compatibility_check
  163. nsc
  164. end
  165. def cmd_nexpose_activity(*args)
  166. return if not nexpose_verify
  167. scans = @nsc.scan_activity || []
  168. case scans.length
  169. when 0
  170. print_status("There are currently no active scan jobs on this Nexpose instance")
  171. when 1
  172. print_status("There is 1 active scan job on this Nexpose instance")
  173. else
  174. print_status("There are currently #{scans.length} active scan jobs on this Nexpose instance")
  175. end
  176. scans.each do |scan|
  177. print_status(" Scan ##{scan.scan_id} is running on Engine ##{scan.engine_id} against site ##{scan.site_id} since #{scan.start_time.to_s}")
  178. end
  179. end
  180. def cmd_nexpose_sites(*args)
  181. return if not nexpose_verify
  182. sites = @nsc.list_sites || []
  183. case sites.length
  184. when 0
  185. print_status("There are currently no active sites on this Nexpose instance")
  186. end
  187. sites.each do |site|
  188. print_status(" Site ##{site.id} '#{site.name}' Risk Factor: #{site.risk_factor} Risk Score: #{site.risk_score}")
  189. end
  190. end
  191. def cmd_nexpose_site_devices(*args)
  192. return if not nexpose_verify
  193. site_id = args.shift
  194. if not site_id
  195. print_error("No site ID was specified")
  196. return
  197. end
  198. devices = @nsc.list_site_devices(site_id) || []
  199. case devices.length
  200. when 0
  201. print_status("There are currently no devices within this site")
  202. end
  203. devices.each do |device|
  204. print_status(" Host: #{device.address} ID: #{device.id} Risk Factor: #{device.risk_factor} Risk Score: #{device.risk_score}")
  205. end
  206. end
  207. def cmd_nexpose_report_templates(*args)
  208. return if not nexpose_verify
  209. res = @nsc.list_report_templates || []
  210. res.each do |report|
  211. print_status(" Template: #{report.id} Name: '#{report.name}' Description: #{report.description}")
  212. end
  213. end
  214. def cmd_nexpose_command(*args)
  215. return if not nexpose_verify
  216. if args.length == 0
  217. print_error("No command was specified")
  218. return
  219. end
  220. res = @nsc.console_command(args.join(" ")) || ""
  221. print_status("Command Output")
  222. print_line(res)
  223. print_line("")
  224. end
  225. def cmd_nexpose_sysinfo(*args)
  226. return if not nexpose_verify
  227. res = @nsc.system_information
  228. print_status("System Information")
  229. res.each_pair do |k,v|
  230. print_status(" #{k}: #{v}")
  231. end
  232. end
  233. def nexpose_compatibility_check
  234. res = @nsc.console_command("ver")
  235. if res !~ /^(NSC|Console) Version ID:\s*4[89]0\s*$/m
  236. print_error("")
  237. print_error("Warning: This version of Nexpose has not been tested with Metasploit!")
  238. print_error("")
  239. end
  240. end
  241. def cmd_nexpose_site_import(*args)
  242. site_id = args.shift
  243. if not site_id
  244. print_error("No site ID was specified")
  245. return
  246. end
  247. msfid = Time.now.to_i
  248. report_formats = ["raw-xml-v2", "ns-xml"]
  249. report_format = report_formats.shift
  250. report = Nexpose::ReportConfig.build(@nsc, site_id, "Metasploit Export #{msfid}", "pentest-audit", report_format, true)
  251. report.delivery = Nexpose::Delivery.new(true)
  252. begin
  253. report.format = report_format
  254. report.save(@nsc)
  255. rescue ::Exception => e
  256. report_format = report_formats.shift
  257. if report_format
  258. retry
  259. end
  260. raise e
  261. end
  262. print_status("Generating the export data file...")
  263. last_report = nil
  264. while(! last_report)
  265. last_report = @nsc.last_report(report.id)
  266. select(nil, nil, nil, 1.0)
  267. end
  268. url = last_report.uri
  269. print_status("Downloading the export data...")
  270. data = @nsc.download(url)
  271. # Delete the temporary report ID
  272. @nsc.delete_report_config(report.id)
  273. print_status("Importing Nexpose data...")
  274. process_nexpose_data(report_format, data)
  275. end
  276. def cmd_nexpose_discover(*args)
  277. args << "-h" if args.length == 0
  278. args << "-t"
  279. args << "aggressive-discovery"
  280. cmd_nexpose_scan(*args)
  281. end
  282. def cmd_nexpose_exhaustive(*args)
  283. args << "-h" if args.length == 0
  284. args << "-t"
  285. args << "exhaustive-audit"
  286. cmd_nexpose_scan(*args)
  287. end
  288. def cmd_nexpose_dos(*args)
  289. args << "-h" if args.length == 0
  290. args << "-t"
  291. args << "dos-audit"
  292. cmd_nexpose_scan(*args)
  293. end
  294. def cmd_nexpose_scan(*args)
  295. opts = Rex::Parser::Arguments.new(
  296. "-h" => [ false, "This help menu"],
  297. "-t" => [ true, "The scan template to use (default:pentest-audit options:full-audit,exhaustive-audit,discovery,aggressive-discovery,dos-audit)"],
  298. "-c" => [ true, "Specify credentials to use against these targets (format is type:user:pass"],
  299. "-n" => [ true, "The maximum number of IPs to scan at a time (default is 32)"],
  300. "-s" => [ true, "The directory to store the raw XML files from the Nexpose instance (optional)"],
  301. "-P" => [ false, "Leave the scan data on the server when it completes (this counts against the maximum licensed IPs)"],
  302. "-v" => [ false, "Display diagnostic information about the scanning process"],
  303. "-d" => [ false, "Scan hosts based on the contents of the existing database"],
  304. "-I" => [ true, "Only scan systems with an address within the specified range"],
  305. "-E" => [ true, "Exclude hosts in the specified range from the scan"]
  306. )
  307. opt_template = "pentest-audit"
  308. opt_maxaddrs = 32
  309. opt_monitor = false
  310. opt_verbose = false
  311. opt_savexml = nil
  312. opt_preserve = false
  313. opt_rescandb = false
  314. opt_addrinc = nil
  315. opt_addrexc = nil
  316. opt_scanned = []
  317. opt_credentials = []
  318. opt_ranges = []
  319. opts.parse(args) do |opt, idx, val|
  320. case opt
  321. when "-h"
  322. print_line("Usage: nexpose_scan [options] <Target IP Ranges>")
  323. print_line(opts.usage)
  324. return
  325. when "-t"
  326. opt_template = val
  327. when "-n"
  328. opt_maxaddrs = val.to_i
  329. when "-s"
  330. opt_savexml = val
  331. when "-c"
  332. if (val =~ /^([^:]+):([^:]+):(.+)/)
  333. type, user, pass = [ $1, $2, $3 ]
  334. msfid = Time.now.to_i
  335. newcreds = Nexpose::SiteCredentials.for_service("Metasploit Site Credential #{msfid}", nil, nil, nil, nil, type)
  336. newcreds.user_name = user
  337. newcreds.password = pass
  338. opt_credentials << newcreds
  339. else
  340. print_error("Unrecognized Nexpose scan credentials: #{val}")
  341. return
  342. end
  343. when "-v"
  344. opt_verbose = true
  345. when "-P"
  346. opt_preserve = true
  347. when "-d"
  348. opt_rescandb = true
  349. when '-I'
  350. opt_addrinc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
  351. when '-E'
  352. opt_addrexc = OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(val)
  353. else
  354. opt_ranges << val
  355. end
  356. end
  357. return if not nexpose_verify
  358. # Include all database hosts as scan targets if specified
  359. if(opt_rescandb)
  360. print_status("Loading scan targets from the active database...") if opt_verbose
  361. framework.db.hosts.each do |host|
  362. next if host.state != ::Msf::HostState::Alive
  363. opt_ranges << host.address
  364. end
  365. end
  366. possible_files = opt_ranges # don't allow DOS by circular reference
  367. possible_files.each do |file|
  368. if ::File.readable? file
  369. print_status "Parsing ranges from #{file}"
  370. range_list = ::File.open(file,"rb") {|f| f.read f.stat.size}
  371. range_list.each_line { |subrange| opt_ranges << subrange}
  372. opt_ranges.delete(file)
  373. end
  374. end
  375. opt_ranges = opt_ranges.join(' ')
  376. if(opt_ranges.strip.empty?)
  377. print_line("Usage: nexpose_scan [options] <Target IP Ranges>")
  378. print_line(opts.usage)
  379. return
  380. end
  381. if(opt_verbose)
  382. print_status("Creating a new scan using template #{opt_template} and #{opt_maxaddrs} concurrent IPs against #{opt_ranges}")
  383. end
  384. range_inp = ::Msf::OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(opt_ranges)
  385. range = ::Rex::Socket::RangeWalker.new(range_inp)
  386. include_range = opt_addrinc ? ::Rex::Socket::RangeWalker.new(opt_addrinc) : nil
  387. exclude_range = opt_addrexc ? ::Rex::Socket::RangeWalker.new(opt_addrexc) : nil
  388. completed = 0
  389. total = range.num_ips
  390. count = 0
  391. print_status("Scanning #{total} addresses with template #{opt_template} in sets of #{opt_maxaddrs}")
  392. while(completed < total)
  393. count += 1
  394. queue = []
  395. while(ip = range.next_ip and queue.length < opt_maxaddrs)
  396. if(exclude_range and exclude_range.include?(ip))
  397. print_status(" >> Skipping host #{ip} due to exclusion") if opt_verbose
  398. next
  399. end
  400. if(include_range and ! include_range.include?(ip))
  401. print_status(" >> Skipping host #{ip} due to inclusion filter") if opt_verbose
  402. next
  403. end
  404. opt_scanned << ip
  405. queue << ip
  406. end
  407. break if queue.empty?
  408. print_status("Scanning #{queue[0]}-#{queue[-1]}...") if opt_verbose
  409. msfid = Time.now.to_i
  410. # Create a temporary site
  411. site = Nexpose::Site.new(nil, opt_template)
  412. site.name = "Metasploit-#{msfid}"
  413. site.description = "Autocreated by the Metasploit Framework"
  414. site.included_addresses = queue
  415. site.site_credentials = opt_credentials
  416. site.save(@nsc)
  417. print_status(" >> Created temporary site ##{site.id}") if opt_verbose
  418. report_formats = ["raw-xml-v2", "ns-xml"]
  419. report_format = report_formats.shift
  420. report = Nexpose::ReportConfig.build(@nsc, site.id, site.name, opt_template, report_format, true)
  421. report.delivery = Nexpose::Delivery.new(true)
  422. begin
  423. report.format = report_format
  424. report.save(@nsc, true)
  425. rescue ::Exception => e
  426. report_format = report_formats.shift
  427. if report_format
  428. retry
  429. end
  430. raise e
  431. end
  432. print_status(" >> Created temporary report configuration ##{report.id}") if opt_verbose
  433. # Run the scan
  434. begin
  435. res = site.scan(@nsc)
  436. rescue Nexpose::APIError => e
  437. nexpose_error_message = e.message
  438. nexpose_error_message.gsub!(/NexposeAPI: Action failed: /, '')
  439. print_error "#{nexpose_error_message}"
  440. return
  441. end
  442. sid = res.id
  443. print_status(" >> Scan has been launched with ID ##{sid}") if opt_verbose
  444. rep = true
  445. begin
  446. prev = nil
  447. while(true)
  448. info = @nsc.scan_statistics(sid)
  449. break if info.status != "running"
  450. stat = "Found #{info.nodes.live} devices and #{info.nodes.dead} unresponsive"
  451. if(stat != prev)
  452. print_status(" >> #{stat}") if opt_verbose
  453. end
  454. prev = stat
  455. select(nil, nil, nil, 5.0)
  456. end
  457. print_status(" >> Scan has been completed with ID ##{sid}") if opt_verbose
  458. rescue ::Interrupt
  459. rep = false
  460. print_status(" >> Terminating scan ID ##{sid} due to console interupt") if opt_verbose
  461. @nsc.stop_scan(sid)
  462. break
  463. end
  464. # Wait for the automatic report generation to complete
  465. if(rep)
  466. print_status(" >> Waiting on the report to generate...") if opt_verbose
  467. last_report = nil
  468. while(! last_report)
  469. last_report = @nsc.last_report(report.id)
  470. select(nil, nil, nil, 1.0)
  471. end
  472. url = last_report.uri
  473. print_status(" >> Downloading the report data from Nexpose...") if opt_verbose
  474. data = @nsc.download(url)
  475. if(opt_savexml)
  476. ::FileUtils.mkdir_p(opt_savexml)
  477. path = ::File.join(opt_savexml, "nexpose-#{msfid}-#{count}.xml")
  478. print_status(" >> Saving scan data into #{path}") if opt_verbose
  479. ::File.open(path, "wb") { |fd| fd.write(data) }
  480. end
  481. process_nexpose_data(report_format, data)
  482. end
  483. if ! opt_preserve
  484. # Make sure the scan has finished clean up before attempting to delete the site
  485. while (true)
  486. info = @nsc.scan_statistics(sid)
  487. break if info.status == 'stopped' || info.status == 'finished'
  488. select(nil, nil, nil, 5.0)
  489. end
  490. print_status(" >> Deleting the temporary site and report...") if opt_verbose
  491. begin
  492. @nsc.delete_site(site.id)
  493. rescue ::Nexpose::APIError => e
  494. print_status(" >> Deletion of temporary site and report failed: #{e.inspect}")
  495. end
  496. end
  497. end
  498. print_status("Completed the scan of #{total} addresses")
  499. end
  500. def cmd_nexpose_disconnect(*args)
  501. @nsc.logout if @nsc
  502. @nsc = nil
  503. end
  504. def process_nexpose_data(fmt, data)
  505. case fmt
  506. when 'raw-xml-v2'
  507. framework.db.import({:data => data})
  508. when 'ns-xml'
  509. framework.db.import({:data => data})
  510. else
  511. print_error("Unsupported Nexpose data format: #{fmt}")
  512. end
  513. end
  514. #
  515. # Nexpose vuln lookup
  516. #
  517. def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil)
  518. doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef|
  519. title = vulndef.attributes['title']
  520. pciSeverity = vulndef.attributes['pciSeverity']
  521. cvss_score = vulndef.attributes['cvssScore']
  522. cvss_vector = vulndef.attributes['cvssVector']
  523. vulndef.elements['references'].elements.each('reference') do |ref|
  524. if ref.attributes['source'] == 'BID'
  525. refs[ 'BID-' + ref.text ] = true
  526. elsif ref.attributes['source'] == 'CVE'
  527. # ref.text is CVE-$ID
  528. refs[ ref.text ] = true
  529. elsif ref.attributes['source'] == 'MS'
  530. refs[ 'MSB-MS-' + ref.text ] = true
  531. end
  532. end
  533. refs[ 'NEXPOSE-' + vid.downcase ] = true
  534. vuln = framework.db.find_or_create_vuln(
  535. :host => host,
  536. :service => serv,
  537. :name => 'NEXPOSE-' + vid.downcase,
  538. :data => title)
  539. rids = []
  540. refs.keys.each do |r|
  541. rids << framework.db.find_or_create_ref(:name => r)
  542. end
  543. vuln.refs << (rids - vuln.refs)
  544. end
  545. end
  546. end
  547. #
  548. # Plugin initialization
  549. #
  550. def initialize(framework, opts)
  551. super
  552. add_console_dispatcher(NexposeCommandDispatcher)
  553. banner = ["0a205f5f5f5f202020202020202020202020205f20202020205f205f5f5f5f5f2020205f2020205f20202020205f5f20205f5f2020202020202020202020202020202020202020200a7c20205f205c205f5f205f205f205f5f20285f29205f5f7c207c5f5f5f20207c207c205c207c207c205f5f5f5c205c2f202f5f205f5f2020205f5f5f20205f5f5f20205f5f5f200a7c207c5f29202f205f60207c20275f205c7c207c2f205f60207c20202f202f20207c20205c7c207c2f205f205c5c20202f7c20275f205c202f205f205c2f205f5f7c2f205f205c0a7c20205f203c20285f7c207c207c5f29207c207c20285f7c207c202f202f2020207c207c5c20207c20205f5f2f2f20205c7c207c5f29207c20285f29205c5f5f205c20205f5f2f0a7c5f7c205c5f5c5f5f2c5f7c202e5f5f2f7c5f7c5c5f5f2c5f7c2f5f2f202020207c5f7c205c5f7c5c5f5f5f2f5f2f5c5f5c202e5f5f2f205c5f5f5f2f7c5f5f5f2f5c5f5f5f7c0a20202020202020202020207c5f7c20202020202020202020202020202020202020202020202020202020202020202020207c5f7c202020202020202020202020202020202020200a0a0a"].pack("H*")
  554. # Do not use this UTF-8 encoded high-ascii art for non-UTF-8 or windows consoles
  555. lang = Rex::Compat.getenv("LANG")
  556. if (lang and lang =~ /UTF-8/)
  557. # Cygwin/Windows should not be reporting UTF-8 either...
  558. # (! (Rex::Compat.is_windows or Rex::Compat.is_cygwin))
  559. banner = ["202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29684e29684e29684202020e29684e29684202020202020202020202020e29684e29684e296842020e29684e29684e2968420202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29688202020e29688e2968820202020202020202020202020e29688e2968820e29684e29688e296882020202020202020202020202020202020202020202020202020202020202020202020202020202020200a20e29688e29688e29680e296882020e29688e29688202020e29684e29688e29688e29688e29688e296842020202020e29688e29688e29688e2968820202020e29688e29688e29684e29688e29688e29688e2968420202020e29684e29688e29688e29688e29688e29684202020e29684e29684e29688e29688e29688e29688e29688e29684202020e29684e29688e29688e29688e29688e2968420200a20e29688e2968820e29688e2968820e29688e296882020e29688e29688e29684e29684e29684e29684e29688e296882020202020e29688e296882020202020e29688e29688e296802020e29680e29688e296882020e29688e29688e296802020e29680e29688e296882020e29688e29688e29684e29684e29684e2968420e296802020e29688e29688e29684e29684e29684e29684e29688e29688200a20e29688e296882020e29688e29684e29688e296882020e29688e29688e29680e29680e29680e29680e29680e2968020202020e29688e29688e29688e2968820202020e29688e2968820202020e29688e296882020e29688e2968820202020e29688e29688202020e29680e29680e29680e29680e29688e29688e296842020e29688e29688e29680e29680e29680e29680e29680e29680200a20e29688e29688202020e29688e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688202020e29688e296882020e29688e29688202020e29688e29688e29688e29684e29684e29688e29688e296802020e29680e29688e29688e29684e29684e29688e29688e296802020e29688e29684e29684e29684e29684e29684e29688e296882020e29680e29688e29688e29684e29684e29684e29684e29688200a20e29680e29680202020e29680e29680e2968020202020e29680e29680e29680e29680e29680202020e29680e29680e296802020e29680e29680e296802020e29688e2968820e29680e29680e29680202020202020e29680e29680e29680e296802020202020e29680e29680e29680e29680e29680e296802020202020e29680e29680e29680e29680e2968020200a20202020202020202020202020202020202020202020202020202020202020e29688e29688202020202020202020202020202020202020202020202020202020202020202020202020200a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020200a"].pack("H*")
  560. end
  561. print(banner)
  562. print_status("Nexpose integration has been activated")
  563. end
  564. def cleanup
  565. remove_console_dispatcher('Nexpose')
  566. end
  567. def name
  568. "nexpose"
  569. end
  570. def desc
  571. "Integrates with the Rapid7 Nexpose vulnerability management product"
  572. end
  573. end
  574. end
  575. module Nexpose
  576. class IPRange
  577. def to_json
  578. if @to.present?
  579. "#{@from} - #{@to}".to_json
  580. else
  581. @from.to_json
  582. end
  583. end
  584. end
  585. end