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-b9f4589650da
unstable
HD Moore 2010-03-28 23:02:28 +00:00
parent deeb00e4ae
commit 09dd8c1fcc
3 changed files with 177 additions and 17 deletions

View File

@ -1100,6 +1100,8 @@ class DBManager
return import_nessus_xml(data, wspace)
when "NessusClientData_v2"
return import_nessus_xml_v2(data, wspace)
when "SCAN"
return import_qualys_xml(data, wspace)
else
# Give up if we haven't hit the root tag in the first few lines
break if line_count > 10
@ -1493,6 +1495,85 @@ class DBManager
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)
f = File.open(filename, 'r')
data = f.read(f.stat.size)
@ -1510,6 +1591,7 @@ class DBManager
data = f.read(f.stat.size)
import_amap_log(data, wspace)
end
def import_amap_mlog(data, wspace)
data.each_line do |line|
next if line =~ /^#/
@ -1643,6 +1725,31 @@ protected
:refs => refs)
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)
refs = []
vid = vuln.attributes['id'].to_s.downcase

View File

@ -61,6 +61,7 @@ class Db
"db_import_nessus_nbe" => "Import a Nessus scan result file (NBE)",
"db_import_nessus_xml" => "Import a Nessus scan result file (NESSUS)",
"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",
}
@ -456,9 +457,9 @@ class Db
when '-X'
targ_exc << OptAddressRange.new('TEMPRANGE', [ true, '' ]).normalize(args.shift)
when '-PI'
port_inc = Rex::Socket.portspec_crack(args.shift)
port_inc = Rex::Socket.portspec_to_portlist(args.shift)
when '-PX'
port_exc = Rex::Socket.portspec_crack(args.shift)
port_exc = Rex::Socket.portspec_to_portlist(args.shift)
when '-m'
regx = args.shift
when '-R'
@ -929,6 +930,22 @@ class Db
framework.db.import_nmap_xml_file(args[0])
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
#

View File

@ -350,11 +350,16 @@ module Socket
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
end
def self.portspec_crack(pspec)
portspec_to_portlist(pspec)
end
#
# Converts a port specification like "80,21-23,443" into a sorted,
# unique array of valid port numbers like [21,22,23,80,443]
#
def self.portspec_crack(pspec)
def self.portspec_to_portlist(pspec)
ports = []
# Build ports array from port specification
@ -370,7 +375,38 @@ module Socket
end
# 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
##
@ -396,18 +432,18 @@ module Socket
#
# 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.
# Note: OpenSSL requires native ruby sockets for its io.
#
def self.tcp_socket_pair
lsock = nil
rsock = nil
laddr = '127.0.0.1'
laddr = '127.0.0.1'
lport = 0
threads = []
mutex = ::Mutex.new
threads << ::Thread.new {
server = nil
mutex.synchronize {
@ -424,31 +460,31 @@ module Socket
lsock, saddr = server.accept
server.close
}
threads.each { |t| t.join }
return [lsock, rsock]
end
#
# Create a UDP socket pair using native ruby UDP sockets.
#
def self.udp_socket_pair
laddr = '127.0.0.1'
laddr = '127.0.0.1'
lsock = ::UDPSocket.new
lsock.bind( laddr, 0 )
rsock = ::UDPSocket.new
rsock.bind( laddr, 0 )
rsock.connect( *lsock.addr.values_at(3,1) )
lsock.connect( *rsock.addr.values_at(3,1) )
return [lsock, rsock]
end
##
#
# Class initialization