# -*- coding: binary -*- require "rex/parser/nokogiri_doc_mixin" module Rex module Parser # If Nokogiri is available, define OpenVAS document class. load_nokogiri && class OpenVASDocument < Nokogiri::XML::SAX::Document include NokogiriDocMixin # ourselves with the @state variable, turning things on when we # get here (and turning things off when we exit in end_element()). def start_element(name=nil,attrs=[]) attrs = normalize_attrs(attrs) block = @block @state[:current_tag][name] = true case name when "host" @state[:has_text] = true end end # When we exit a tag, this is triggered. def end_element(name=nil) block = @block case name when "name" return if not in_tag("result") @state[:has_text] = true @state[:vuln_name] = @text.strip if @text @text = nil when "description" @state[:has_text] = true @state[:vuln_desc] = @text.strip if @text @text = nil when "bid" return if not in_tag("result") return if not in_tag("nvt") @state[:has_text] = true @state[:bid] = @text.strip if @text @text = nil when "cve" return if not in_tag("result") return if not in_tag("nvt") @state[:has_text] = true @state[:cves] = @text.strip if @text @text = nil when "risk_factor" return if not in_tag("result") return if not in_tag("nvt") #we do this to clean out the buffer so to speak #if we don't set text to nil now, the text will show up later @state[:has_text] = true @text = nil when "cvss_base" return if not in_tag("result") return if not in_tag("nvt") @state[:has_text] = true @text = nil when "subnet" @state[:has_text] = true @text = nil when "result" return if not in_tag("results") record_vuln when "threat" return if not in_tag("ports") return if not in_tag("port") @state[:has_text] = true if not @text.index('(') @state[:name] = nil @state[:port] = nil @state[:proto] = nil @text = nil return end @state[:name] = @text.split(' ')[0] if @text @state[:port] = @text.split('(')[1].split('/')[0] if @text @state[:proto] = @text.split('(')[1].split('/')[1].split(')')[0] if @text @text = nil when "host" if in_tag('result') @state[:has_text] = true @state[:host] = @text.strip if @text @text = nil elsif in_tag('ports') return if not in_tag('port') @state[:has_text] = true @state[:host] = @text.strip if @text @text = nil end when "port" if in_tag('result') @state[:has_text] = true if not @text.index('(') @state[:proto] = nil @state[:port] = nil @text = nil return end @state[:proto] = @text.split('(')[0].strip if @text @state[:port] = @text.split('(')[1].split('/')[0].gsub(/\)/, '') if @text @text = nil elsif in_tag('ports') record_service end when "name" return if not in_tag("result") @state[:has_text] = true @text = nil else @text = nil end @state[:current_tag].delete name end def record_vuln if (@state[:cves] and @state[:cves] == "NOCVE") and (@state[:bid] and @state[:bid] == "NOBID") return end if @state[:cves] and @state[:cves] != "NOCVE" and !@state[:cves].empty? @state[:cves].split(',').each do |cve| vuln_info = {} vuln_info[:host] = @state[:host] vuln_info[:refs] = normalize_references([{ :source => "CVE", :value => cve}]) vuln_info[:name] = @state[:vuln_name] vuln_info[:info] = @state[:vuln_desc] vuln_info[:port] = @state[:port] vuln_info[:proto] = @state[:proto] db_report(:vuln, vuln_info) end end if @state[:bid] and @state[:bid] != "NOBID" and !@state[:bid].empty? @state[:bid].split(',').each do |bid| vuln_info = {} vuln_info[:host] = @state[:host] vuln_info[:refs] = normalize_references([{ :source => "BID", :value => bid}]) vuln_info[:name] = @state[:vuln_name] vuln_info[:info] = @state[:vuln_desc] vuln_info[:port] = @state[:port] vuln_info[:proto] = @state[:proto] db_report(:vuln, vuln_info) end end end def record_service return if not @state[:name] service_info = {} service_info[:host] = @state[:host] service_info[:name] = @state[:name] service_info[:port] = @state[:port] service_info[:proto] = @state[:proto] db_report(:service, service_info) host_info = {} host_info[:host] = @state[:host] db_report(:host, host_info) end end end end