add a generic db_import command that auto-detects filetype, move import parsing into msf/core/db. fixes 750

git-svn-id: file:///home/svn/framework3/trunk@8085 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
James Lee 2010-01-07 19:06:29 +00:00
parent 1bd6872c6f
commit 30b897b6cd
3 changed files with 499 additions and 339 deletions

View File

@ -880,6 +880,480 @@ class DBManager
ActiveRecord::Base.connection.select_all(sqlquery)
end
##
#
# Import methods
#
##
#
# Generic importer that automatically determines the file type being
# imported. Since this looks for vendor-specific strings in the given
# file, there shouldn't be any false detections, but no guarantees.
#
def import_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import(data)
end
def import(data)
firstline = data[0, data.index("\n")]
if (firstline.index("<NeXposeSimpleXML"))
return import_nexpose_simplexml(data)
elsif (firstline.index("<?xml"))
# it's xml, check for root tags we can handle
line_count = 0
data.each_line { |line|
line =~ /<([a-zA-Z-]+)[ >]/
case $1
when "nmaprun"
return import_nmap_xml(data)
when "openvas-report"
return import_openvas_xml(data)
when "NessusClientData"
return import_nessus_xml(data)
else
# Give up if we haven't hit the root tag in the first few lines
break if line_count > 10
end
line_count += 1
}
elsif (firstline.index("timestamps|||scan_start"))
# then it's a nessus nbe
return import_nessus_nbe(data)
elsif (firstline.index("# amap v"))
# then it's an amap mlog
return import_amap_mlog(data)
end
raise RuntimeError.new("Could not automatically determine file type")
end
#
# Nexpose Simple XML
#
# XXX At some point we'll want to make this a stream parser for dealing
# with large results files
#
def import_nexpose_simplexml_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import_nexpose_simplexml(data)
end
def import_nexpose_simplexml(data)
if data.kind_of? REXML::Document
doc = data
else
doc = REXML::Document.new(data)
end
p doc.root
doc.elements.each('/NeXposeSimpleXML/devices/device') do |dev|
addr = dev.attributes['address'].to_s
desc = ''
dev.elements.each('fingerprint/description') do |fdesc|
desc = fdesc.text.to_s.strip
end
host = framework.db.find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
next if not host
# Load vulnerabilities not associated with a service
dev.elements.each('vulnerabilities/vulnerability') do |vuln|
vid = vuln.attributes['id'].to_s.downcase
rids = []
refs = process_nexpose_data_sxml_refs(vuln)
next if not refs
vuln = framework.db.find_or_create_vuln(
:host => host,
:name => 'NEXPOSE-' + vid,
:data => vid)
refs.each { |r| rids << framework.db.find_or_create_ref(:name => r) }
vuln.refs << (rids - vuln.refs)
end
# Load the services
dev.elements.each('services/service') do |svc|
sname = svc.attributes['name'].to_s
sprot = svc.attributes['protocol'].to_s.downcase
sport = svc.attributes['port'].to_s.to_i
name = sname.split('(')[0].strip
if(sname.downcase != '<unknown>')
serv = framework.db.find_or_create_service(:host => host, :proto => sprot, :port => sport, :name => name)
else
serv = framework.db.find_or_create_service(:host => host, :proto => sprot, :port => sport)
end
# Load vulnerabilities associated with this service
svc.elements.each('vulnerabilities/vulnerability') do |vuln|
vid = vuln.attributes['id'].to_s.downcase
rids = []
refs = process_nexpose_data_sxml_refs(vuln)
next if not refs
vuln = framework.db.find_or_create_vuln(:host => host, :service => serv, :name => 'NEXPOSE-' + vid, :data => vid)
refs.each { |r| rids << framework.db.find_or_create_ref(:name => r) }
vuln.refs << (rids - vuln.refs)
end
end
end
return false
end
#
# Nexpose Raw XML
#
# XXX At some point we'll want to make this a stream parser for dealing
# with large results files
#
def import_nexpose_rawxml_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import_nexpose_rawxml(data)
end
def import_nexpose_rawxml(data)
doc = REXML::Document.new(data)
doc.elements.each('/NexposeReport/nodes/node') do |host|
addr = host.attributes['address']
xhost = addr
refs = {}
# os based vuln
host.elements['tests'].elements.each('test') do |vuln|
if vuln.attributes['status'] == 'vulnerable-exploited' or vuln.attributes['status'] == 'vulnerable-version'
dhost = framework.db.find_or_create_host(:host => addr)
next if not dhost
vid = vuln.attributes['id'].to_s
nexpose_vuln_lookup(doc,vid,refs,dhost)
nexpose_vuln_lookup(doc,vid.upcase,refs,dhost)
end
end
# skip if no endpoints
next unless host.elements['endpoints']
# parse the ports and add the vulns
host.elements['endpoints'].elements.each('endpoint') do |port|
prot = port.attributes['protocol']
pnum = port.attributes['port']
stat = port.attributes['status']
next if not port.elements['services']
name = port.elements['services'].elements['service'].attributes['name'].downcase
next if not port.elements['services'].elements['service'].elements['fingerprints']
prod = port.elements['services'].elements['service'].elements['fingerprints'].elements['fingerprint'].attributes['product']
vers = port.elements['services'].elements['service'].elements['fingerprints'].elements['fingerprint'].attributes['version']
vndr = port.elements['services'].elements['service'].elements['fingerprints'].elements['fingerprint'].attributes['vendor']
next if stat != 'open'
dhost = framework.db.find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
next if not dhost
if name != "unknown"
service = framework.db.find_or_create_service(:host => dhost, :proto => prot.downcase, :port => pnum.to_i, :name => name)
else
service = framework.db.find_or_create_service(:host => dhost, :proto => prot.downcase, :port => pnum.to_i)
end
port.elements['services'].elements['service'].elements['tests'].elements.each('test') do |vuln|
if vuln.attributes['status'] == 'vulnerable-exploited' or vuln.attributes['status'] == 'vulnerable-version'
vid = vuln.attributes['id'].to_s
# TODO, improve the vuln_lookup check so case of the vuln_id doesnt matter
nexpose_vuln_lookup(doc,vid,refs,dhost,service)
nexpose_vuln_lookup(doc,vid.upcase,refs,dhost,service)
end
end
end
end
end
#
# Import Nmap's -oX xml output
#
def import_nmap_xml_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import_nmap_xml(data)
end
def import_nmap_xml(data)
# Use a stream parser instead of a tree parser so we can deal with
# huge results files without running out of memory.
parser = Rex::Parser::NmapXMLStreamParser.new
# Whenever the parser pulls a host out of the nmap results, store
# it, along with any associated services, in the database.
parser.on_found_host = Proc.new { |h|
data = {}
if (h["addrs"].has_key?("ipv4"))
data[:host] = h["addrs"]["ipv4"]
elsif (h["addrs"].has_key?("ipv6"))
data[:host] = h["addrs"]["ipv6"]
else
# Can't report it if it doesn't have an IP
return
end
if (h["addrs"].has_key?("mac"))
data[:mac] = h["addrs"]["mac"]
end
data[:state] = (h["status"] == "up" ? Msf::HostState::Alive : Msf::HostState::Dead)
host = framework.db.find_or_create_host(data)
# Put all the ports, regardless of state, into the db.
h["ports"].each { |p|
extra = ""
extra << p["product"] + " " if p["product"]
extra << p["version"] + " " if p["version"]
extra << p["extrainfo"] + " " if p["extrainfo"]
data = {}
data[:proto] = p["protocol"].downcase
data[:port] = p["portid"].to_i
data[:state] = p["state"]
data[:host] = host
data[:info] = extra if not extra.empty?
if p["name"] != "unknown"
data[:name] = p["name"]
end
framework.db.report_service(data)
}
}
REXML::Document.parse_stream(data, parser)
end
#
# Import Nessus NBE files
#
def import_nessus_nbe_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import_nessus_nbe(data)
end
def import_nessus_nbe(data)
data.each_line do |line|
r = line.split('|')
next if r[0] != 'results'
addr = r[2]
port = r[3]
nasl = r[4]
type = r[5]
data = r[6]
# Match the NBE types with the XML severity ratings
case type
# log messages don't actually have any data, they are just
# complaints about not being able to perform this or that test
# because such-and-such was missing
when "Log Message"; next
when "Security Hole"; severity = 3
when "Security Warning"; severity = 2
when "Security Note"; severity = 1
# a severity 0 means there's no extra data, it's just an open port
else; severity = 0
end
handle_nessus(addr, port, nasl, severity, data)
end
end
#
# Of course they had to change the nessus format.
#
def import_openvas_xml(filename)
raise NotImplementedError.new("No openvas XML support. Patches welcome")
end
#
# Import Nessus XML v1 output
#
# Old versions of openvas exported this as well
#
def import_nessus_xml_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import_nessus_xml(data)
end
def import_nessus_xml(data)
if(data.index("NessusClientData_v2"))
raise RuntimeError.new("The v2 .nessus format is not currently supported (patches welcome).")
end
doc = REXML::Document.new(file_contents)
doc.elements.each('/NessusClientData/Report/ReportHost') do |host|
addr = host.elements['HostName'].text
host.elements.each('ReportItem') do |item|
nasl = item.elements['pluginID'].text
port = item.elements['port'].text
data = item.elements['data'].text
severity = item.elements['severity'].text
handle_nessus(addr, port, nasl, severity, data)
end
end
end
def import_nessus_xml_file(filename)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
import_nessus_xml(data)
end
def import_amap_mlog(data)
data.each_line do |line|
next if line =~ /^#/
r = line.split(':')
next if r.length < 6
addr = r[0]
port = r[1].to_i
proto = r[2].downcase
status = r[3]
name = r[5]
next if status != "open"
host = find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
next if not host
info = {
:host => host,
:proto => proto,
:port => port
}
if name != "unidentified"
info[:name] = name
end
service = find_or_create_service(info)
end
end
protected
#
# This holds all of the shared parsing/handling used by the
# Nessus NBE and NESSUS methods
#
def handle_nessus(addr, port, nasl, severity, data)
# The port section looks like:
# http (80/tcp)
p = port.match(/^([^\(]+)\((\d+)\/([^\)]+)\)/)
return if not p
host = find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
return if not host
info = { :host => host, :port => p[2].to_i, :proto => p[3].downcase }
name = p[1].strip
if name != "unknown"
info[:name] = name
end
service = find_or_create_service(info)
return if not nasl
data.gsub!("\\n", "\n")
refs = []
if (data =~ /^CVE : (.*)$/)
$1.gsub(/C(VE|AN)\-/, '').split(',').map { |r| r.strip }.each do |r|
refs.push('CVE-' + r)
end
end
if (data =~ /^BID : (.*)$/)
$1.split(',').map { |r| r.strip }.each do |r|
refs.push('BID-' + r)
end
end
if (data =~ /^Other references : (.*)$/)
$1.split(',').map { |r| r.strip }.each do |r|
ref_id, ref_val = r.split(':')
ref_val ? refs.push(ref_id + '-' + ref_val) : refs.push(ref_id)
end
end
nss = 'NSS-' + nasl.to_s
vuln = find_or_create_vuln(
:host => host,
:service => service,
:name => nss,
:data => data)
rids = []
refs.each do |r|
rids << find_or_create_ref(:name => r)
end
vuln.refs << (rids - vuln.refs)
end
def process_nexpose_data_sxml_refs(vuln)
refs = []
vid = vuln.attributes['id'].to_s.downcase
vry = vuln.attributes['resultCode'].to_s.upcase
# Only process vuln-exploitable and vuln-version statuses
return if vry !~ /^V[VE]$/
refs = []
vuln.elements.each('id') do |ref|
rtyp = ref.attributes['type'].to_s.upcase
rval = ref.text.to_s.strip
case rtyp
when 'CVE'
refs << rval.gsub('CAN', 'CVE')
when 'MS' # obsolete?
refs << "MSB-MS-#{rval}"
else
refs << "#{rtyp}-#{rval}"
end
end
refs << "NEXPOSE-#{vid}"
refs
end
#
# NeXpose vuln lookup
#
def nexpose_vuln_lookup(doc, vid, refs, host, serv=nil)
doc.elements.each("/NexposeReport/VulnerabilityDefinitions/vulnerability[@id = '#{vid}']]") do |vulndef|
title = vulndef.attributes['title']
pciSeverity = vulndef.attributes['pciSeverity']
cvss_score = vulndef.attributes['cvssScore']
cvss_vector = vulndef.attributes['cvssVector']
vulndef.elements['references'].elements.each('reference') do |ref|
if ref.attributes['source'] == 'BID'
refs[ 'BID-' + ref.text ] = true
elsif ref.attributes['source'] == 'CVE'
# ref.text is CVE-$ID
refs[ ref.text ] = true
elsif ref.attributes['source'] == 'MS'
refs[ 'MSB-MS-' + ref.text ] = true
end
end
refs[ 'NEXPOSE-' + vid.downcase ] = true
vuln = find_or_create_vuln(
:host => host,
:service => serv,
:name => 'NEXPOSE-' + vid.downcase,
:data => title)
rids = []
refs.keys.each do |r|
rids << find_or_create_ref(:name => r)
end
vuln.refs << (rids - vuln.refs)
end
end
end
end

