Merge in nCircle support from Dave Lassalle, handle file reads more consistently
git-svn-id: file:///home/svn/framework3/trunk@10902 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
13a93d41f6
commit
652764ebd3
|
@ -3,6 +3,8 @@ require 'rex/parser/nexpose_xml'
|
|||
require 'rex/parser/retina_xml'
|
||||
require 'rex/parser/netsparker_xml'
|
||||
require 'rex/parser/nessus_xml'
|
||||
require 'rex/parser/ip360_xml'
|
||||
require 'rex/parser/ip360_aspl_xml'
|
||||
require 'rex/socket'
|
||||
require 'zip'
|
||||
require 'uri'
|
||||
|
@ -1883,8 +1885,11 @@ class DBManager
|
|||
@import_filedata = {}
|
||||
@import_filedata[:filename] = filename
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
|
||||
if data[0,4] == "PK\x03\x04"
|
||||
data = Zip::ZipFile.open(filename)
|
||||
end
|
||||
|
@ -1909,7 +1914,7 @@ class DBManager
|
|||
end
|
||||
ftype = import_filetype_detect(data)
|
||||
yield(:filetype, @import_filedata[:type]) if block
|
||||
|
||||
|
||||
self.send "import_#{ftype}".to_sym, args, &block
|
||||
end
|
||||
|
||||
|
@ -1976,6 +1981,9 @@ class DBManager
|
|||
when /netsparker/
|
||||
@import_filedata[:type] = "NetSparker XML"
|
||||
return :netsparker_xml
|
||||
when /audits/
|
||||
@import_filedata[:type] = "IP360 XML v3"
|
||||
return :ip360_xml_v3
|
||||
else
|
||||
# Give up if we haven't hit the root tag in the first few lines
|
||||
break if line_count > 10
|
||||
|
@ -2131,8 +2139,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_nexpose_simplexml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -2141,8 +2151,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_msf_xml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -2707,8 +2719,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_nexpose_rawxml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -2880,8 +2894,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_retina_xml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -2962,8 +2978,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_netsparker_xml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -3345,8 +3363,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_nmap_xml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -3499,8 +3519,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_nessus_nbe(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -3582,6 +3604,20 @@ class DBManager
|
|||
raise DBImportError.new("No OpenVAS XML support. Please submit a patch to msfdev[at]metasploit.com")
|
||||
end
|
||||
|
||||
#
|
||||
# Import IP360 XML v3 output
|
||||
#
|
||||
def import_ip360_xml_file(args={})
|
||||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_ip360_xml_v3(args.merge(:data => data))
|
||||
end
|
||||
|
||||
#
|
||||
# Import Nessus XML v1 and v2 output
|
||||
#
|
||||
|
@ -3591,8 +3627,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
|
||||
if data.index("NessusClientData_v2")
|
||||
import_nessus_xml_v2(args.merge(:data => data))
|
||||
|
@ -3757,6 +3795,125 @@ class DBManager
|
|||
|
||||
end
|
||||
|
||||
#
|
||||
# Import IP360's xml output
|
||||
#
|
||||
def import_ip360_xml_v3(args={}, &block)
|
||||
data = args[:data]
|
||||
wspace = args[:wspace] || workspace
|
||||
bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []
|
||||
|
||||
# @aspl = {'vulns' => {'name' => { }, 'cve' => { }, 'bid' => { } }
|
||||
# 'oses' => {'name' } }
|
||||
|
||||
aspl_path = File.join(Msf::Config.data_directory, "data", "ncircle", "ip360.aspl")
|
||||
|
||||
if not ::File.exist?(aspl_path)
|
||||
raise DBImportError.new("The nCircle IP360 ASPL file is not present.\n Download ASPL from nCircle VNE | Administer | Support | Resources, unzip it, and save it as " + aspl_path)
|
||||
end
|
||||
|
||||
if not ::File.readable?(aspl_path)
|
||||
raise DBImportError.new("Could not read the IP360 ASPL XML file provided at " + aspl_path)
|
||||
end
|
||||
|
||||
# parse nCircle ASPL file
|
||||
aspl = ""
|
||||
::File.open(aspl_path, "rb") do |f|
|
||||
aspl = f.read(f.stat.size)
|
||||
end
|
||||
|
||||
@asplhash = nil
|
||||
parser = Rex::Parser::IP360ASPLXMLStreamParser.new
|
||||
parser.on_found_aspl = Proc.new { |asplh|
|
||||
@asplhash = asplh
|
||||
}
|
||||
REXML::Document.parse_stream(aspl, parser)
|
||||
|
||||
#@host = {'hname' => nil, 'addr' => nil, 'mac' => nil, 'os' => nil, 'hid' => nil,
|
||||
# 'vulns' => ['vuln' => {'vulnid' => nil, 'port' => nil, 'proto' => nil } ],
|
||||
# 'apps' => ['app' => {'appid' => nil, 'svcid' => nil, 'port' => nil, 'proto' => nil } ],
|
||||
# 'shares' => []
|
||||
# }
|
||||
|
||||
# nCircle has some quotes escaped which causes the parser to break
|
||||
# we don't need these lines so just replace \" with "
|
||||
data.gsub!(/\\"/,'"')
|
||||
|
||||
# parse nCircle Scan Output
|
||||
parser = Rex::Parser::IP360XMLStreamParser.new
|
||||
parser.on_found_host = Proc.new { |host|
|
||||
|
||||
addr = host['addr'] || host['hname']
|
||||
|
||||
next unless ipv4_validator(addr) # Catches SCAN-ERROR, among others.
|
||||
|
||||
if bl.include? addr
|
||||
next
|
||||
else
|
||||
yield(:address,addr) if block
|
||||
end
|
||||
|
||||
os = host['os']
|
||||
yield(:os, os) if block
|
||||
if os
|
||||
report_note(
|
||||
:workspace => wspace,
|
||||
:host => addr,
|
||||
:type => 'host.os.ip360_fingerprint',
|
||||
:data => {
|
||||
:os => @asplhash['oses'][os].to_s.strip
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
hname = host['hname']
|
||||
|
||||
if hname
|
||||
report_host(
|
||||
:workspace => wspace,
|
||||
:host => addr,
|
||||
:name => hname.to_s.strip
|
||||
)
|
||||
end
|
||||
|
||||
mac = host['mac']
|
||||
|
||||
if mac
|
||||
report_host(
|
||||
:workspace => wspace,
|
||||
:host => addr,
|
||||
:mac => mac.to_s.strip.upcase
|
||||
)
|
||||
end
|
||||
|
||||
host['apps'].each do |item|
|
||||
port = item['port'].to_s
|
||||
proto = item['proto'].to_s
|
||||
|
||||
handle_ip360_v3_svc(wspace, addr, port, proto, hname)
|
||||
end
|
||||
|
||||
|
||||
host['vulns'].each do |item|
|
||||
vulnid = item['vulnid'].to_s
|
||||
port = item['port'].to_s
|
||||
proto = item['proto'] || "tcp"
|
||||
vulnname = @asplhash['vulns']['name'][vulnid]
|
||||
cves = @asplhash['vulns']['cve'][vulnid]
|
||||
bids = @asplhash['vulns']['bid'][vulnid]
|
||||
|
||||
yield(:port, port) if block
|
||||
|
||||
handle_ip360_v3_vuln(wspace, addr, port, proto, hname, vulnid, vulnname, cves, bids)
|
||||
|
||||
end
|
||||
|
||||
yield(:end, hname) if block
|
||||
}
|
||||
|
||||
REXML::Document.parse_stream(data, parser)
|
||||
end
|
||||
|
||||
#
|
||||
# Import Qualys' xml output
|
||||
#
|
||||
|
@ -3764,8 +3921,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_qualys_xml(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -3852,8 +4011,10 @@ class DBManager
|
|||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
import_ip_list(args.merge(:data => data))
|
||||
end
|
||||
|
||||
|
@ -3876,8 +4037,11 @@ class DBManager
|
|||
def import_amap_log_file(args={})
|
||||
filename = args[:filename]
|
||||
wspace = args[:wspace] || workspace
|
||||
f = File.open(filename, 'rb')
|
||||
data = f.read(f.stat.size)
|
||||
data = ""
|
||||
::File.open(filename, 'rb') do |f|
|
||||
data = f.read(f.stat.size)
|
||||
end
|
||||
|
||||
case import_filetype_detect(data)
|
||||
when :amap_log
|
||||
import_amap_log(args.merge(:data => data))
|
||||
|
@ -4101,6 +4265,65 @@ protected
|
|||
report_vuln(vuln)
|
||||
end
|
||||
|
||||
#
|
||||
# IP360 v3 vuln
|
||||
#
|
||||
def handle_ip360_v3_svc(wspace,addr,port,proto,hname)
|
||||
|
||||
report_host(:workspace => wspace, :host => addr, :state => Msf::HostState::Alive)
|
||||
|
||||
info = { :workspace => wspace, :host => addr, :port => port, :proto => proto }
|
||||
if hname != "unknown" and hname[-1,1] != "?"
|
||||
info[:name] = hname
|
||||
end
|
||||
|
||||
if port.to_i != 0
|
||||
report_service(info)
|
||||
end
|
||||
end #handle_ip360_v3_svc
|
||||
|
||||
#
|
||||
# IP360 v3 vuln
|
||||
#
|
||||
def handle_ip360_v3_vuln(wspace,addr,port,proto,hname,vulnid,vulnname,cves,bids)
|
||||
|
||||
report_host(:workspace => wspace, :host => addr, :state => Msf::HostState::Alive)
|
||||
|
||||
info = { :workspace => wspace, :host => addr, :port => port, :proto => proto }
|
||||
if hname != "unknown" and hname[-1,1] != "?"
|
||||
info[:name] = hname
|
||||
end
|
||||
|
||||
if port.to_i != 0
|
||||
report_service(info)
|
||||
end
|
||||
|
||||
refs = []
|
||||
|
||||
cves.split(/,/).each do |cve|
|
||||
refs.push(cve.to_s)
|
||||
end if cves
|
||||
|
||||
bids.split(/,/).each do |bid|
|
||||
refs.push('BID-' + bid.to_s)
|
||||
end if bids
|
||||
|
||||
description = nil # not working yet
|
||||
vuln = {
|
||||
:workspace => wspace,
|
||||
:host => addr,
|
||||
:name => vulnname,
|
||||
:info => description ? description : "",
|
||||
:refs => refs
|
||||
}
|
||||
|
||||
if port.to_i != 0
|
||||
vuln[:port] = port
|
||||
vuln[:proto] = proto
|
||||
end
|
||||
|
||||
report_vuln(vuln)
|
||||
end #handle_ip360_v3_vuln
|
||||
|
||||
#
|
||||
# Qualys report parsing/handling
|
||||
|
|
|
@ -69,6 +69,7 @@ class Db
|
|||
"db_import_amap_log" => "Import a THC-Amap scan results file (-o )",
|
||||
"db_import_nessus_nbe" => "Import a Nessus scan result file (NBE)",
|
||||
"db_import_nessus_xml" => "Import a Nessus scan result file (NESSUS)",
|
||||
"db_import_ip360_xml" => "Import an IP360 scan result file (XML)",
|
||||
"db_import_nmap_xml" => "Import a Nmap scan results file (-oX)",
|
||||
"db_import_qualys_xml" => "Import a Qualys scan results file (XML)",
|
||||
"db_import_msfe_xml" => "Import a Metasploit Express report (XML)",
|
||||
|
@ -1236,6 +1237,26 @@ class Db
|
|||
framework.db.import_nessus_xml_file(:filename => args[0])
|
||||
end
|
||||
|
||||
#
|
||||
# Import IP360 XML files
|
||||
#
|
||||
def cmd_db_import_ip360_xml(*args)
|
||||
return unless active?
|
||||
if (not (args and args.length == 1))
|
||||
print_status("Usage: db_import_ip360_xml <ip360.xml>")
|
||||
return
|
||||
end
|
||||
|
||||
warn_about_db_import(args[0])
|
||||
|
||||
if (not File.readable?(args[0]))
|
||||
print_status("Could not read the IP360 Scan file provided")
|
||||
return
|
||||
end
|
||||
|
||||
framework.db.import_ip360_xml_file(:filename => args[0])
|
||||
end
|
||||
|
||||
#
|
||||
# Import Nmap data from a file
|
||||
#
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
require 'rexml/document'
|
||||
require 'rex/ui'
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
|
||||
|
||||
class IP360ASPLXMLStreamParser
|
||||
|
||||
@vulnid = nil
|
||||
@appid = nil
|
||||
@location = nil
|
||||
|
||||
attr_accessor :on_found_aspl
|
||||
|
||||
def initialize(&block)
|
||||
reset_state
|
||||
on_found_aspl = block if block
|
||||
end
|
||||
|
||||
def reset_state
|
||||
@aspl = {'vulns' => {'name' => { }, 'cve' => { }, 'bid' => { } },
|
||||
'oses' => {'name' => { } } }
|
||||
@state = :generic_state
|
||||
end
|
||||
|
||||
def tag_start(name, attributes)
|
||||
case name
|
||||
when "vulns"
|
||||
@location = "vulns"
|
||||
when "vuln"
|
||||
@vulnid = attributes['id'].strip
|
||||
when "name"
|
||||
@state = :is_name
|
||||
when "advisories"
|
||||
@c = ""
|
||||
@cfirst = 1
|
||||
@b = ""
|
||||
@bfirst = 1
|
||||
@x = Hash.new
|
||||
when "publisher"
|
||||
@state = :is_pub
|
||||
when "id"
|
||||
@state = :is_refid
|
||||
when "operatingSystems"
|
||||
@location = "os"
|
||||
when "operatingSystem"
|
||||
@osid = attributes['id'].strip
|
||||
end
|
||||
end
|
||||
|
||||
def text(str)
|
||||
case @state
|
||||
when :is_name
|
||||
@aspl['vulns']['name'][@vulnid] = str if @location == "vulns"
|
||||
@aspl['oses'][@osid] = str if @location == "os"
|
||||
when :is_pub
|
||||
@x['pub'] = str
|
||||
when :is_refid
|
||||
@x['refid'] = str
|
||||
end
|
||||
end
|
||||
|
||||
def tag_end(name)
|
||||
case name
|
||||
when "ontology"
|
||||
on_found_aspl.call(@aspl) if on_found_aspl
|
||||
reset_state
|
||||
when "advisory"
|
||||
if (@x['pub'] =~ /CVE/)
|
||||
if (@cfirst == 0)
|
||||
@c += ","
|
||||
end
|
||||
@c += @x['refid']
|
||||
@cfirst = 0
|
||||
elsif (@x['pub'] =~ /BugTraq/)
|
||||
if (@bfirst == 0)
|
||||
@b += ","
|
||||
end
|
||||
@b += @x['refid']
|
||||
@bfirst = 0
|
||||
end
|
||||
when "advisories"
|
||||
@aspl['vulns']['cve'][@vulnid] = @c
|
||||
@aspl['vulns']['bid'][@vulnid] = @b
|
||||
@c = ""
|
||||
@b = ""
|
||||
end
|
||||
@state = :generic_state
|
||||
end
|
||||
|
||||
# We don't need these methods, but they're necessary to keep REXML happy
|
||||
#
|
||||
def xmldecl(version, encoding, standalone); end
|
||||
def cdata; end
|
||||
def comment(str); end
|
||||
def instruction(name, instruction); end
|
||||
def attlist; end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,93 @@
|
|||
require 'rexml/document'
|
||||
require 'rex/ui'
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
|
||||
|
||||
class IP360XMLStreamParser
|
||||
|
||||
attr_accessor :on_found_host
|
||||
|
||||
def initialize(&block)
|
||||
reset_state
|
||||
on_found_host = block if block
|
||||
end
|
||||
|
||||
def reset_state
|
||||
@host = {'hname' => nil, 'hid' => nil, 'addr' => nil, 'mac' => nil, 'os' => nil,
|
||||
'vulns' => ['vuln' => {'vulnid' => nil, 'port' => nil, 'proto' => nil} ],
|
||||
'apps' => ['app' => {'appid' => nil, 'svcid' => nil, 'port' => nil, 'proto' => nil } ],
|
||||
}
|
||||
@state = :generic_state
|
||||
end
|
||||
|
||||
def tag_start(name, attributes)
|
||||
case name
|
||||
when "host"
|
||||
@host['hid'] = attributes['persistent_id']
|
||||
when "ip"
|
||||
@state = :is_ip
|
||||
when "dnsName"
|
||||
@state = :is_fqdn
|
||||
when "macAddress"
|
||||
@state = :is_mac
|
||||
when "os"
|
||||
@host['os'] = attributes['id']
|
||||
when "vulnerability"
|
||||
@x = Hash.new
|
||||
@x['vulnid'] = attributes['id']
|
||||
when "port"
|
||||
@state = :is_port
|
||||
when "protocol"
|
||||
@state = :is_proto
|
||||
when "application"
|
||||
@y = Hash.new
|
||||
@y['appid'] = attributes['application_id']
|
||||
@y['svcid'] = attributes['svcid']
|
||||
@y['port'] = attributes['port']
|
||||
@y['proto'] = attributes['protocol']
|
||||
@host['apps'].push @y
|
||||
end
|
||||
end
|
||||
|
||||
def text(str)
|
||||
case @state
|
||||
when :is_fqdn
|
||||
@host['hname'] = str
|
||||
when :is_ip
|
||||
@host['addr'] = str
|
||||
when :is_mac
|
||||
@host['mac'] = str
|
||||
when :is_port
|
||||
@x['port'] = str
|
||||
when :is_proto
|
||||
@x['proto'] = str
|
||||
end
|
||||
end
|
||||
|
||||
def tag_end(name)
|
||||
case name
|
||||
when "host"
|
||||
on_found_host.call(@host) if on_found_host
|
||||
reset_state
|
||||
when "vulnerability"
|
||||
@host['vulns'].push @x
|
||||
end
|
||||
@state = :generic_state
|
||||
end
|
||||
|
||||
def cdata(d)
|
||||
#do nothing
|
||||
end
|
||||
|
||||
# We don't need these methods, but they're necessary to keep REXML happy
|
||||
#
|
||||
def xmldecl(version, encoding, standalone); end
|
||||
def comment(str); end
|
||||
def instruction(name, instruction); end
|
||||
def attlist; end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue