Adds portlist_to_portspec and portspec_to_portlist. Merges in Qualys XML support from Sertan Kolat
git-svn-id: file:///home/svn/framework3/trunk@8949 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
deeb00e4ae
commit
09dd8c1fcc
|
@ -1100,6 +1100,8 @@ class DBManager
|
||||||
return import_nessus_xml(data, wspace)
|
return import_nessus_xml(data, wspace)
|
||||||
when "NessusClientData_v2"
|
when "NessusClientData_v2"
|
||||||
return import_nessus_xml_v2(data, wspace)
|
return import_nessus_xml_v2(data, wspace)
|
||||||
|
when "SCAN"
|
||||||
|
return import_qualys_xml(data, wspace)
|
||||||
else
|
else
|
||||||
# Give up if we haven't hit the root tag in the first few lines
|
# Give up if we haven't hit the root tag in the first few lines
|
||||||
break if line_count > 10
|
break if line_count > 10
|
||||||
|
@ -1493,6 +1495,85 @@ class DBManager
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Import Qualys' xml output
|
||||||
|
#
|
||||||
|
def import_qualys_xml_file(filename, wspace=workspace)
|
||||||
|
f = File.open(filename, 'r')
|
||||||
|
data = f.read(f.stat.size)
|
||||||
|
import_qualys_xml(data, wspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
def import_qualys_xml(data, wspace=workspace)
|
||||||
|
|
||||||
|
doc = REXML::Document.new(data)
|
||||||
|
doc.elements.each('/SCAN/IP') do |host|
|
||||||
|
addr = host.attributes['value']
|
||||||
|
hname = host.attributes['name'] || ''
|
||||||
|
|
||||||
|
report_host(:workspace => wspace, :host => addr, :name => hname, :state => Msf::HostState::Alive)
|
||||||
|
|
||||||
|
if host.elements["OS"]
|
||||||
|
hos = host.elements["OS"].text
|
||||||
|
report_note(
|
||||||
|
:workspace => wspace,
|
||||||
|
:host => addr,
|
||||||
|
:type => 'host.os.qualys_fingerprint',
|
||||||
|
:data => {
|
||||||
|
:os => hos
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Open TCP Services List (Qualys ID 82023)
|
||||||
|
services_tcp = host.elements["SERVICES/CAT/SERVICE[@number='82023']/RESULT"]
|
||||||
|
if services_tcp
|
||||||
|
services_tcp.text.scan(/([0-9]+)\t(.*?)\t.*?\t([^\t\n]*)/) do |match|
|
||||||
|
if match[2] == nil or match[2].strip == 'unknown'
|
||||||
|
name = match[1].strip
|
||||||
|
else
|
||||||
|
name = match[2].strip
|
||||||
|
end
|
||||||
|
handle_qualys(wspace, addr, match[0].to_s, 'tcp', 0, nil, nil, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# Open UDP Services List (Qualys ID 82004)
|
||||||
|
services_udp = host.elements["SERVICES/CAT/SERVICE[@number='82004']/RESULT"]
|
||||||
|
if services_udp
|
||||||
|
services_udp.text.scan(/([0-9]+)\t(.*?)\t.*?\t([^\t\n]*)/) do |match|
|
||||||
|
if match[2] == nil or match[2].strip == 'unknown'
|
||||||
|
name = match[1].strip
|
||||||
|
else
|
||||||
|
name = match[2].strip
|
||||||
|
end
|
||||||
|
handle_qualys(wspace, addr, match[0].to_s, 'udp', 0, nil, nil, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# VULNS are confirmed, PRACTICES are unconfirmed vulnerabilities
|
||||||
|
host.elements.each('VULNS/CAT | PRACTICES/CAT') do |cat|
|
||||||
|
port = cat.attributes['port']
|
||||||
|
protocol = cat.attributes['protocol']
|
||||||
|
cat.elements.each('VULN | PRACTICE') do |vuln|
|
||||||
|
refs = []
|
||||||
|
qid = vuln.attributes['number']
|
||||||
|
severity = vuln.attributes['severity']
|
||||||
|
vuln.elements.each('VENDOR_REFERENCE_LIST/VENDOR_REFERENCE') do |ref|
|
||||||
|
refs.push(ref.elements['ID'].text.to_s)
|
||||||
|
end
|
||||||
|
vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref|
|
||||||
|
refs.push('CVE-' + /C..-([0-9\-]{9})/.match(ref.elements['ID'].text.to_s)[1])
|
||||||
|
end
|
||||||
|
vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref|
|
||||||
|
refs.push('BID-' + ref.elements['ID'].text.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
handle_qualys(wspace, addr, port, protocol, qid, severity, refs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def import_ip_list_file(filename, wspace=workspace)
|
def import_ip_list_file(filename, wspace=workspace)
|
||||||
f = File.open(filename, 'r')
|
f = File.open(filename, 'r')
|
||||||
data = f.read(f.stat.size)
|
data = f.read(f.stat.size)
|
||||||
|
@ -1510,6 +1591,7 @@ class DBManager
|
||||||
data = f.read(f.stat.size)
|
data = f.read(f.stat.size)
|
||||||
import_amap_log(data, wspace)
|
import_amap_log(data, wspace)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_amap_mlog(data, wspace)
|
def import_amap_mlog(data, wspace)
|
||||||
data.each_line do |line|
|
data.each_line do |line|
|
||||||
next if line =~ /^#/
|
next if line =~ /^#/
|
||||||
|
@ -1643,6 +1725,31 @@ protected
|
||||||
:refs => refs)
|
:refs => refs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Qualys report parsing/handling
|
||||||
|
#
|
||||||
|
def handle_qualys(wspace, addr, port, protocol, qid, severity, refs, name=nil)
|
||||||
|
|
||||||
|
port = port.to_i
|
||||||
|
|
||||||
|
info = { :workspace => wspace, :host => addr, :port => port, :proto => protocol }
|
||||||
|
if name and name != 'unknown'
|
||||||
|
info[:name] = name
|
||||||
|
end
|
||||||
|
|
||||||
|
report_service(info)
|
||||||
|
|
||||||
|
return if qid == 0
|
||||||
|
|
||||||
|
report_vuln(
|
||||||
|
:workspace => wspace,
|
||||||
|
:host => addr,
|
||||||
|
:port => port,
|
||||||
|
:proto => protocol,
|
||||||
|
:name => 'QUALYS-' + qid,
|
||||||
|
:refs => refs)
|
||||||
|
end
|
||||||
|
|
||||||
def process_nexpose_data_sxml_refs(vuln)
|
def process_nexpose_data_sxml_refs(vuln)
|
||||||
refs = []
|
refs = []
|
||||||
vid = vuln.attributes['id'].to_s.downcase
|
vid = vuln.attributes['id'].to_s.downcase
|
||||||
|
|
|
@ -61,6 +61,7 @@ class Db
|
||||||
"db_import_nessus_nbe" => "Import a Nessus scan result file (NBE)",
|
"db_import_nessus_nbe" => "Import a Nessus scan result file (NBE)",
|
||||||
"db_import_nessus_xml" => "Import a Nessus scan result file (NESSUS)",
|
"db_import_nessus_xml" => "Import a Nessus scan result file (NESSUS)",
|
||||||
"db_import_nmap_xml" => "Import a Nmap scan results file (-oX)",
|
"db_import_nmap_xml" => "Import a Nmap scan results file (-oX)",
|
||||||
|
"db_import_qualys_xml" => "Import a Qualys scan results file (XML)",
|
||||||
"db_nmap" => "Executes nmap and records the output automatically",
|
"db_nmap" => "Executes nmap and records the output automatically",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,9 +457,9 @@ class Db
|
||||||
when '-X'
|
when '-X'
|
||||||
targ_exc << OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(args.shift)
|
targ_exc << OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(args.shift)
|
||||||
when '-PI'
|
when '-PI'
|
||||||
port_inc = Rex::Socket.portspec_crack(args.shift)
|
port_inc = Rex::Socket.portspec_to_portlist(args.shift)
|
||||||
when '-PX'
|
when '-PX'
|
||||||
port_exc = Rex::Socket.portspec_crack(args.shift)
|
port_exc = Rex::Socket.portspec_to_portlist(args.shift)
|
||||||
when '-m'
|
when '-m'
|
||||||
regx = args.shift
|
regx = args.shift
|
||||||
when '-R'
|
when '-R'
|
||||||
|
@ -929,6 +930,22 @@ class Db
|
||||||
framework.db.import_nmap_xml_file(args[0])
|
framework.db.import_nmap_xml_file(args[0])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Import Qualys XML files
|
||||||
|
#
|
||||||
|
def cmd_db_import_qualys_xml(*args)
|
||||||
|
if not (args and args.length == 1)
|
||||||
|
print_status("Usage: db_import_qualys_xml <result.xml>")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not File.readable?(args[0])
|
||||||
|
print_status("Could not read the Qualys file")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
framework.db.import_qualys_xml_file(args[0])
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Import IP List from a file
|
# Import IP List from a file
|
||||||
#
|
#
|
||||||
|
|
|
@ -350,11 +350,16 @@ module Socket
|
||||||
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
|
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.portspec_crack(pspec)
|
||||||
|
portspec_to_portlist(pspec)
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Converts a port specification like "80,21-23,443" into a sorted,
|
# Converts a port specification like "80,21-23,443" into a sorted,
|
||||||
# unique array of valid port numbers like [21,22,23,80,443]
|
# unique array of valid port numbers like [21,22,23,80,443]
|
||||||
#
|
#
|
||||||
def self.portspec_crack(pspec)
|
def self.portspec_to_portlist(pspec)
|
||||||
ports = []
|
ports = []
|
||||||
|
|
||||||
# Build ports array from port specification
|
# Build ports array from port specification
|
||||||
|
@ -370,7 +375,38 @@ module Socket
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sort, and remove dups and invalid ports
|
# Sort, and remove dups and invalid ports
|
||||||
ports.sort.uniq.delete_if { |p| p < 0 or p > 65535 }
|
ports.sort.uniq.delete_if { |p| p < 1 or p > 65535 }
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Converts a port list like [1,2,3,4,5,100] into a
|
||||||
|
# range specification like "1-5,100"
|
||||||
|
#
|
||||||
|
def self.portlist_to_portspec(parr)
|
||||||
|
ranges = []
|
||||||
|
range = []
|
||||||
|
lastp = nil
|
||||||
|
|
||||||
|
parr.uniq.sort{|a,b| a<=>b}.map{|a| a.to_i}.each do |n|
|
||||||
|
next if (n < 1 or n > 65535)
|
||||||
|
if not lastp
|
||||||
|
range = [n]
|
||||||
|
lastp = n
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if lastp == n - 1
|
||||||
|
range << n
|
||||||
|
else
|
||||||
|
ranges << range
|
||||||
|
range = [n]
|
||||||
|
end
|
||||||
|
lastp = n
|
||||||
|
end
|
||||||
|
|
||||||
|
ranges << range
|
||||||
|
ranges.delete(nil)
|
||||||
|
ranges.uniq.map{|x| x.length == 1 ? "#{x[0]}" : "#{x[0]}-#{x[-1]}"}.join(",")
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -396,18 +432,18 @@ module Socket
|
||||||
#
|
#
|
||||||
# Create a TCP socket pair.
|
# Create a TCP socket pair.
|
||||||
#
|
#
|
||||||
# sf: This create a socket pair using native ruby sockets and will work
|
# sf: This create a socket pair using native ruby sockets and will work
|
||||||
# on Windows where ::Socket.pair is not implemented.
|
# on Windows where ::Socket.pair is not implemented.
|
||||||
# Note: OpenSSL requires native ruby sockets for its io.
|
# Note: OpenSSL requires native ruby sockets for its io.
|
||||||
#
|
#
|
||||||
def self.tcp_socket_pair
|
def self.tcp_socket_pair
|
||||||
lsock = nil
|
lsock = nil
|
||||||
rsock = nil
|
rsock = nil
|
||||||
laddr = '127.0.0.1'
|
laddr = '127.0.0.1'
|
||||||
lport = 0
|
lport = 0
|
||||||
threads = []
|
threads = []
|
||||||
mutex = ::Mutex.new
|
mutex = ::Mutex.new
|
||||||
|
|
||||||
threads << ::Thread.new {
|
threads << ::Thread.new {
|
||||||
server = nil
|
server = nil
|
||||||
mutex.synchronize {
|
mutex.synchronize {
|
||||||
|
@ -424,31 +460,31 @@ module Socket
|
||||||
lsock, saddr = server.accept
|
lsock, saddr = server.accept
|
||||||
server.close
|
server.close
|
||||||
}
|
}
|
||||||
|
|
||||||
threads.each { |t| t.join }
|
threads.each { |t| t.join }
|
||||||
|
|
||||||
return [lsock, rsock]
|
return [lsock, rsock]
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Create a UDP socket pair using native ruby UDP sockets.
|
# Create a UDP socket pair using native ruby UDP sockets.
|
||||||
#
|
#
|
||||||
def self.udp_socket_pair
|
def self.udp_socket_pair
|
||||||
laddr = '127.0.0.1'
|
laddr = '127.0.0.1'
|
||||||
|
|
||||||
lsock = ::UDPSocket.new
|
lsock = ::UDPSocket.new
|
||||||
lsock.bind( laddr, 0 )
|
lsock.bind( laddr, 0 )
|
||||||
|
|
||||||
rsock = ::UDPSocket.new
|
rsock = ::UDPSocket.new
|
||||||
rsock.bind( laddr, 0 )
|
rsock.bind( laddr, 0 )
|
||||||
|
|
||||||
rsock.connect( *lsock.addr.values_at(3,1) )
|
rsock.connect( *lsock.addr.values_at(3,1) )
|
||||||
|
|
||||||
lsock.connect( *rsock.addr.values_at(3,1) )
|
lsock.connect( *rsock.addr.values_at(3,1) )
|
||||||
|
|
||||||
return [lsock, rsock]
|
return [lsock, rsock]
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Class initialization
|
# Class initialization
|
||||||
|
|
Loading…
Reference in New Issue