2011-05-25 18:48:23 +00:00
|
|
|
module Rex
|
|
|
|
module Parser
|
|
|
|
|
|
|
|
# Determines if Nokogiri is available and if it's a minimum
|
|
|
|
# acceptable version.
|
|
|
|
def self.load_nokogiri
|
|
|
|
@nokogiri_loaded = false
|
|
|
|
begin
|
|
|
|
require 'nokogiri'
|
|
|
|
major,minor = Nokogiri::VERSION.split(".")[0,2]
|
|
|
|
if major.to_i >= 1
|
|
|
|
if minor.to_i >= 4
|
|
|
|
@nokogiri_loaded = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue LoadError => e
|
|
|
|
@nokogiri_loaded = false
|
|
|
|
@nokogiri_error = e
|
|
|
|
end
|
|
|
|
@nokogiri_loaded
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.nokogiri_loaded
|
|
|
|
!!@nokogiri_loaded
|
|
|
|
end
|
|
|
|
|
2011-05-27 17:30:11 +00:00
|
|
|
# Useful during development, shouldn't be used in normal operation.
|
|
|
|
def self.reload(fname)
|
|
|
|
$stdout.puts "Reloading #{fname}..."
|
|
|
|
load __FILE__
|
|
|
|
load File.join(File.expand_path(File.dirname(__FILE__)),fname)
|
|
|
|
end
|
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
end
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
module Rex
|
|
|
|
module Parser
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
load_nokogiri && module NokogiriDocMixin
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
# Set up the getters and instance variables for the document
|
|
|
|
eval("attr_reader :args, :db, :state, :block, :report_data")
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
def initialize(args,db,&block)
|
|
|
|
@args = args
|
|
|
|
@db = db
|
|
|
|
@state = {}
|
|
|
|
@state[:current_tag] = {}
|
|
|
|
@block = block if block
|
|
|
|
@report_data = {:wspace => args[:wspace]}
|
|
|
|
super()
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
# Turn XML attribute pairs in to more workable hashes (there
|
|
|
|
# are better Enumerable tricks in Ruby 1.9, but ignoring for now)
|
|
|
|
def attr_hash(attrs)
|
|
|
|
h = {}
|
|
|
|
attrs.each {|k,v| h[k] = v}
|
|
|
|
h
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
def valid_ip(addr)
|
|
|
|
valid = false
|
|
|
|
valid = ::Rex::Socket::RangeWalker.new(addr).valid? rescue false
|
|
|
|
!!valid
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-27 17:30:11 +00:00
|
|
|
def normalize_ref(ref_type, ref_value)
|
|
|
|
return if ref_type.nil? || ref_type.empty? || ref_value.nil? || ref_value.empty?
|
|
|
|
ref_value = ref_value.strip
|
|
|
|
ref_type = ref_type.strip.upcase
|
|
|
|
ret = case ref_type
|
|
|
|
when "CVE"
|
|
|
|
ref_value.gsub("CAN", "CVE")
|
|
|
|
when "MS"
|
|
|
|
"MSB-MS-#{ref_value}"
|
|
|
|
when "URL", "BID"
|
|
|
|
"#{ref_type}-#{ref_value}"
|
|
|
|
else # Handle others?
|
|
|
|
"#{ref_type}-#{ref_value}"
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
|
|
|
def normalize_references(orig_refs)
|
|
|
|
return [] unless orig_refs
|
|
|
|
refs = []
|
|
|
|
orig_refs.each do |ref_hash|
|
|
|
|
ref_hash_sym = Hash[ref_hash.map {|k, v| [k.to_sym, v] }]
|
|
|
|
ref_type = ref_hash_sym[:source].to_s.strip.upcase
|
|
|
|
ref_value = ref_hash_sym[:value].to_s.strip
|
|
|
|
refs << normalize_ref(ref_type, ref_value)
|
|
|
|
end
|
|
|
|
return refs.compact.uniq
|
|
|
|
end
|
|
|
|
|
|
|
|
def in_tag(tagname)
|
|
|
|
@state[:current_tag].keys.include? tagname
|
|
|
|
end
|
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
# If there's an address, it's not on the blacklist,
|
|
|
|
# it has ports, and the port list isn't
|
|
|
|
# empty... it's okay.
|
|
|
|
def host_is_okay
|
|
|
|
return false unless @report_data[:host]
|
|
|
|
return false unless valid_ip(@report_data[:host])
|
|
|
|
return false unless @report_data[:state] == Msf::HostState::Alive
|
|
|
|
if @args[:blacklist]
|
|
|
|
return false if @args[:blacklist].include?(@report_data[:host])
|
2011-05-25 18:48:23 +00:00
|
|
|
end
|
2011-05-26 20:29:47 +00:00
|
|
|
return false unless @report_data[:ports]
|
|
|
|
return false if @report_data[:ports].empty?
|
|
|
|
return true
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-31 15:24:42 +00:00
|
|
|
# XXX: Document classes ought to define this
|
2011-05-26 20:29:47 +00:00
|
|
|
def determine_port_state(v)
|
|
|
|
return v
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
|
2011-05-26 20:29:47 +00:00
|
|
|
# Nokogiri 1.4.4 (and presumably beyond) generates attrs as pairs,
|
|
|
|
# like [["value1","foo"],["value2","bar"]] (but not hashes for some
|
|
|
|
# reason). 1.4.3.1 (and presumably 1.4.3.x and prior) generates attrs
|
|
|
|
# as a flat array of strings. We want array_pairs.
|
|
|
|
def normalize_attrs(attrs)
|
|
|
|
attr_pairs = []
|
|
|
|
case attrs.first
|
|
|
|
when Array, NilClass
|
|
|
|
attr_pairs = attrs
|
|
|
|
when String
|
|
|
|
attrs.each_index {|i|
|
|
|
|
next if i % 2 == 0
|
|
|
|
attr_pairs << [attrs[i-1],attrs[i]]
|
|
|
|
}
|
|
|
|
else # Wow, yet another format! It's either from the distant past or distant future.
|
|
|
|
raise ::Msf::DBImportError.new("Unknown format for XML attributes. Please check your Nokogiri version.")
|
|
|
|
end
|
|
|
|
return attr_pairs
|
2011-05-25 18:48:23 +00:00
|
|
|
end
|
2011-05-26 20:29:47 +00:00
|
|
|
|
|
|
|
def end_document
|
|
|
|
block = @block
|
2011-05-31 15:24:42 +00:00
|
|
|
return unless @report_type_ok
|
2011-05-26 20:29:47 +00:00
|
|
|
unless @state[:current_tag].empty?
|
|
|
|
missing_ends = @state[:current_tag].keys.map {|x| "'#{x}'"}.join(", ")
|
|
|
|
msg = "Warning, the provided file is incomplete, and there may be missing\n"
|
|
|
|
msg << "data. The following tags were not closed: #{missing_ends}."
|
2011-05-31 15:24:42 +00:00
|
|
|
db.emit(:warning,msg,&block) if block
|
2011-05-26 20:29:47 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-05-25 18:48:23 +00:00
|
|
|
end
|
2011-05-26 20:29:47 +00:00
|
|
|
|
|
|
|
end
|
2011-05-25 18:48:23 +00:00
|
|
|
end
|