View File

@ -55,6 +55,7 @@ class Db
"db_del_host" => "Delete one or more hosts from the database",
"db_del_port" => "Delete one port from the database",
"db_autopwn" => "Automatically exploit everything",
"db_import" => "Import a scan result file (filetype will be auto-detected)",
"db_import_amap_mlog" => "Import a THC-Amap scan results file (-o -m)",
"db_import_nessus_nbe" => "Import a Nessus scan result file (NBE)",
"db_import_nessus_xml" => "Import a Nessus scan result file (NESSUS)",
@ -824,64 +825,19 @@ class Db
# EOM
end
#
# This holds all of the shared parsing/handling used by the
# Nessus NBE and NESSUS methods
# Generic import that automatically detects the file type
#
def handle_nessus(addr, port, nasl, data)
p = port.match(/^([^\(]+)\((\d+)\/([^\)]+)\)/)
return if not p
host = framework.db.find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
return if not host
info = { :host => host, :proto => p[3].downcase, :port => p[2].to_i }
name = p[1].strip
if name != "unknown"
info[:name] = name
def cmd_db_import(*args)
if (not (args and args.length == 1))
print_error("Usage: db_import <filename>")
return
end
service = framework.db.find_or_create_service(info)
return if not nasl
data.gsub!("\\n", "\n")
refs = {}
if (data =~ /^CVE : (.*)$/)
$1.gsub(/C(VE|AN)\-/, '').split(',').map { |r| r.strip }.each do |r|
refs[ 'CVE-' + r ] = true
end
if (not File.readable?(args[0]))
print_error("Could not read the file")
return
end
if (data =~ /^BID : (.*)$/)
$1.split(',').map { |r| r.strip }.each do |r|
refs[ 'BID-' + r ] = true
end
end
if (data =~ /^Other references : (.*)$/)
$1.split(',').map { |r| r.strip }.each do |r|
ref_id, ref_val = r.split(':')
ref_val ? refs[ ref_id + '-' + ref_val ] = true : refs[ ref_id ] = true
end
end
nss = 'NSS-' + nasl.to_s
vuln = framework.db.find_or_create_vuln(
:host => host,
:service => service,
:name => nss,
:data => data)
rids = []
refs.keys.each do |r|
rids << framework.db.find_or_create_ref(:name => r)
end
vuln.refs << (rids - vuln.refs)
framework.db.import_file(args[0])
end
#
@ -889,7 +845,7 @@ class Db
#
def cmd_db_import_nessus_nbe(*args)
if (not (args and args.length == 1))
print_status("Usage: db_import_nessus_nbe [nessus.nbe]")
print_status("Usage: db_import_nessus_xml <nessus.nbe>")
return
end
@ -897,19 +853,7 @@ class Db
print_status("Could not read the NBE file")
return
end
fd = File.open(args[0], 'r')
fd.each_line do |line|
r = line.split('|')
next if r[0] != 'results'
addr = r[2]
port = r[3]
nasl = r[4]
data = r[6]
handle_nessus(addr, port, nasl, data)
end
fd.close
framework.db.import_nessus_nbe_file(args[0])
end
#
@ -917,7 +861,7 @@ class Db
#
def cmd_db_import_nessus_xml(*args)
if (not (args and args.length == 1))
print_status("Usage: db_import_nessus_xml [nessus.nessus]")
print_status("Usage: db_import_nessus_xml <nessus.nessus>")
return
end
@ -925,28 +869,7 @@ class Db
print_status("Could not read the NESSUS file")
return
end
fd = File.open(args[0], 'r')
data = fd.read(fd.stat.size)
fd.close
if(data.index("NessusClientData_v2"))
print_status("The v2 .nessus format is not currently supported (patches welcome).")
return
end
doc = REXML::Document.new(data)
doc.elements.each('/NessusClientData/Report/ReportHost') do |host|
addr = host.elements['HostName'].text
host.elements.each('ReportItem') do |item|
nasl = item.elements['pluginID'].text
port = item.elements['port'].text
data = item.elements['data'].text
handle_nessus(addr, port, nasl, data)
end
end
framework.db.import_nessus_xml_file(args[0])
end
#
@ -954,11 +877,15 @@ class Db
#
def cmd_db_import_nmap_xml(*args)
if (not (args and args.length == 1))
print_status("Usage: db_import_nmap_xml [nmap.xml]")
print_error("Usage: db_import_nmap_xml <nmap.xml>")
return
end
load_nmap_xml(args[0])
if (not File.readable?(args[0]))
print_status("Could not read the NESSUS file")
return
end
framework.db.import_nmap_xml_file(args[0])
end
#
@ -974,7 +901,7 @@ class Db
Rex::FileUtils.find_full_path("nmap") ||
Rex::FileUtils.find_full_path("nmap.exe")
if(not nmap)
if (not nmap)
print_error("The nmap executable could not be found")
return
end
@ -1001,62 +928,7 @@ class Db
# end
::File.unlink(fo.path)
load_nmap_xml(fd.path)
end
#
# Process Nmap XML data
#
def load_nmap_xml(filename)
if (not File.readable?(filename) or File.size(filename) < 1)
print_status("Could not read the XML file")
return
end
# Use a stream parser instead of a tree parser so we can deal with
# huge results files without running out of memory.
parser = Rex::Parser::NmapXMLStreamParser.new
# Whenever the parser pulls a host out of the nmap results, store
# it, along with any associated services, in the database.
parser.on_found_host = Proc.new { |h|
data = {}
if (h["addrs"].has_key?("ipv4"))
data[:host] = h["addrs"]["ipv4"]
elsif (h["addrs"].has_key?("ipv6"))
data[:host] = h["addrs"]["ipv6"]
else
# Can't report it if it doesn't have an IP
return
end
if (h["addrs"].has_key?("mac"))
data[:mac] = h["addrs"]["mac"]
end
data[:state] = (h["status"] == "up" ? Msf::HostState::Alive : Msf::HostState::Dead)
host = framework.db.find_or_create_host(data)
# Put all the ports, regardless of state, into the db.
h["ports"].each { |p|
extra = ""
extra << p["product"] + " " if p["product"]
extra << p["version"] + " " if p["version"]
extra << p["extrainfo"] + " " if p["extrainfo"]
data = {}
data[:proto] = p["protocol"].downcase
data[:port] = p["portid"].to_i
data[:state] = p["state"]
data[:host] = host
data[:info] = extra if not extra.empty?
if p["name"] != "unknown"
data[:name] = p["name"]
end
framework.db.report_service(data)
}
}
REXML::Document.parse_stream(File.new(filename), parser)
framework.db.import_nmap_xml_file(fd.path)
end
#
@ -1073,37 +945,7 @@ class Db
return
end
fd = File.open(args[0], 'r')
fd.each_line do |line|
line.sub!(/#.*/, "")
r = line.split(':')
next if r.length < 6
addr = r[0]
port = r[1].to_i
proto = r[2].downcase
status = r[3]
name = r[5]
next if status != "open"
host = framework.db.find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
next if not host
info = {
:host => host,
:proto => proto,
:port => port
}
if name != "unidentified"
info[:name] = name
end
service = framework.db.find_or_create_service(info)
end
fd.close
framework.db.import_amap_mlog_file(args[0])
end
#

View File

@ -22,8 +22,6 @@ class Plugin::Nexpose < Msf::Plugin
'nexpose_exhaustive' => "Launch a scan covering all TCP ports and all authorized safe checks",
'nexpose_dos' => "Launch a scan that includes checks that can crash services and devices (caution)",
'nexpose_import_raw' => "Import a Raw XML file from NeXpose (premium editions)",
'nexpose_import_simple' => "Import a Simple XML file from any version of NeXpose",
'nexpose_disconnect' => "Disconnect from an active NeXpose instance",
# TODO:
@ -368,93 +366,17 @@ class Plugin::Nexpose < Msf::Plugin
@nsc = nil
end
def cmd_nexpose_import_raw(*args)
if ! (args[0] and args[0] != "-h")
print_status("Usage: nexpose_import_raw <filename>")
return
end
process_nexpose_data_rxml(::File.read(args[0], ::File.size(args[0])))
end
def cmd_nexpose_import_simple(*args)
if ! (args[0] and args[0] != "-h")
print_status("Usage: nexpose_import_simple <filename>")
return
end
process_nexpose_data_sxml(::File.read(args[0], ::File.size(args[0])))
end
def process_nexpose_data(fmt, data)
case fmt
when 'raw-xml'
process_nexpose_data_rxml(data)
framework.db.import_nexpose_rawxml(data)
when 'ns-xml'
process_nexpose_data_sxml(data)
framework.db.import_nexpose_simplexml(data)
else
print_error("Unsupported NeXpose data format: #{fmt}")
end
end
def process_nexpose_data_rxml(data)
doc = REXML::Document.new(data)
doc.elements.each('/NexposeReport/nodes/node') do |host|
addr = host.attributes['address']
xhost = addr
refs = {}
# os based vuln
host.elements['tests'].elements.each('test') do |vuln|
if vuln.attributes['status'] == 'vulnerable-exploited' or vuln.attributes['status'] == 'vulnerable-version'
dhost = framework.db.find_or_create_host(:host => addr)
next if not dhost
vid = vuln.attributes['id'].to_s
nexpose_vuln_lookup(doc,vid,refs,dhost)
nexpose_vuln_lookup(doc,vid.upcase,refs,dhost)
end
end
# skip if no endpoints
next unless host.elements['endpoints']
# parse the ports and add the vulns
host.elements['endpoints'].elements.each('endpoint') do |port|
prot = port.attributes['protocol']
pnum = port.attributes['port']
stat = port.attributes['status']
next if not port.elements['services']
name = port.elements['services'].elements['service'].attributes['name'].downcase
next if not port.elements['services'].elements['service'].elements['fingerprints']
prod = port.elements['services'].elements['service'].elements['fingerprints'].elements['fingerprint'].attributes['product']
vers = port.elements['services'].elements['service'].elements['fingerprints'].elements['fingerprint'].attributes['version']
vndr = port.elements['services'].elements['service'].elements['fingerprints'].elements['fingerprint'].attributes['vendor']
next if stat != 'open'
dhost = framework.db.find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
next if not dhost
if name != "unknown"
service = framework.db.find_or_create_service(:host => dhost, :proto => prot.downcase, :port => pnum.to_i, :name => name)
else
service = framework.db.find_or_create_service(:host => dhost, :proto => prot.downcase, :port => pnum.to_i)
end
port.elements['services'].elements['service'].elements['tests'].elements.each('test') do |vuln|
if vuln.attributes['status'] == 'vulnerable-exploited' or vuln.attributes['status'] == 'vulnerable-version'
vid = vuln.attributes['id'].to_s
# TODO, improve the vuln_lookup check so case of the vuln_id doesnt matter
nexpose_vuln_lookup(doc,vid,refs,dhost,service)
nexpose_vuln_lookup(doc,vid.upcase,refs,dhost,service)
end
end
end
end
end
#
# NeXpose vuln lookup
#
@ -494,84 +416,6 @@ class Plugin::Nexpose < Msf::Plugin
end
end
def process_nexpose_data_sxml(data)
doc = REXML::Document.new(data)
doc.elements.each('/NeXposeSimpleXML/devices/device') do |dev|
addr = dev.attributes['address'].to_s
desc = ''
dev.elements.each('fingerprint/description') do |fdesc|
desc = fdesc.text.to_s.strip
end
host = framework.db.find_or_create_host(:host => addr, :state => Msf::HostState::Alive)
next if not host
# Load vulnerabilities not associated with a service
dev.elements.each('vulnerabilities/vulnerability') do |vuln|
vid = vuln.attributes['id'].to_s.downcase
rids = []
refs = process_nexpose_data_sxml_refs(vuln)
next if not refs
vuln = framework.db.find_or_create_vuln(
:host => host,
:name => 'NEXPOSE-' + vid,
:data => vid)
refs.each { |r| rids << framework.db.find_or_create_ref(:name => r) }
vuln.refs << (rids - vuln.refs)
end
# Load the services
dev.elements.each('services/service') do |svc|
sname = svc.attributes['name'].to_s
sprot = svc.attributes['protocol'].to_s.downcase
sport = svc.attributes['port'].to_s.to_i
name = sname.split('(')[0].strip
if(sname.downcase != '<unknown>')
serv = framework.db.find_or_create_service(:host => host, :proto => sprot, :port => sport, :name => name)
else
serv = framework.db.find_or_create_service(:host => host, :proto => sprot, :port => sport)
end
# Load vulnerabilities associated with this service
svc.elements.each('vulnerabilities/vulnerability') do |vuln|
vid = vuln.attributes['id'].to_s.downcase
rids = []
refs = process_nexpose_data_sxml_refs(vuln)
next if not refs
vuln = framework.db.find_or_create_vuln(:host => host, :service => serv, :name => 'NEXPOSE-' + vid, :data => vid)
refs.each { |r| rids << framework.db.find_or_create_ref(:name => r) }
vuln.refs << (rids - vuln.refs)
end
end
end
end
def process_nexpose_data_sxml_refs(vuln)
refs = []
vid = vuln.attributes['id'].to_s.downcase
vry = vuln.attributes['resultCode'].to_s.upcase
# Only process vuln-exploitable and vuln-version statuses
return if vry !~ /^V[VE]$/
refs = []
vuln.elements.each('id') do |ref|
rtyp = ref.attributes['type'].to_s.upcase
rval = ref.text.to_s.strip
case rtyp
when 'CVE'
refs << rval.gsub('CAN', 'CVE')
when 'MS' # obsolete?
refs << "MSB-MS-#{rval}"
else
refs << "#{rtyp}-#{rval}"
end
end
refs << "NEXPOSE-#{vid}"
refs
end
end
#