module NmapAutoAnalyzer
Constants
- VERSION
Attributes
execute[RW]
options[RW]
parsed_hosts[RW]
scan_files[RW]
scanned_files[RW]
valid_entries[RW]
Public Class Methods
excel_report()
click to toggle source
# File lib/nmap_auto_analyzer.rb, line 317 def self.excel_report begin require 'rubyXL' rescue LoadError puts "Couldn't load rubyXL, try gem install rubyXL" exit end workbook = RubyXL::Workbook.new sheet = workbook.worksheets[0] sheet.add_cell(0,0,"IP Address") sheet.add_cell(0,1,"TCP Ports") sheet.add_cell(0,2,"UDP Ports") curr_row = 1 sorted_hosts = @parsed_hosts.sort_by {|address,find| address.split('.').map{ |digits| digits.to_i}} sorted_hosts.each do |entry| host, ports = entry[0], entry[1] next if ports.length == 0 tcp_ports = Array.new udp_ports = Array.new ports.each do |port,data| portnum, protocol = port.split(' - ') if protocol == 'TCP' tcp_ports << portnum elsif protocol == 'UDP' udp_ports << portnum end end sheet.add_cell(curr_row,0,host) sheet.add_cell(curr_row,1,tcp_ports.join(', ')) sheet.add_cell(curr_row,2,udp_ports.join(', ')) curr_row = curr_row + 1 end workbook.write(@excel_report_file_name) end
execute(commandlineopts)
click to toggle source
# File lib/nmap_auto_analyzer.rb, line 7 def self.execute(commandlineopts) @options = commandlineopts require 'rubygems' require 'logger' @base_dir = @options.report_directory @scan_dir = @options.scan_directory if !File.exists?(@base_dir) Dir.mkdirs(@base_dir) end @log = Logger.new(@base_dir + '/nmap-analyzer-log') @log.level = Logger::DEBUG @log.debug("Log created at " + Time.now.to_s) @log.debug("Scan type is : #{@options.scan_type}") @log.debug("Directory being scanned is : #{@options.scan_directory}") if @options.scan_type == :directory @log.debug("File being scanned is : #{@options.scan_file}") if @options.scan_type == :file @report_file_name = @base_dir + '/' + @options.report_file @report_file = File.new(@report_file_name + '.txt','w+') @html_report_file = File.new(@report_file_name + '.html','w+') @excel_report_file_name = @report_file_name + '.xlsx' @log.info("New report file created #{@report_file_name}") run end
html_report()
click to toggle source
Generates an HTML report with the results
# File lib/nmap_auto_analyzer.rb, line 300 def self.html_report begin require 'kramdown' rescue LoadError puts "Couldn't load kramdown, try gem install kramdown" exit end base_report = File.open(@report_file_name + '.txt','r').read report = Kramdown::Document.new(base_report) @html_report_file.puts report.to_html end
parse_files()
click to toggle source
Parses the nmap xml files and populates the arrays needed by the report
# File lib/nmap_auto_analyzer.rb, line 78 def self.parse_files #Hash to put the information for each host into @parsed_hosts = Hash.new @scanned_files = Hash.new @closed_ports = Array.new #Ports is an array to contain a list of the unique ports encountered for later feeding to Nessus @ports = Array.new #port_hash is a hash to contain a list of ports and what hosts have them open @port_hash = Hash.new @traceroute_hash = Hash.new @os_hash = Hash.new @web_headers_hash = Hash.new @scan_files.each do |file| begin parser = Nmap::Parser.parsefile(file) rescue IOError => e @log.warn("Warning couldn't parse file #{file}") puts "Couldn't parse file #{file}, check to make sure it wasn't critical!!" next rescue REXML::ParseException @log.warn("Warning, couldn't parse file #{file} due to an XML parse error") puts "Warning, couldn't parse file #{file} due to an XML parse error" next end @scanned_files[file] = Hash.new unless @scanned_files[file] @scanned_files[file][:scan_args] = parser.session.scan_args if parser.session.scan_args @scanned_files[file][:scan_time] = parser.session.scan_time if parser.session.scan_time parser.hosts("up") do |host| #TODO: we should add UDP here too, but watch for no-response otherwise we'll get false positive centraled. next if @options.ignore_chatty && host.tcp_ports("open").length > 900 @parsed_hosts[host.addr] = Hash.new unless @parsed_hosts[host.addr] host.extraports.each do |portstate| if portstate.state == "closed" && portstate.count > 1 @closed_ports << host.addr end end #Add Traceroute information and grab the last hop before the host #It's either the last hop or the one before it #So it looks to me that nmaps traceroute isn't quite right for this #produces different results to traceroute... #if host.traceroute # @log.debug("host address is " + host.addr + "Last traceroute is" + host.traceroute.hops[-1].addr) # if host.traceroute.hops[-1].addr != host.addr || host.traceroute.hops.length == 1 # last_hop = host.traceroute.hops[-1].addr.to_s # else # last_hop = host.traceroute.hops[-2].addr.to_s # end # @traceroute_hash[host.addr] = last_hop #end #Add OS guess information if host.os.name @os_hash[host.addr] = host.os.name + ', ' + host.os.name_accuracy.to_s end host.tcp_ports("open") do |port| #Add the port to the ports array @ports << port.num.to_s #Add the port to the port hash if @port_hash[port.num.to_s + '-TCP'] @port_hash[port.num.to_s + '-TCP'] << host.addr else @port_hash[port.num.to_s + '-TCP'] = Array.new @port_hash[port.num.to_s + '-TCP'] << host.addr end @parsed_hosts[host.addr][port.num.to_s + ' - TCP'] = Hash.new @parsed_hosts[host.addr][port.num.to_s + ' - TCP'][:service] = port.service.name if port.service.name @parsed_hosts[host.addr][port.num.to_s + ' - TCP'][:reason] = port.reason if port.reason @parsed_hosts[host.addr][port.num.to_s + ' - TCP'][:product] = port.service.product if port.service.product if host.tcp_script(port.num.to_s, 'http-methods') @web_headers_hash[host.addr + ':' + port.num.to_s] = host.tcp_script(port.num.to_s, 'http-methods').output.split("\n")[0] end end host.udp_ports("open") do |port| next if port.reason == "no-response" #Add the port to the ports array @ports << port.num.to_s #Add the port to the port hash if @port_hash[port.num.to_s + '-UDP'] @port_hash[port.num.to_s + '-UDP'] << host.addr else @port_hash[port.num.to_s + '-UDP'] = Array.new @port_hash[port.num.to_s + '-UDP'] << host.addr end @parsed_hosts[host.addr][port.num.to_s + ' - UDP'] = Hash.new @parsed_hosts[host.addr][port.num.to_s + ' - UDP'][:service] = port.service.name if port.service.name @parsed_hosts[host.addr][port.num.to_s + ' - UDP'][:reason] = port.reason if port.reason @parsed_hosts[host.addr][port.num.to_s + ' - UDP'][:product] = port.service.product if port.service.product end end end #Once we're done with the files clean up the ports array @ports.uniq! end
report()
click to toggle source
Generates a kramdown compatible report that we can use to generate an HTML report
# File lib/nmap_auto_analyzer.rb, line 179 def self.report @report_file.puts "NMAP AUTO analysis" @report_file.puts "===================\n\n" @report_file.puts "Unique ports open" @report_file.puts "-------------------\n\n" @report_file.puts @ports.join(', ') @report_file.puts "\n\n" @report_file.puts "NMAP Host Analysis" @report_file.puts "-------------------" @report_file.puts "" @report_file.puts "Active Host List" @report_file.puts "---------" active_ipaddresses = Array.new inactive_ipaddresses = Array.new @parsed_hosts.each do |entry| host, ports = entry[0], entry[1] if ports.length > 0 active_ipaddresses << host else inactive_ipaddresses << host end end @report_file.puts active_ipaddresses.uniq.join(', ') @report_file.puts "" @report_file.puts "" @report_file.puts "Inactive Host List" @report_file.puts "---------" @report_file.puts inactive_ipaddresses.uniq.join(', ') @report_file.puts "" @report_file.puts "" if @traceroute_hash.length > 0 @report_file.puts "Traceroute Information" @report_file.puts "---------" @report_file.puts "Target Address, Last Hop" @traceroute_hash.each do |addr, last_hop| @report_file.puts addr + ", " + last_hop end @report_file.puts "" @report_file.puts "" end if @os_hash.length > 0 @report_file.puts "Operating System Information" @report_file.puts "---------" @report_file.puts "Target Address, OS Guess, OS Accuracy" @os_hash.each do |addr, os| @report_file.puts addr + ", " + os end @report_file.puts "" @report_file.puts "" end if @web_headers_hash.length > 0 @report_file.puts "Operating System Information" @report_file.puts "---------" @report_file.puts "Target Web Server, Supported Methods" @web_headers_hash.each do |addr, methods| @report_file.puts addr + ", " + methods end @report_file.puts "" @report_file.puts "" end #sorted_hosts = @parsed_hosts.sort {|a,b| b[1].length <=> a[1].length} sorted_hosts = @parsed_hosts.sort_by {|address,find| address.split('.').map{ |digits| digits.to_i}} sorted_hosts.each do |entry| host, ports = entry[0], entry[1] #This omits any hosts that were deemed up but had no open ports #TODO: Make this an option in reporting (verbose against concise) next if ports.length == 0 @report_file.puts "Host address: #{host} was detected as up by nmap" @report_file.puts "------------------------------------------------" @report_file.puts "" if ports.length == 0 @report_file.puts "No Ports detected as open on this host" end ports.each do |port, data| @report_file.print "Port #{port} is open " @report_file.print ", Service name is #{data[:service]}" if data[:service] @report_file.print ", Service Product name is #{data[:product]}" if data[:product] @report_file.print ", Up reason is #{data[:reason]}" if data[:reason] @report_file.puts "" end @report_file.puts "" @report_file.puts "-------------------" @report_file.puts "" end @report_file.puts "\n\nReport of hosts with a given port open" @report_file.puts "--------------------\n\n" @port_hash.each do |port,hosts| @report_file.puts port + ' - ' + hosts.uniq.length.to_s + ' hosts have this port open' @report_file.puts "-----------------" @report_file.puts hosts.uniq.join(', ') @report_file.puts "\n\n" end @report_file.puts "\n\nActive Hosts with Closed Ports" @report_file.puts "--------------------\n\n" @report_file.puts @closed_ports.uniq.join(', ') @report_file.puts "--------------------\n\n" active_ipaddresses.each do |add| result = "n" result = "y" if @closed_ports.include?(add) @report_file.puts add + ', ' + result end #TODO: Make this an option in terms of reporting volume @report_file.puts "\n\nNMAP runs analysed" @scanned_files.each do |file, data| @report_file.puts "\n-------------------" @report_file.puts file @report_file.puts "Scan arguments were #{data[:scan_args]}" @report_file.puts "Scan Time was #{data[:scan_time]}" end @report_file.close end
run()
click to toggle source
Sets up the process for scanning the xml files and calls the individual methods depending on the scan type
# File lib/nmap_auto_analyzer.rb, line 37 def self.run case @options.scan_type #Directory Mode when :directory @valid_entries = Array.new @valid_entries << '' scan_dirs parse_files report excel_report if @options.html_report html_report end #File Mode when :file @scan_files = Array.new @scan_files << @options.scan_file parse_files report excel_report if @options.html_report html_report end end end
scan_dirs()
click to toggle source
Adds all the xml files in the directory being scanned to the scan_files
array
# File lib/nmap_auto_analyzer.rb, line 65 def self.scan_dirs @scan_files = Array.new @valid_entries.each do |dir| Dir.entries(@scan_dir + dir).each do |scan| if scan =~ /xml$/ @scan_files << @scan_dir + dir + '/' + scan end end end end